Ihor Radchenko <[email protected]> writes:

> Slawomir Grochowski <[email protected]> writes:
>> -     (let* ((pos (match-beginning 0))
>> ...
>> -            (summary
>> -             (org-columns--put-summary pos spec summary)
>> ...
>> +              (value (cond
>> +                      (summary (org-columns--put-summary (point) spec 
>> summary)
>
> Is there any specific reason why you removed using saved POS for 
> `org-columne--put-summary'?

I was simply working on a maximally simplified version of the code while
developing this. Saving the position to a separate variable seemed
unnecessary to me at the time, so when I later ported my changes to the
main branch, I just left it out.

Since you pointed it out, I agree it makes sense to restore it just to
be safe against unexpected point movements.

I've attached the updated patch.

Best,
-- 
Slawomir Grochowski
From 2a94f5f75c04f936a11cda8b8e5da90b843f3d30 Mon Sep 17 00:00:00 2001
From: Slawomir Grochowski <[email protected]>
Date: Thu, 20 Aug 2026 15:10:54 +0200
Subject: [PATCH] ; org-colview: Simplify column summary computation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/org-colview.el (org-columns--extend-values-by-level)
(org-columns--values-below-level)
(org-columns--clear-values-below-level): Remove.
(org-columns--compute-spec): Use a stack of level-value pairs instead of
a dynamically resized vector and level-clearing loops.

Using a stack significantly simplifies the code and improves readability
by eliminating three auxiliary helper functions, manual vector resizing,
and nested level-clearing loops.

It also eliminates the overhead of iterating over deeper levels when
ascending the outline tree.  The speedup scales with the depth of the
hierarchy (up to 5×–10× on deep trees):

Benchmark (10 runs each, median times for summary computation)

  5 summary columns (depth 5):
     Headings  Depth   Old (vector)   New (stack)   Speedup
     ──────────────────────────────────────────────────────
           10      5      0.00490 s     0.00214 s     2.3×
          100      5      0.04935 s     0.01607 s     3.1×
          500      5      0.25625 s     0.13464 s     1.9×
         1000      5      0.56025 s     0.26654 s     2.1×
         2000      5      1.14261 s     0.74478 s     1.5×

  10 summary columns (deep hierarchy, depth 10):
     Headings  Depth   Old (vector)   New (stack)   Speedup
     ──────────────────────────────────────────────────────
          100     10      0.26293 s     0.02592 s    10.1×
          500     10      0.77635 s     0.15476 s     5.0×
         1000     10      1.14439 s     0.23697 s     4.8×
         2000     10      2.37339 s     0.47034 s     5.0×

  Scaling across hierarchy depth (1000 headings, 5 summary columns):
     Headings  Depth   Old (vector)   New (stack)   Speedup
     ──────────────────────────────────────────────────────
         1000      4      0.55512 s     0.30566 s     1.8×
         1000      8      1.06393 s     0.22224 s     4.8×
         1000     16      0.57331 s     0.09688 s     5.9×
         1000     30      0.63103 s     0.09008 s     7.0×

Refactoring: Inline Function, Consolidate Conditional Expression.

No behavior change.
---
 lisp/org-colview.el | 78 ++++++++++++++-------------------------------
 1 file changed, 24 insertions(+), 54 deletions(-)

diff --git a/lisp/org-colview.el b/lisp/org-colview.el
index d5548802f..9556c2957 100644
--- a/lisp/org-colview.el
+++ b/lisp/org-colview.el
@@ -1549,35 +1549,17 @@ they have their own way to be computed."
     (and (not (member property org-special-properties))
 	 (org-columns--spec-operator spec))))
 
-(defun org-columns--extend-values-by-level (values-by-level level)
-  "Return VALUES-BY-LEVEL large enough to include LEVEL."
-  (if (< level (length values-by-level)) values-by-level
-    (vconcat values-by-level
-             (make-vector (- (1+ level) (length values-by-level)) nil))))
-
-(defun org-columns--values-below-level (values-by-level level)
-  "Return values in VALUES-BY-LEVEL accumulated deeper than LEVEL."
-  (cl-loop for deeper-level from (1+ level) below (length values-by-level)
-	   append (aref values-by-level deeper-level)))
-
-(defun org-columns--clear-values-below-level (values-by-level level)
-  "Clear accumulated values below LEVEL in VALUES-BY-LEVEL."
-  (cl-loop for deeper-level from (1+ level) below (length values-by-level)
-	   do (aset values-by-level deeper-level nil)))
-
 (defun org-columns--compute-spec (spec &optional update-property-p)
   "Update tree according to SPEC.
 SPEC is a column format specification.  When optional argument
 UPDATE-PROPERTY-P is non-nil, summarized values can replace
 existing ones in properties drawers."
   (when-let* ((operator (org-columns--summarizable-operator spec)))
-    (let* ((values-by-level (make-vector 1 nil))
-	   (current-level 0)
-	   (previous-level 0)
-	   (property (org-columns--spec-property spec))
-	   (format-string (org-columns--spec-format-string spec))
-	   (collect-function (org-columns--collect-function operator))
-	   (summarize-function (org-columns--summarize-function operator)))
+    (let ((property (org-columns--spec-property spec))
+	  (format-string (org-columns--spec-format-string spec))
+	  (collect-function (org-columns--collect-function operator))
+	  (summarize-function (org-columns--summarize-function operator))
+	  (stack nil))
       (org-with-wide-buffer
        ;; Find the region to compute.
        (goto-char org-columns-top-level-marker)
@@ -1585,38 +1567,26 @@ existing ones in properties drawers."
        ;; Walk the tree from the back and do the computations.
        (while (re-search-backward
 	       org-outline-regexp-bol org-columns-top-level-marker t)
-	 (unless (= current-level 0) (setq previous-level current-level))
-	 (setq current-level (org-reduced-level (org-outline-level)))
-	 (setq values-by-level
-	       (org-columns--extend-values-by-level
-		values-by-level current-level))
 	 (let* ((pos (match-beginning 0))
-		(current-value (if collect-function
-				   (funcall collect-function property)
-				 (org-entry-get (point) property)))
-		(value-nonempty-p (org-string-nw-p current-value)))
-	   (cond
-	    ((< current-level previous-level)
-	     ;; Collect values from lower levels and inline tasks here
-	     ;; and summarize them using SUMMARIZE-FUNCTION.  Store them in text
-	     ;; property `org-summaries', in alist whose key is SPEC.
-	     (let* ((values (and summarize-function
-				 (org-columns--values-below-level
-				  values-by-level current-level)))
-		    (summary (and values
-				  (funcall summarize-function values format-string))))
-	       (cond
-		(summary
-		 (org-columns--put-summary pos spec summary)
-		 (when update-property-p
-		   (org-columns--update-summary-property property current-value summary))
-		 (push summary (aref values-by-level current-level)))
-		(value-nonempty-p
-		 (push current-value (aref values-by-level current-level))))
-	       (org-columns--clear-values-below-level
-		values-by-level current-level)))
-	    (value-nonempty-p
-	     (push current-value (aref values-by-level current-level))))))))))
+		(level (org-reduced-level (org-outline-level)))
+		(child-values nil))
+	   (while (and stack (< level (caar stack)))
+	     (push (cdr (pop stack)) child-values))
+	   (setq child-values (nreverse child-values))
+	   (let* ((summary (and summarize-function
+				child-values
+				(funcall summarize-function child-values format-string)))
+		  (current-value (and (or update-property-p (not summary))
+				      (if collect-function
+					  (funcall collect-function property)
+					(org-entry-get pos property))))
+		  (value (cond
+			  (summary (org-columns--put-summary pos spec summary)
+			   (when update-property-p (org-columns--update-summary-property property current-value summary))
+			   summary)
+			  (t current-value))))
+	     (when (org-string-nw-p value)
+	       (push (cons level value) stack)))))))))
 
 ;;;###autoload
 (defun org-columns-compute (property)
-- 
2.39.5

Reply via email to