Hi, I would like to propose a small cleanup series for lisp/org-colview.el.
The immediate changes are purely organizational. They add and refine section headings, then move a few helpers and variable declarations closer to the code they belong to. There should be no behavior change. The motivation is to make org-colview.el easier to read and maintain, and to prepare it for larger internal improvements. In particular, I would like to work toward a clearer separation of concerns between the column view data model and its presentation. At the moment, column view data is tightly coupled to overlays. One visible consequence is the dynamic block implementation: in order to export column view data, Org first has to construct the column view UI, create overlays, and then read data back from those overlays. This makes the data flow harder to understand, makes non-interactive uses more dependent on display internals than they should be, and can be very slow for larger buffers. Another related area is agenda column view. The current code shares a lot of paths with regular Org buffers and contains many conditionals for agenda-specific behavior. I would eventually like to separate the agenda display/update code more clearly, because further development in this area is currently quite difficult. This branch does not attempt those larger changes yet. It only organizes the existing code into clearer sections so that future patches can be smaller, easier to review, and better scoped. I kept the commits small so each step can be reviewed independently. Most commits are either pure section heading changes or pure code movement. The full branch is also available at: https://cgit.git.savannah.gnu.org/cgit/emacs/org-mode.git/log/?h=org-colview-organize-code-into-clearer-sections I would appreciate feedback on whether this direction is acceptable before doing any deeper cleanup or refactoring. The resulting structure is: - Commentary - Code - Require other packages - Customizable variables - Column View - State - Keymap and menu - org-columns-content - Value collection - org-columns--displayed-value - org-columns--agenda-effort-fallback - org-columns--collect-values - Column widths - org-columns--set-widths - Overlay rendering - Overlay helpers - org-columns--new-overlay - org-columns--overlay-fmt - org-columns--overlay-text - Line rendering - org-columns--pad-line-for-overlays - org-columns--display-here-face - org-columns--mark-line-read-only - org-columns--make-row - org-columns--make-cell-overlay - org-columns--hide-rest-of-line - org-columns--display-here - Display string formatting - org-columns--truncate-below-width - org-columns-add-ellipses - View environment - org-columns--suspend-conflicting-modes - org-columns--resume-conflicting-modes - org-columns--suspend-line-wrapping - org-columns--resume-line-wrapping - Header line - org-columns--remap-header-line - org-columns--display-here-title - org-columns-hscroll-title - View lifecycle - org-columns-remove-overlays - org-columns-quit - Cell actions - org-columns-show-value - org-columns-todo - org-columns-toggle-or-columns-quit - org-columns--toggle - org-columns-open-link - Cell editing and value selection - org-columns-check-computed - org-columns--execute-and-update - org-columns-edit-value - org-columns-edit-allowed - org-columns--call - org-columns-previous-allowed-value - org-columns-next-allowed-value - org-colview-construct-allowed-dates - Format selection and startup - org-columns-get-format-and-top-level - org-columns-get-format - org-columns-goto-top-level - org-columns - Column definition editing - org-columns--summary-types-completion-function - org-columns-new - org-columns-delete - org-columns-edit-attributes - org-columns-widen - org-columns-narrow - Navigation and reordering - org-columns--move-cursor - org-columns-move-up - org-columns-move-down - org-columns-move-right - org-columns-move-left - org-columns--move-row - org-columns-move-row-down - org-columns-move-row-up - Display update - org-columns-update - org-columns-redo - Format storage - org-columns-store-format - Format compilation - org-columns-uncompile-format - org-columns-compile-format - Column View Summary - Summary helpers - org-columns--age-to-minutes - org-columns--format-age - org-columns--summary-apply-times - org-columns--summarize - org-columns--collect - Tree summary computation - org-columns--compute-spec - org-columns-compute - org-columns-compute-all - Summary operators - org-columns--summary-sum - org-columns--summary-currencies - org-columns--summary-checkbox - org-columns--summary-checkbox-count - org-columns--summary-checkbox-percent - org-columns--summary-min - org-columns--summary-max - org-columns--summary-mean - org-columns--summary-sum-times - org-columns--summary-min-time - org-columns--summary-max-time - org-columns--summary-mean-time - org-columns--summary-min-age - org-columns--summary-max-age - org-columns--summary-mean-age - org-columns--summary-estimate - Dynamic block for Column view - Capturing - org-columns--capture-view - org-columns--clean-item - Writing - org-dblock-write:columnview - org-columns-dblock-write-default - Insertion and registration - org-columns-insert-dblock - Column view in the agenda - org-agenda-columns - org-agenda-colview-summarize - org-agenda-colview-compute Best, -- Slawomir Grochowski
>From 1a1e95e851fa95ae30fb37fdcdb188b98748ddff Mon Sep 17 00:00:00 2001 Message-Id: <1a1e95e851fa95ae30fb37fdcdb188b98748ddff.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 02:48:46 +0200 Subject: [PATCH 01/15] org-colview: Organize code with section headings * lisp/org-colview.el: Add section headings to group related code. Pure code organization. No behavior change. --- lisp/org-colview.el | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 1975202db..569d93964 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -132,6 +132,8 @@ For more information, see `org-columns-dblock-write-default'." ;;; Column View +;;;; State + (defvar-local org-columns-overlays nil "Holds the list of current column overlays.") (put 'org-columns-overlays 'permanent-local t) @@ -181,6 +183,8 @@ This is the compiled version of the format.") "Map operators to summary functions. See `org-columns-summary-types' for details.") +;;;; Keymap and menu + (defun org-columns-content () "Switch to contents view while in columns view." (interactive nil org-mode) @@ -249,6 +253,8 @@ See `org-columns-summary-types' for details.") "--" ["Quit" org-columns-quit t])) +;;;; Value Collection and Widths + (defun org-columns--displayed-value (spec value &optional no-star) "Return displayed value for specification SPEC in current entry. @@ -356,6 +362,8 @@ where: (setq w (cdr w))))) (apply #'vector widths)))) +;;;; Overlay rendering + (defun org-columns--new-overlay (beg end &optional string face) "Create a new column overlay and add it to the list." (let ((ov (make-overlay beg end))) @@ -418,6 +426,8 @@ This is needed to later remove this relative remapping.") (defvar org-columns--read-only-string nil) +;;;; Row Rendering + (defun org-columns--pad-line-for-overlays () "Pad current line with spaces, one per column if needed. Kludge: column display modifies the buffer here, which it should not. @@ -556,6 +566,8 @@ substring whose `string-width' does not exceed WIDTH." string (- width (string-width org-columns-ellipses))) org-columns-ellipses)))) +;;;; Header Line and View Environment + (defvar org-columns-full-header-line-format nil "The full header line format, will be shifted by horizontal scrolling." ) (defvar org-previous-header-line-format nil @@ -640,6 +652,8 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (defvar org-colview-initial-truncate-line-value nil "Remember the value of `truncate-lines' across colview.") +;;;; View lifecycle + ;;;###autoload (defun org-columns-remove-overlays () "Remove all currently active column overlays." @@ -682,6 +696,8 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (message "Modification not yet reflected in Agenda buffer, use `r' to refresh"))) +;;;; Cell editing and value selection + (defun org-columns-check-computed () "Throw an error if current column value is computed." (let ((spec (nth (org-current-text-column) org-columns-current-fmt-compiled))) @@ -905,6 +921,8 @@ around it." (let ((value (get-char-property (point) 'org-columns-value))) (org-link-open-from-string value arg))) +;;;; Format selection and startup + ;;;###autoload (defun org-columns-get-format-and-top-level () (let ((fmt (org-columns-get-format))) @@ -1016,6 +1034,8 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format." (if doc (format " -- %s" doc) ""))))))))) (complete-with-action flag completion-table string pred))) +;;;; Column definition editing + (defun org-columns-new (&optional spec &rest attributes) "Insert a new column, to the left of the current column. Interactively fill attributes for new column. When column format @@ -1094,6 +1114,8 @@ non-interactively. See `org-columns-compile-format' for details." (interactive "p" org-mode org-agenda-mode) (org-columns-widen (- arg))) +;;;; Navigation and reordering + (defun org-columns--move-cursor (up) "Move cursor up or down one row. When UP is non-nil, move up; otherwise, move down." @@ -1202,6 +1224,8 @@ the current buffer." (insert-before-markers "#+COLUMNS: " fmt "\n")))) (setq-local org-columns-default-format fmt)))))) +;;;; Display update + (defun org-columns-update (property) "Recompute PROPERTY, and update the columns display for it." (org-columns-compute property) @@ -1244,6 +1268,8 @@ the current buffer." (call-interactively #'org-agenda-columns))) (message "Recomputing columns...done"))) +;;;; Format compilation + (defun org-columns-uncompile-format (compiled) "Turn the compiled columns format back into a string representation. @@ -1302,6 +1328,8 @@ Set and return `org-columns-current-fmt-compiled'." ;;;; Column View Summary +;;;;; Summary helpers + (defun org-columns--age-to-minutes (s) "Turn age string S into a number of minutes. An age is either computed from a given timestamp, or indicated @@ -1328,6 +1356,8 @@ Return the result as a duration." (apply fun (mapcar #'org-duration-to-minutes times)) (org-duration-h:mm-only-p times))) +;;;;; Tree summary computation + (defun org-columns--compute-spec (spec &optional update) "Update tree according to SPEC. SPEC is a column format specification. When optional argument @@ -1428,6 +1458,8 @@ column specification." (org-columns--compute-spec spec (not (member property seen))) (push property seen))))) +;;;;; Summary operators + (defun org-columns--summary-sum (values fmt) "Compute the sum of VALUES. When FMT is non-nil, use it to format the result." @@ -1534,6 +1566,8 @@ and variances (respectively) of the individual estimates." ;;; Dynamic block for Column view +;;;; Capturing + (defun org-columns--capture-view (maxlevel match skip-empty exclude-tags format local) "Get the column view of the current buffer. @@ -1602,6 +1636,8 @@ an inline src-block." (org-no-properties (org-element-interpret-data data))))) +;;;; Writing + ;;;###autoload (defun org-dblock-write:columnview (params) "Write the column view table. @@ -1778,6 +1814,8 @@ definition." (when (seq-find #'identity width-specs) (org-table-shrink)))))) +;;;; Insertion and registration + ;;;###autoload (defun org-columns-insert-dblock () "Create a dynamic block capturing a column view table." -- 2.39.5
>From f8faaac267a4e6dd0543a47eae62370f01577786 Mon Sep 17 00:00:00 2001 Message-Id: <f8faaac267a4e6dd0543a47eae62370f01577786.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 02:57:43 +0200 Subject: [PATCH 02/15] org-colview: Move summary lookup helpers * lisp/org-colview.el (org-columns--summarize) (org-columns--collect): Move near the summary computation helpers. Pure code movement. No behavior change. --- lisp/org-colview.el | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 569d93964..5689d5e9d 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -380,25 +380,6 @@ non-nil, omit the trailing space after the separator, since no further column follows." (format (if lastp "%%-%d.%ds |" "%%-%d.%ds | ") width width)) -(defun org-columns--summarize (operator) - "Return summary function associated to string OPERATOR." - (pcase (or (assoc operator org-columns-summary-types) - (assoc operator org-columns-summary-types-default)) - (`nil (error "Unknown %S operator" operator)) - (`(,_ . ,(and (pred functionp) summarize)) summarize) - (`(,_ ,summarize ,_) summarize) - (_ (error "Invalid definition for operator %S" operator)))) - -(defun org-columns--collect (operator) - "Return collect function associated to string OPERATOR. -Return nil if no collect function is associated to OPERATOR." - (pcase (or (assoc operator org-columns-summary-types) - (assoc operator org-columns-summary-types-default)) - (`nil (error "Unknown %S operator" operator)) - (`(,_ . ,(pred functionp)) nil) ;default value - (`(,_ ,_ ,collect) collect) - (_ (error "Invalid definition for operator %S" operator)))) - (defun org-columns--overlay-text (value fmt width property original) "Return decorated VALUE string for columns overlay display. FMT is a format string. WIDTH is the width of the column, as an @@ -1356,6 +1337,25 @@ Return the result as a duration." (apply fun (mapcar #'org-duration-to-minutes times)) (org-duration-h:mm-only-p times))) +(defun org-columns--summarize (operator) + "Return summary function associated to string OPERATOR." + (pcase (or (assoc operator org-columns-summary-types) + (assoc operator org-columns-summary-types-default)) + (`nil (error "Unknown %S operator" operator)) + (`(,_ . ,(and (pred functionp) summarize)) summarize) + (`(,_ ,summarize ,_) summarize) + (_ (error "Invalid definition for operator %S" operator)))) + +(defun org-columns--collect (operator) + "Return collect function associated to string OPERATOR. +Return nil if no collect function is associated to OPERATOR." + (pcase (or (assoc operator org-columns-summary-types) + (assoc operator org-columns-summary-types-default)) + (`nil (error "Unknown %S operator" operator)) + (`(,_ . ,(pred functionp)) nil) ;default value + (`(,_ ,_ ,collect) collect) + (_ (error "Invalid definition for operator %S" operator)))) + ;;;;; Tree summary computation (defun org-columns--compute-spec (spec &optional update) -- 2.39.5
>From 87406c68f9dff282d014a1094c993a4281f0cf4e Mon Sep 17 00:00:00 2001 Message-Id: <87406c68f9dff282d014a1094c993a4281f0cf4e.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 02:58:48 +0200 Subject: [PATCH 03/15] org-colview: Move format storage helper * lisp/org-colview.el (org-columns-store-format): Move near the format compilation helpers. Pure code movement. No behavior change. --- lisp/org-colview.el | 60 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 5689d5e9d..dd7d3aec5 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -1175,36 +1175,6 @@ With non-nil optional argument UP, move it up." (interactive nil org-mode) (org-columns--move-row 'up)) -(defun org-columns-store-format () - "Store the text version of the current columns format. -The format is stored either in the COLUMNS property of the node -starting the current column display, or in a #+COLUMNS line of -the current buffer." - (let ((fmt (org-columns-uncompile-format org-columns-current-fmt-compiled))) - (setq-local org-columns-current-fmt fmt) - (when org-columns-overlays - (org-with-point-at org-columns-top-level-marker - (if (and (org-at-heading-p) (org-entry-get nil "COLUMNS")) - (org-entry-put nil "COLUMNS" fmt) - (goto-char (point-min)) - (let ((case-fold-search t)) - ;; Try to replace the first COLUMNS keyword available. - (catch :found - (while (re-search-forward "^[ \t]*#\\+COLUMNS:\\(.*\\)" nil t) - (let ((element (save-match-data (org-element-at-point)))) - (when (and (org-element-type-p element 'keyword) - (equal (org-element-property :key element) - "COLUMNS")) - (replace-match (concat " " fmt) t t nil 1) - (throw :found nil)))) - ;; No COLUMNS keyword in the buffer. Insert one at the - ;; beginning, right before the first heading, if any. - (goto-char (point-min)) - (unless (org-at-heading-p) (outline-next-heading)) - (let ((inhibit-read-only t)) - (insert-before-markers "#+COLUMNS: " fmt "\n")))) - (setq-local org-columns-default-format fmt)))))) - ;;;; Display update (defun org-columns-update (property) @@ -1251,6 +1221,36 @@ the current buffer." ;;;; Format compilation +(defun org-columns-store-format () + "Store the text version of the current columns format. +The format is stored either in the COLUMNS property of the node +starting the current column display, or in a #+COLUMNS line of +the current buffer." + (let ((fmt (org-columns-uncompile-format org-columns-current-fmt-compiled))) + (setq-local org-columns-current-fmt fmt) + (when org-columns-overlays + (org-with-point-at org-columns-top-level-marker + (if (and (org-at-heading-p) (org-entry-get nil "COLUMNS")) + (org-entry-put nil "COLUMNS" fmt) + (goto-char (point-min)) + (let ((case-fold-search t)) + ;; Try to replace the first COLUMNS keyword available. + (catch :found + (while (re-search-forward "^[ \t]*#\\+COLUMNS:\\(.*\\)" nil t) + (let ((element (save-match-data (org-element-at-point)))) + (when (and (org-element-type-p element 'keyword) + (equal (org-element-property :key element) + "COLUMNS")) + (replace-match (concat " " fmt) t t nil 1) + (throw :found nil)))) + ;; No COLUMNS keyword in the buffer. Insert one at the + ;; beginning, right before the first heading, if any. + (goto-char (point-min)) + (unless (org-at-heading-p) (outline-next-heading)) + (let ((inhibit-read-only t)) + (insert-before-markers "#+COLUMNS: " fmt "\n")))) + (setq-local org-columns-default-format fmt)))))) + (defun org-columns-uncompile-format (compiled) "Turn the compiled columns format back into a string representation. -- 2.39.5
>From 9a34c7e287bf8ca25baf9dac7db3264d313ef623 Mon Sep 17 00:00:00 2001 Message-Id: <9a34c7e287bf8ca25baf9dac7db3264d313ef623.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 02:59:44 +0200 Subject: [PATCH 04/15] org-colview: Move format override variables * lisp/org-colview.el (org-overriding-columns-format) (org-local-columns-format): Move near the format selection helpers. Pure code movement. No behavior change. --- lisp/org-colview.el | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index dd7d3aec5..f91979d8e 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -706,18 +706,6 @@ See info documentation about realizing a suitable checkbox." (org-columns-next-allowed-value) t)) -(defvar org-overriding-columns-format nil - "When set, overrides any other format definition for the agenda. -Don't set this, this is meant for dynamic scoping. Set -`org-columns-default-format' and `org-columns-default-format-for-agenda' -instead. You should use this variable only in the local settings -section for a custom agenda view.") - -(defvar-local org-local-columns-format nil - "When set, overrides any other format definition for the agenda. -This can be set as a buffer local value to avoid interfering with -dynamic scoping for `org-overriding-columns-format'.") - (defun org-columns--execute-and-update (action pom key col) "Execute ACTION and update column view. POM is the point or marker for the heading. @@ -904,6 +892,18 @@ around it." ;;;; Format selection and startup +(defvar org-overriding-columns-format nil + "When set, overrides any other format definition for the agenda. +Don't set this, this is meant for dynamic scoping. Set +`org-columns-default-format' and `org-columns-default-format-for-agenda' +instead. You should use this variable only in the local settings +section for a custom agenda view.") + +(defvar-local org-local-columns-format nil + "When set, overrides any other format definition for the agenda. +This can be set as a buffer local value to avoid interfering with +dynamic scoping for `org-overriding-columns-format'.") + ;;;###autoload (defun org-columns-get-format-and-top-level () (let ((fmt (org-columns-get-format))) -- 2.39.5
>From 88a3beb48d2a8d5dd6f8b5a7ab5be1761109628d Mon Sep 17 00:00:00 2001 Message-Id: <88a3beb48d2a8d5dd6f8b5a7ab5be1761109628d.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:28:27 +0200 Subject: [PATCH 05/15] org-colview: Group summary completion helper * lisp/org-colview.el (org-columns--summary-types-completion-function): Move the column definition editing section heading above the helper. Pure code organization. No behavior change. --- lisp/org-colview.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index f91979d8e..de444bb6b 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -992,6 +992,8 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format." (goto-char (car entry)) (org-columns--display-here (cdr entry))))))))) +;;;; Column definition editing + (defun org-columns--summary-types-completion-function (string pred flag) (let ((completion-table (org-completion-table-with-metadata @@ -1015,8 +1017,6 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format." (if doc (format " -- %s" doc) ""))))))))) (complete-with-action flag completion-table string pred))) -;;;; Column definition editing - (defun org-columns-new (&optional spec &rest attributes) "Insert a new column, to the left of the current column. Interactively fill attributes for new column. When column format -- 2.39.5
>From 32438c3ab32a42e72288184ce1327924e002729c Mon Sep 17 00:00:00 2001 Message-Id: <32438c3ab32a42e72288184ce1327924e002729c.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:33:19 +0200 Subject: [PATCH 06/15] org-colview: Split format storage section * lisp/org-colview.el (org-columns-store-format): Put under a dedicated format storage section. Pure code organization. No behavior change. --- lisp/org-colview.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index de444bb6b..7cb6f3dd8 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -1219,7 +1219,7 @@ With non-nil optional argument UP, move it up." (call-interactively #'org-agenda-columns))) (message "Recomputing columns...done"))) -;;;; Format compilation +;;;; Format storage (defun org-columns-store-format () "Store the text version of the current columns format. @@ -1251,6 +1251,8 @@ the current buffer." (insert-before-markers "#+COLUMNS: " fmt "\n")))) (setq-local org-columns-default-format fmt)))))) +;;;; Format compilation + (defun org-columns-uncompile-format (compiled) "Turn the compiled columns format back into a string representation. -- 2.39.5
>From 8267f7472445c3732bc011c18ac6d20eb0438101 Mon Sep 17 00:00:00 2001 Message-Id: <8267f7472445c3732bc011c18ac6d20eb0438101.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:43:00 +0200 Subject: [PATCH 07/15] org-colview: Split view environment and header line sections * lisp/org-colview.el (org-columns-full-header-line-format) (org-previous-header-line-format): Move under a dedicated header line section. Split the combined view environment and header line section. Pure code organization. No behavior change. --- lisp/org-colview.el | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 7cb6f3dd8..6f773f143 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -547,12 +547,8 @@ substring whose `string-width' does not exceed WIDTH." string (- width (string-width org-columns-ellipses))) org-columns-ellipses)))) -;;;; Header Line and View Environment +;;;; View environment -(defvar org-columns-full-header-line-format nil - "The full header line format, will be shifted by horizontal scrolling." ) -(defvar org-previous-header-line-format nil - "The header line format before column view was turned on.") (defvar org-columns-inhibit-recalculation nil "Inhibit recomputing of columns on column view startup.") (defvar org-columns-flyspell-was-active nil @@ -564,9 +560,6 @@ for the duration of the command.") Org-num mode can cause problems in columns view, so it is turned off for the duration of the command.") -(defvar header-line-format) -(defvar org-columns-previous-hscroll 0) - (defun org-columns--suspend-conflicting-modes () "Suspend minor modes that conflict with column view." (when (setq-local org-columns-flyspell-was-active @@ -596,6 +589,15 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (when (local-variable-p 'org-colview-initial-truncate-line-value) (setq truncate-lines org-colview-initial-truncate-line-value))) +;;;; Header line + +(defvar org-columns-full-header-line-format nil + "The full header line format, will be shifted by horizontal scrolling." ) +(defvar org-previous-header-line-format nil + "The header line format before column view was turned on.") +(defvar header-line-format) +(defvar org-columns-previous-hscroll 0) + (defun org-columns--display-here-title () "Prepare the table heading with column titles for the window's header line." (let ((title "") -- 2.39.5
>From 625af2b5b2b7823771105739b970b4783ad05f91 Mon Sep 17 00:00:00 2001 Message-Id: <625af2b5b2b7823771105739b970b4783ad05f91.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:49:35 +0200 Subject: [PATCH 08/15] org-colview: Split cell actions from editing * lisp/org-colview.el (org-columns-show-value) (org-columns-todo) (org-columns-toggle-or-columns-quit) (org-columns--toggle) (org-columns-open-link): Group under a dedicated cell actions section. Pure code organization. No behavior change. --- lisp/org-colview.el | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 6f773f143..ed157d7db 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -661,12 +661,6 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (org-columns--resume-conflicting-modes) (org-columns--resume-line-wrapping))) -(defun org-columns-show-value () - "Show the full value of the property." - (interactive nil org-mode org-agenda-mode) - (let ((value (get-char-property (point) 'org-columns-value))) - (message "Value is: %s" (or value "")))) - (defvar org-agenda-columns-active) ;; defined in org-agenda.el (defun org-columns-quit () @@ -679,15 +673,13 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (message "Modification not yet reflected in Agenda buffer, use `r' to refresh"))) -;;;; Cell editing and value selection +;;;; Cell actions -(defun org-columns-check-computed () - "Throw an error if current column value is computed." - (let ((spec (nth (org-current-text-column) org-columns-current-fmt-compiled))) - (and - (nth 3 spec) - (assoc spec (get-text-property (line-beginning-position) 'org-summaries)) - (error "This value is computed from the entry's children")))) +(defun org-columns-show-value () + "Show the full value of the property." + (interactive nil org-mode org-agenda-mode) + (let ((value (get-char-property (point) 'org-columns-value))) + (message "Value is: %s" (or value "")))) (defun org-columns-todo (&optional _arg) "Change the TODO state during column view." @@ -708,6 +700,21 @@ See info documentation about realizing a suitable checkbox." (org-columns-next-allowed-value) t)) +(defun org-columns-open-link (&optional arg) + (interactive "P" org-mode org-agenda-mode) + (let ((value (get-char-property (point) 'org-columns-value))) + (org-link-open-from-string value arg))) + +;;;; Cell editing and value selection + +(defun org-columns-check-computed () + "Throw an error if current column value is computed." + (let ((spec (nth (org-current-text-column) org-columns-current-fmt-compiled))) + (and + (nth 3 spec) + (assoc spec (get-text-property (line-beginning-position) 'org-summaries)) + (error "This value is computed from the entry's children")))) + (defun org-columns--execute-and-update (action pom key col) "Execute ACTION and update column view. POM is the point or marker for the heading. @@ -887,11 +894,6 @@ around it." (mapcar (lambda (x) (format-time-string fmt (org-encode-time x))) (list time-before time time-after))))) -(defun org-columns-open-link (&optional arg) - (interactive "P" org-mode org-agenda-mode) - (let ((value (get-char-property (point) 'org-columns-value))) - (org-link-open-from-string value arg))) - ;;;; Format selection and startup (defvar org-overriding-columns-format nil -- 2.39.5
>From 47626a60cfc97a01790aa6c913beb697c05a6d16 Mon Sep 17 00:00:00 2001 Message-Id: <47626a60cfc97a01790aa6c913beb697c05a6d16.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:49:56 +0200 Subject: [PATCH 09/15] org-colview: Split display string formatting section * lisp/org-colview.el (org-columns--truncate-below-width) (org-columns-add-ellipses): Group under a dedicated display string formatting section. Pure code organization. No behavior change. --- lisp/org-colview.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index ed157d7db..f65c7661f 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -522,6 +522,8 @@ DATELINE is non-nil when the face used should be (org-columns--hide-rest-of-line) (org-columns--mark-line-read-only)))) +;;;; Display string formatting + (defun org-columns--truncate-below-width (string width) "Return a substring of STRING no wider than WIDTH. This substring must start at 0, and must be the longest possible -- 2.39.5
>From 5b4bcfba733a40dab9bc7a80887fdcf287f0a3f6 Mon Sep 17 00:00:00 2001 Message-Id: <5b4bcfba733a40dab9bc7a80887fdcf287f0a3f6.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:53:02 +0200 Subject: [PATCH 10/15] org-colview: Move header line remapping helper * lisp/org-colview.el (org-columns--remap-header-line): Move to the header line section. Pure code movement. No behavior change. --- lisp/org-colview.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index f65c7661f..cba0b62ab 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -457,12 +457,6 @@ line-beginning, keeping the rendered overlay region uneditable." "Type \\<org-columns-map>`\\[org-columns-edit-value]' \ to edit property" t))))))) -(defun org-columns--remap-header-line () - "Remap the header line to default face if not already done." - (unless org-columns-header-line-remap - (setq org-columns-header-line-remap - (face-remap-add-relative 'header-line '(:inherit default))))) - (defun org-columns--make-row (columns face) "Create and install the overlay for each column on the next character." (let ((i 0) @@ -600,6 +594,12 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (defvar header-line-format) (defvar org-columns-previous-hscroll 0) +(defun org-columns--remap-header-line () + "Remap the header line to default face if not already done." + (unless org-columns-header-line-remap + (setq org-columns-header-line-remap + (face-remap-add-relative 'header-line '(:inherit default))))) + (defun org-columns--display-here-title () "Prepare the table heading with column titles for the window's header line." (let ((title "") -- 2.39.5
>From a74680433fd7951a9786b1e69e2e8bceeeee8cdf Mon Sep 17 00:00:00 2001 Message-Id: <a74680433fd7951a9786b1e69e2e8bceeeee8cdf.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:56:58 +0200 Subject: [PATCH 11/15] org-colview: Move agenda active declaration * lisp/org-colview.el (org-agenda-columns-active): Move near the other agenda variable declarations. Pure code movement. No behavior change. --- lisp/org-colview.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index cba0b62ab..84bddd6c7 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -51,6 +51,7 @@ (declare-function face-remap-add-relative "face-remap" (face &rest specs)) (defvar org-agenda-columns-add-appointments-to-effort-sum) +(defvar org-agenda-columns-active) ;; defined in org-agenda.el (defvar org-agenda-columns-compute-summary-properties) (defvar org-agenda-columns-show-summaries) (defvar org-agenda-view-columns-initially) @@ -663,8 +664,6 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (org-columns--resume-conflicting-modes) (org-columns--resume-line-wrapping))) -(defvar org-agenda-columns-active) ;; defined in org-agenda.el - (defun org-columns-quit () "Remove the column overlays and in this way exit column editing." (interactive nil org-mode org-agenda-mode) -- 2.39.5
>From 8b5b8f7cf3f86d5f7baf46c13dcf75869766f594 Mon Sep 17 00:00:00 2001 Message-Id: <8b5b8f7cf3f86d5f7baf46c13dcf75869766f594.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 03:58:10 +0200 Subject: [PATCH 12/15] org-colview: Split value collection and widths * lisp/org-colview.el (org-columns--set-widths): Put under a dedicated column widths section. Pure code organization. No behavior change. --- lisp/org-colview.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 84bddd6c7..a3456f0d4 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -254,7 +254,7 @@ See `org-columns-summary-types' for details.") "--" ["Quit" org-columns-quit t])) -;;;; Value Collection and Widths +;;;; Value collection (defun org-columns--displayed-value (spec value &optional no-star) "Return displayed value for specification SPEC in current entry. @@ -327,6 +327,8 @@ displayed without leading stars." (list spec value (org-columns--displayed-value spec value agenda-mode)))) fmt))) +;;;; Column widths + (defun org-columns--set-widths (cache) "Compute the maximum column widths from the format and CACHE. This function sets `org-columns-current-maxwidths' as a vector of -- 2.39.5
>From 5a85c68931f9a41e2fffe6c1e9fdde234e2da767 Mon Sep 17 00:00:00 2001 Message-Id: <5a85c68931f9a41e2fffe6c1e9fdde234e2da767.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 04:01:01 +0200 Subject: [PATCH 13/15] org-colview: Nest line rendering under overlays * lisp/org-colview.el: Make line rendering a subsection of overlay rendering. (org-columns-header-line-remap): Move to the header line section. Pure code organization. No behavior change. --- lisp/org-colview.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index a3456f0d4..352f0b65e 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -367,6 +367,8 @@ where: ;;;; Overlay rendering +;;;;; Overlay helpers + (defun org-columns--new-overlay (beg end &optional string face) "Create a new column overlay and add it to the list." (let ((ov (make-overlay beg end))) @@ -404,13 +406,9 @@ ORIGINAL is the real string, i.e., before it is modified by ("TODO" (propertize v 'face (org-get-todo-face original))) (_ v))))) -(defvar-local org-columns-header-line-remap nil - "Store the relative remapping of column header-line. -This is needed to later remove this relative remapping.") - (defvar org-columns--read-only-string nil) -;;;; Row Rendering +;;;;; Line rendering (defun org-columns--pad-line-for-overlays () "Pad current line with spaces, one per column if needed. @@ -595,6 +593,9 @@ Saved value is restored by `org-columns--resume-line-wrapping'." (defvar org-previous-header-line-format nil "The header line format before column view was turned on.") (defvar header-line-format) +(defvar-local org-columns-header-line-remap nil + "Store the relative remapping of column header-line. +This is needed to later remove this relative remapping.") (defvar org-columns-previous-hscroll 0) (defun org-columns--remap-header-line () -- 2.39.5
>From 09c2e971b16593de2a25b29b7d21382f07aa7909 Mon Sep 17 00:00:00 2001 Message-Id: <09c2e971b16593de2a25b29b7d21382f07aa7909.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 04:11:23 +0200 Subject: [PATCH 14/15] org-colview: Move truncate-lines state variable * lisp/org-colview.el (org-colview-initial-truncate-line-value): Move near the line wrapping helpers. Pure code movement. No behavior change. --- lisp/org-colview.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index 352f0b65e..c48ecac2c 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -556,6 +556,8 @@ for the duration of the command.") "Remember the state of `org-num-mode' before column view. Org-num mode can cause problems in columns view, so it is turned off for the duration of the command.") +(defvar org-colview-initial-truncate-line-value nil + "Remember the value of `truncate-lines' across colview.") (defun org-columns--suspend-conflicting-modes () "Suspend minor modes that conflict with column view." @@ -638,9 +640,6 @@ This is needed to later remove this relative remapping.") org-columns-previous-hscroll hscroll) (force-mode-line-update)))) -(defvar org-colview-initial-truncate-line-value nil - "Remember the value of `truncate-lines' across colview.") - ;;;; View lifecycle ;;;###autoload -- 2.39.5
>From ad7ce7b80122b200b430db744e57dc5364f11f67 Mon Sep 17 00:00:00 2001 Message-Id: <ad7ce7b80122b200b430db744e57dc5364f11f67.1778741726.git.slawomir.grochow...@gmail.com> In-Reply-To: <[email protected]> References: <[email protected]> From: Slawomir Grochowski <[email protected]> Date: Thu, 14 May 2026 08:04:03 +0200 Subject: [PATCH 15/15] org-colview: Refine top-level section headings --- lisp/org-colview.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/org-colview.el b/lisp/org-colview.el index c48ecac2c..60ad0760c 100644 --- a/lisp/org-colview.el +++ b/lisp/org-colview.el @@ -28,6 +28,7 @@ ;; This file contains the column view for Org. ;;; Code: +;;;; Require other packages (require 'org-macs) (org-assert-version) @@ -58,7 +59,7 @@ (defvar org-inlinetask-min-level) -;;; Configuration +;;;; Customizable variables (defcustom org-columns-checkbox-allowed-values '("[ ]" "[X]") "Allowed values for columns with SUMMARY-TYPE that uses checkbox. -- 2.39.5
