Browse Source

org-colview: Fix columns format extraction

* lisp/org-colview.el (org-columns-get-format): Do not assume
  `org-columns-default-format' contains valid columns format.  Look for
  COLUMNS keyword if necessary.
(org-columns-quit): Reset `org-columns-current-fmt'.
(org-columns-redo): Provide `org-columns-current-fmt' as the active
format so as to avoid extracting it again.

* testing/lisp/test-org-colview.el (test-org-colview/get-format): New
  test.
Nicolas Goaziou 5 years ago
parent
commit
14d81d38e0
2 changed files with 75 additions and 20 deletions
  1. 33 20
      lisp/org-colview.el
  2. 42 0
      testing/lisp/test-org-colview.el

+ 33 - 20
lisp/org-colview.el

@@ -498,7 +498,8 @@ for the duration of the command.")
    (org-columns-remove-overlays)
    (let ((inhibit-read-only t))
      (remove-text-properties (point-min) (point-max) '(read-only t))))
-  (when (eq major-mode 'org-agenda-mode)
+  (if (not (eq major-mode 'org-agenda-mode))
+      (setq org-columns-current-fmt nil)
     (setq org-agenda-columns-active nil)
     (message
      "Modification not yet reflected in Agenda buffer, use `r' to refresh")))
@@ -728,14 +729,28 @@ around it."
     fmt))
 
 (defun org-columns-get-format (&optional fmt-string)
+  "Return columns format specifications.
+When optional argument FMT-STRING is non-nil, use it as the
+current specifications.  This function also sets
+`org-columns-current-fmt-compiled' and
+`org-columns-current-fmt'."
   (interactive)
-  (let (fmt-as-property fmt)
-    (when (condition-case nil (org-back-to-heading) (error nil))
-      (setq fmt-as-property (org-entry-get nil "COLUMNS" t)))
-    (setq fmt (or fmt-string fmt-as-property org-columns-default-format))
-    (setq-local org-columns-current-fmt fmt)
-    (org-columns-compile-format fmt)
-    fmt))
+  (let ((format
+	 (or fmt-string
+	     (org-entry-get nil "COLUMNS" t)
+	     (org-with-wide-buffer
+	      (goto-char (point-min))
+	      (catch :found
+		(let ((case-fold-search t))
+		  (while (re-search-forward "^[ \t]*#\\+COLUMNS: .+$" nil t)
+		    (let ((element (org-element-at-point)))
+		      (when (eq (org-element-type element) 'keyword)
+			(throw :found (org-element-property :value element)))))
+		  nil)))
+	     org-columns-default-format)))
+    (setq org-columns-current-fmt format)
+    (org-columns-compile-format format)
+    format))
 
 (defun org-columns-goto-top-level ()
   "Move to the beginning of the column view area.
@@ -956,18 +971,16 @@ the current buffer."
   "Construct the column display again."
   (interactive)
   (message "Recomputing columns...")
-  (let ((line (org-current-line))
-	(col (current-column)))
-    (save-excursion
-      (if (marker-position org-columns-begin-marker)
-	  (goto-char org-columns-begin-marker))
-      (org-columns-remove-overlays)
-      (if (derived-mode-p 'org-mode)
-	  (call-interactively 'org-columns)
-	(org-agenda-redo)
-	(call-interactively 'org-agenda-columns)))
-    (org-goto-line line)
-    (move-to-column col))
+  (org-with-wide-buffer
+   (when (marker-position org-columns-begin-marker)
+     (goto-char org-columns-begin-marker))
+   (org-columns-remove-overlays)
+   (if (derived-mode-p 'org-mode)
+       ;; Since we already know the columns format, provide it instead
+       ;; of computing again.
+       (call-interactively #'org-columns org-columns-current-fmt)
+     (org-agenda-redo)
+     (call-interactively #'org-agenda-columns)))
   (message "Recomputing columns...done"))
 
 (defun org-columns-uncompile-format (compiled)

+ 42 - 0
testing/lisp/test-org-colview.el

@@ -23,6 +23,48 @@
 
 (require 'cl-lib)
 
+(ert-deftest test-org-colview/get-format ()
+  "Test `org-columns-get-format' specifications."
+  ;; Without any clue, use `org-columns-default-format'.
+  (should
+   (equal "%A"
+	  (org-test-with-temp-text "* H"
+	    (let ((org-columns-default-format "%A"))
+	      (org-columns-get-format)))))
+  ;; If COLUMNS keyword is set, use it.
+  (should
+   (equal "%B"
+	  (org-test-with-temp-text "#+COLUMNS: %B\n* H"
+	    (let ((org-columns-default-format "%A"))
+	      (org-columns-get-format)))))
+  (should
+   (equal "%B"
+	  (org-test-with-temp-text "#+columns: %B\n* H"
+	    (let ((org-columns-default-format "%A"))
+	      (org-columns-get-format)))))
+  (should
+   (equal "%B"
+	  (org-test-with-temp-text "* H\n#+COLUMNS: %B"
+	    (let ((org-columns-default-format "%A"))
+	      (org-columns-get-format)))))
+  ;; When :COLUMNS: property is set somewhere in the tree, use it over
+  ;; the previous ways.
+  (should
+   (equal
+    "%C"
+    (org-test-with-temp-text
+	"#+COLUMNS: %B\n* H\n:PROPERTIES:\n:COLUMNS: %C\n:END:\n** S\n<point>"
+      (let ((org-columns-default-format "%A"))
+	(org-columns-get-format)))))
+  ;; When optional argument is provided, prefer it.
+  (should
+   (equal
+    "%D"
+    (org-test-with-temp-text
+	"#+COLUMNS: %B\n* H\n:PROPERTIES:\n:COLUMNS: %C\n:END:\n** S\n<point>"
+      (let ((org-columns-default-format "%A"))
+	(org-columns-get-format "%D"))))))
+
 (ert-deftest test-org-colview/columns-scope ()
   "Test `org-columns' scope."
   ;; Before the first headline, view all document.