org-colview: Allow custom COLLECT functions for derived properties

* lisp/org-colview.el (org-columns-summary-types): Allow new format.
(org-columns--summarize): Update to new summary type format.
(org-columns--collect): New function.
(org-columns--compute-spec): Apply changes.

* testing/lisp/test-org-colview.el (test-org-colview/columns-summary):
  Add test.

In addition to (LABEL . SUMMARIZE), org-columns-summary-types now
accepts (LABEL SUMMARIZE COLLECT) entries. The new COLLECT function is
called with one argument, the property being summarized.

TINYCHANGE
This commit is contained in:
Stig Brautaset 2017-09-08 20:26:56 +01:00 committed by Nicolas Goaziou
parent 3ab1afd0ea
commit 2b2314d46d
3 changed files with 127 additions and 7 deletions

View File

@ -54,6 +54,53 @@ its previous state.
Editing the column automatically expands the whole column to its full Editing the column automatically expands the whole column to its full
size. size.
*** =org-columns-summary-types= entries can take an optional COLLECT function
You can use this to make collection of a property from an entry
conditional on another entry. E.g. given this configuration:
#+BEGIN_SRC emacs-lisp
(defun custom/org-collect-confirmed (property)
"Return `PROPERTY' for `CONFIRMED' entries"
(let ((prop (org-entry-get nil property))
(confirmed (org-entry-get nil "CONFIRMED")))
(if (and prop (string= "[X]" confirmed))
prop
"0")))
(setq org-columns-summary-types
'(("X+" org-columns--summary-sum
custom/org-collect-confirmed)))
#+END_SRC
You can have a file =bananas.org= containing:
#+BEGIN_SRC org
,#+columns: %ITEM %CONFIRMED %Bananas{+} %Bananas(Confirmed Bananas){X+}
,* All shipments
,** Shipment 1
:PROPERTIES:
:CONFIRMED: [X]
:Bananas: 4
:END:
,** Shipment 2
:PROPERTIES:
:CONFIRMED: [ ]
:BANANAS: 7
:END:
#+END_SRC
... and when going to the top of that file and entering column view
you should expect to see something like:
| ITEM | CONFIRMED | Bananas | Confirmed Bananas |
|-----------------+-----------+---------+-------------------|
| All shipments | | 11 | 4 |
| Shipment 1 | [X] | 4 | 4 |
| Shipment 2 | [ ] | 7 | 7 |
#+BEGIN_EXAMPLE #+BEGIN_EXAMPLE
,#+STARTUP: shrink ,#+STARTUP: shrink
#+END_EXAMPLE #+END_EXAMPLE

View File

@ -67,7 +67,8 @@ or nil if the normal value should be used."
(defcustom org-columns-summary-types nil (defcustom org-columns-summary-types nil
"Alist between operators and summarize functions. "Alist between operators and summarize functions.
Each association follows the pattern (LABEL . SUMMARIZE) where Each association follows the pattern (LABEL . SUMMARIZE),
or (LABEL SUMMARISE COLLECT) where
LABEL is a string used in #+COLUMNS definition describing the LABEL is a string used in #+COLUMNS definition describing the
summary type. It can contain any character but \"}\". It is summary type. It can contain any character but \"}\". It is
@ -78,6 +79,13 @@ Each association follows the pattern (LABEL . SUMMARIZE) where
The second one is a format string or nil. It has to return The second one is a format string or nil. It has to return
a string summarizing the list of values. a string summarizing the list of values.
COLLECT is a function called with one argument, a property
name. It is called in the context of a headline and must
return the collected property, or the empty string. You can
use this to only collect a property if a related conditional
properties is set, e.g., to return VACATION_DAYS only if
CONFIRMED is true.
Note that the return value can become one value for an higher Note that the return value can become one value for an higher
order summary, so the function is expected to handle its own order summary, so the function is expected to handle its own
output. output.
@ -301,10 +309,22 @@ integers greater than 0."
(defun org-columns--summarize (operator) (defun org-columns--summarize (operator)
"Return summary function associated to string OPERATOR." "Return summary function associated to string OPERATOR."
(if (not operator) nil (pcase (or (assoc operator org-columns-summary-types)
(cdr (or (assoc operator org-columns-summary-types) (assoc operator org-columns-summary-types-default))
(assoc operator org-columns-summary-types-default) (`nil (error "Unknown %S operator" operator))
(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) (defun org-columns--overlay-text (value fmt width property original)
"Return text " "Return text "
@ -1110,7 +1130,9 @@ properties drawers."
(last-level lmax) (last-level lmax)
(property (car spec)) (property (car spec))
(printf (nth 4 spec)) (printf (nth 4 spec))
(summarize (org-columns--summarize (nth 3 spec)))) (operator (nth 3 spec))
(collect (and operator (org-columns--collect operator)))
(summarize (and operator (org-columns--summarize operator))))
(org-with-wide-buffer (org-with-wide-buffer
;; Find the region to compute. ;; Find the region to compute.
(goto-char org-columns-top-level-marker) (goto-char org-columns-top-level-marker)
@ -1122,7 +1144,8 @@ properties drawers."
(setq last-level level)) (setq last-level level))
(setq level (org-reduced-level (org-outline-level))) (setq level (org-reduced-level (org-outline-level)))
(let* ((pos (match-beginning 0)) (let* ((pos (match-beginning 0))
(value (org-entry-get nil property)) (value (if collect (funcall collect property)
(org-entry-get (point) property)))
(value-set (org-string-nw-p value))) (value-set (org-string-nw-p value)))
(cond (cond
((< level last-level) ((< level last-level)

View File

@ -683,6 +683,56 @@
'(("custom" . (lambda (s _) (mapconcat #'identity s "|"))))) '(("custom" . (lambda (s _) (mapconcat #'identity s "|")))))
(org-columns-default-format "%A{custom}")) (org-columns)) (org-columns-default-format "%A{custom}")) (org-columns))
(get-char-property (point) 'org-columns-value-modified)))) (get-char-property (point) 'org-columns-value-modified))))
;; Allow custom _collect_ for summary types.
(should
(equal
"2"
(org-test-with-temp-text
"* H
** S1
:PROPERTIES:
:A: 1
:END:
** S1
:PROPERTIES:
:A: 2
:A-OK: 1
:END:"
(let ((org-columns-summary-types
'(("custom" org-columns--summary-sum
(lambda (p)
(if (equal "1" (org-entry-get nil (format "%s-OK" p)))
(org-entry-get nil p)
"")))))
(org-columns-default-format "%A{custom}")) (org-columns))
(get-char-property (point) 'org-columns-value-modified))))
;; Allow custom collect function to be used for different columns
(should
(equal
'("2" "1")
(org-test-with-temp-text
"* H
** S1
:PROPERTIES:
:A: 1
:B: 1
:B-OK: 1
:END:
** S1
:PROPERTIES:
:A: 2
:B: 2
:A-OK: 1
:END:"
(let ((org-columns-summary-types
'(("custom" org-columns--summary-sum
(lambda (p)
(if (equal "1" (org-entry-get nil (format "%s-OK" p)))
(org-entry-get nil p)
"")))))
(org-columns-default-format "%A{custom} %B{custom}")) (org-columns))
(list (get-char-property (point) 'org-columns-value-modified)
(get-char-property (1+ (point)) 'org-columns-value-modified)))))
;; Allow multiple summary types applied to the same property. ;; Allow multiple summary types applied to the same property.
(should (should
(equal (equal