From db3a250482886197a20c3e417f61c1d96977f4d9 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sun, 21 Jul 2019 02:55:44 -0500 Subject: [PATCH 1/6] Add: :auto-date selector This is pretty handy, especially in the coming version of org-sidebar. I don't like how much timestamp-related code this requires, but maybe that can be reduced using ts.el in the future. --- README.org | 5 ++++ org-super-agenda.el | 59 +++++++++++++++++++++++++++++++++++++++++-- org-super-agenda.info | 41 +++++++++++++++++++----------- test/results.el | 47 ++++++++++++++++++++++++++++++++++ test/test.el | 5 ++++ 5 files changed, 140 insertions(+), 17 deletions(-) diff --git a/README.org b/README.org index ea30d01..14c49cb 100644 --- a/README.org +++ b/README.org @@ -168,6 +168,7 @@ Every selector requires an argument, even if it's just ~t~, e.g. ~:anything~, ~: + =:anything= :: Select every item, no matter what. This is probably most useful with ~:discard~, because it doesn't actually test anything, so it's faster than, e.g. ~:regexp "."~, which has to get the entry text for every item. + =:auto-category= :: This automatically groups items by their category (usually the filename it's in, without the =.org= suffix). + ~:auto-dir-name~ :: This automatically groups items by the directory name of their source buffer. ++ =:auto-date= :: This automatically groups items by their earliest of scheduled date or deadline, formatted according to variable ~org-super-agenda-date-format~. + =:auto-group= :: This selects items that have the =agenda-group= Org property set. By setting this property for a subtree, every item in it will be sorted into an agenda group by that name and placed into the agenda where the ~:auto-group~ selector is ([[examples.org#automatically-by-group][example]]). + ~:auto-map~ :: This automatically groups items by the value returned when applying each item to the given function as a string from the agenda buffer ([[examples.org#automatically-by-mapping-a-function][example]]). The function should return a string to be used as the grouping key and as the header for its group. + ~:auto-parent~ :: This automatically groups items by their parent heading. This is surprisingly handy, especially if you group tasks hierarchically by project and use agenda restrictions to limit the agenda to a subtree. @@ -218,6 +219,10 @@ These selectors take one argument alone, or multiple arguments in a list. ** 1.2-pre +*Added* ++ Selector ~:auto-date~, which groups items by their earliest of scheduled date or deadline, formatted according to variable ~org-super-agenda-date-format~. ++ Option ~org-super-agenda-date-format~, used to format date headers in the ~:auto-date~ selector. + *Fixed* + =:children todo= group selection ([[https://github.com/alphapapa/org-super-agenda/issues/75][#75]]). (Thanks to [[https://github.com/bleggett][Ben Leggett]].) + =:children= group headings. diff --git a/org-super-agenda.el b/org-super-agenda.el index f108e9d..fdd2801 100644 --- a/org-super-agenda.el +++ b/org-super-agenda.el @@ -183,6 +183,11 @@ making it stretch across the screen." "String inserted before group headers." :type 'string) +(defcustom org-super-agenda-date-format "%e %B %Y" + "Format string for date headers. +See `format-time-string'." + :type 'string) + ;;;; Faces (defface org-super-agenda-header '((t (:inherit org-agenda-structure))) @@ -222,6 +227,20 @@ If ANY is non-nil, return as soon as FORM returns non-nil." ;;;; Support functions +(defun org-super-agenda--org-timestamp-element< (a b) + "Return non-nil if A's date element is earlier than B's. +A and B are Org timestamp elements." + ;; Copied from `org-ql'. + (cl-macrolet ((ts (ts) + `(when ,ts + (org-timestamp-format ,ts "%s")))) + (let* ((a-ts (ts a)) + (b-ts (ts b))) + (cond ((and a-ts b-ts) + (string< a-ts b-ts)) + (a-ts t) + (b-ts nil))))) + (defsubst org-super-agenda--get-marker (s) "Return `org-marker' text properties of string S." (org-find-text-property-in-string 'org-marker s)) @@ -777,7 +796,8 @@ The string should be the priority cookie letter, e.g. \"A\".") ;;;;; Auto-grouping (cl-defmacro org-super-agenda--def-auto-group (name docstring-ending - &key keyword key-form (header-form 'key)) + &key keyword key-form + (header-form 'key) (key-sort-fn #'string<)) "Define an auto-grouping function. The function will be named `org-super-agenda--auto-group-NAME'. @@ -790,6 +810,9 @@ Items will be grouped by the value of KEY-FORM evaluated for each item, with the variable `item' bound to the string from the agenda buffer. +Group headers will be sorted by KEY-SORT-FN; usually the default +will suffice. + The groups' headers will be the value of HEADER-FORM, evaluated for each group after items are grouped, with the variable `key' bound to the group's key. The form defaults to `key'. @@ -821,7 +844,7 @@ of the arguments to the function." else collect item into non-matching finally return (list ,keyword non-matching - (cl-loop for key in (sort (ht-keys groups) #'string<) + (cl-loop for key in (sort (ht-keys groups) #',key-sort-fn) for name = ,header-form collect (list :name name :items (nreverse (ht-get groups key))))))) @@ -829,6 +852,38 @@ of the arguments to the function." ,keyword #',fn-name)) (add-to-list 'org-super-agenda-auto-selector-keywords ,keyword))))) +(org-super-agenda--def-auto-group date + "their earliest deadline or scheduled date (formatted according to `org-super-agenda-date-format', which see)" + :keyword :auto-date + ;; This is convoluted, mainly because dates and times in Emacs are kind of + ;; insane. Good luck parsing a simple "%e %B %Y"-formatted time back to a + ;; time value that can be compared. It's virtually impossible, at least + ;; without a lot of work (hence my ts.el package, but it's not yet mature + ;; enough to use here). So we store the Org timestamp element in the text + ;; properties of the formatted time. + :key-form (cl-flet ((get-date-type (type) + (when-let* ((date-string (org-entry-get (point) type))) + (with-temp-buffer + ;; FIXME: Hack: since we're using (org-element-property + ;; :type date-element) below, we need this date parsed + ;; into an org-element element. + (insert date-string) + (goto-char 0) + (org-element-timestamp-parser))))) + (org-super-agenda--when-with-marker-buffer (org-super-agenda--get-marker item) + ;; MAYBE: Also check CLOSED date. + (let ((earliest-ts (car (sort (list (get-date-type "SCHEDULED") + (get-date-type "DEADLINE")) + #'org-super-agenda--org-timestamp-element<)))) + (pcase earliest-ts + ('nil nil) + (_ (propertize (org-timestamp-format earliest-ts org-super-agenda-date-format) + 'org-super-agenda-ts earliest-ts)))))) + :key-sort-fn (lambda (a b) + (org-super-agenda--org-timestamp-element< + (get-text-property 0 'org-super-agenda-ts a) + (get-text-property 0 'org-super-agenda-ts b)))) + (org-super-agenda--def-auto-group items "their AGENDA-GROUP property" :keyword :auto-group :key-form (org-entry-get (org-super-agenda--get-marker item) diff --git a/org-super-agenda.info b/org-super-agenda.info index b6510ac..b054ef6 100644 --- a/org-super-agenda.info +++ b/org-super-agenda.info @@ -319,6 +319,10 @@ Every selector requires an argument, even if it’s just ‘t’, e.g. ‘‘:auto-dir-name’’ This automatically groups items by the directory name of their source buffer. +‘:auto-date’ + This automatically groups items by their earliest of scheduled date + or deadline, formatted according to variable + ‘org-super-agenda-date-format’. ‘:auto-group’ This selects items that have the agenda-group Org property set. By setting this property for a subtree, every item in it will be @@ -494,7 +498,14 @@ File: README.info, Node: 12-pre, Next: 111, Up: Changelog 6.1 1.2-pre =========== -*Fixed* +*Added* + • Selector ‘:auto-date’, which groups items by their earliest of + scheduled date or deadline, formatted according to variable + ‘org-super-agenda-date-format’. + • Option ‘org-super-agenda-date-format’, used to format date headers + in the ‘:auto-date’ selector. + + *Fixed* • :children todo group selection (#75 (https://github.com/alphapapa/org-super-agenda/issues/75)). (Thanks to Ben Leggett (https://github.com/bleggett).) @@ -658,20 +669,20 @@ Node: Examples4297 Node: Group selectors7782 Node: Keywords8971 Node: Special selectors9712 -Node: Normal selectors12374 -Node: Tips17005 -Node: Changelog17856 -Node: 12-pre18081 -Node: 11118430 -Node: 1118603 -Node: 10320175 -Node: 10220386 -Node: 10120520 -Node: 10020858 -Node: Development20963 -Node: Bugs21365 -Node: Tests22018 -Node: Credits22337 +Node: Normal selectors12555 +Node: Tips17186 +Node: Changelog18037 +Node: 12-pre18262 +Node: 11118920 +Node: 1119093 +Node: 10320665 +Node: 10220876 +Node: 10121010 +Node: 10021348 +Node: Development21453 +Node: Bugs21855 +Node: Tests22508 +Node: Credits22827  End Tag Table diff --git a/test/results.el b/test/results.el index 2f240b8..91a9e36 100644 --- a/test/results.el +++ b/test/results.el @@ -1830,4 +1830,51 @@ Wednesday 5 July 2017 ideas: Scheduled: SOMEDAY Rewrite Emacs in Common Lisp :Emacs:elisp:computers:software:programming: test: In 16 d.: TODO [#B] Internet :bills: test: Scheduled: TODO [#C] Get haircut :personal:@town: +" "415e1172e603da0549cd21c3d9ecf418" "Day-agenda (W27): +Wednesday 5 July 2017 + + 4 July 2017 + ambition: Sched. 1x: TODO [#A] Skype with president of Antarctica :universe:ambition:world::meetings: + + 5 July 2017 + test: 18:00...... Scheduled: TODO Order a pizza :food:dinner: + test: Scheduled: TODO [#B] Fix flux capacitor :spaceship:shopping:@computer: + test: Scheduled: TODO Shop for groceries :food:shopping:@town: + ideas: Scheduled: SOMEDAY Rewrite Emacs in Common Lisp :Emacs:elisp:computers:software:programming: + test: Deadline: CHECK /r/emacs :website:Emacs: + test: Scheduled: TODO [#C] Get haircut :personal:@town: + ambition: TODO Practice leaping tall ! :universe:ambition::personal: + + 7 July 2017 + ambition: In 2 d.: TODO [#A] Take over the world :universe:ambition::world: + + 10 July 2017 + ambition: In 5 d.: TODO [#B] Renew membership in supervillain club :universe:ambition:: + + 15 July 2017 + ambition: In 10 d.: TODO [#A] Take over the universe :universe:ambition: + + 21 July 2017 + test: In 16 d.: TODO [#B] Internet :bills: + + 1 August 2017 + test: In 27 d.: TODO [#A] Spaceship lease :bills:spaceship: + + 27 August 2017 + ambition: In 53 d.: WAITING Visit the moon :universe:ambition::space:travel: + + 20 September 2017 + ambition: In 77 d.: TODO Visit Mars :universe:ambition::space:travel:planet: + + Other items + test: 7:02...... Sunrise (12:04 of daylight) + 8:00...... ---------------- + 10:00...... ---------------- + 12:00...... now - - - - - - - - - - - - - - - - - - - - - - - - - + 12:00...... ---------------- + 14:00...... ---------------- + 16:00...... ---------------- + 18:00...... ---------------- + test: 19:07...... Sunset + 20:00...... ---------------- ")) \ No newline at end of file diff --git a/test/test.el b/test/test.el index 7bd29be..6a64472 100644 --- a/test/test.el +++ b/test/test.el @@ -625,6 +625,11 @@ buffer and do not save the results." (should (org-super-agenda--test-run :groups '((:auto-category t))))) +(ert-deftest org-super-agenda--test-:auto-date () + ;; DONE: Works. + (should (org-super-agenda--test-run + :groups '((:auto-date t))))) + (ert-deftest org-super-agenda--test-:auto-group () ;; DONE: Works. (should (org-super-agenda--test-run From cf0e57bddaa1544332490aa5f29bf3c378844d78 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sun, 21 Jul 2019 02:57:04 -0500 Subject: [PATCH 2/6] Tidy: Headers --- notes.org | 2 +- org-super-agenda.el | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/notes.org b/notes.org index 263b1e1..7ea6023 100644 --- a/notes.org +++ b/notes.org @@ -1260,7 +1260,7 @@ CLOSED: [2017-07-28 Fri 00:02] - State "DONE" from "TODO" [2017-07-28 Fri 00:02] :END: -#+BEGIN_SRC elisp +#+BEGIN_SRC elisp :results silent (defun osa/describe-groupers () (require 'dash-functional) (let ((groups (cl-loop for (group-type fn) on org-super-agenda-group-types by 'cddr diff --git a/org-super-agenda.el b/org-super-agenda.el index fdd2801..e76f333 100644 --- a/org-super-agenda.el +++ b/org-super-agenda.el @@ -142,6 +142,8 @@ only with point on the group headers (e.g. use `origami' to fold group headings by binding a key to `origami-toggle-node' in this map).") +;;;; Customization + (defgroup org-super-agenda nil "Settings for `org-super-agenda'." :group 'org From 3f957dad0d297ecb6de689e2497677dd06833a36 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sun, 21 Jul 2019 17:34:19 -0500 Subject: [PATCH 3/6] Docs: Improve example --- examples.org | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/examples.org b/examples.org index 5f60c50..d05986b 100644 --- a/examples.org +++ b/examples.org @@ -152,17 +152,18 @@ This example groups items by the value of their =ProjectId= property (a more fle You can also use one or more arbitrary predicate functions, including lambdas. Note that, since the group list is already quoted, function name symbols are not quoted again, nor is ~#'~ used. #+BEGIN_SRC elisp - (defun pizza-p (item) - (s-matches? "pizza" item)) + (defun emacs-p (item) + (s-matches? "emacs" item)) (let ((org-super-agenda-groups - '((:pred pizza-p)))) + '((:pred emacs-p)))) (org-agenda-list)) (let ((org-super-agenda-groups - '((:pred (pizza-p - (lambda (item) - (s-matches? "Skype" item))))))) + '((:pred + ;; A list of two functions + (emacs-p (lambda (item) + (s-matches? "Lisp" item))))))) (org-agenda-list)) #+END_SRC From 789e43ab07f99705bae120dd8fb321967687a0b9 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Tue, 23 Jul 2019 19:54:57 -0500 Subject: [PATCH 4/6] Change: (--make-agenda-header) Append header face Instead of overriding it. This allows other faces to be used as well, e.g. to-do keyword faces. --- README.org | 3 +++ org-super-agenda.el | 13 +++++++------ org-super-agenda.info | 24 ++++++++++++++---------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/README.org b/README.org index 14c49cb..cba9d58 100644 --- a/README.org +++ b/README.org @@ -223,6 +223,9 @@ These selectors take one argument alone, or multiple arguments in a list. + Selector ~:auto-date~, which groups items by their earliest of scheduled date or deadline, formatted according to variable ~org-super-agenda-date-format~. + Option ~org-super-agenda-date-format~, used to format date headers in the ~:auto-date~ selector. +*Changed* ++ Group headers face is now appended to face list instead of overriding it. + *Fixed* + =:children todo= group selection ([[https://github.com/alphapapa/org-super-agenda/issues/75][#75]]). (Thanks to [[https://github.com/bleggett][Ben Leggett]].) + =:children= group headings. diff --git a/org-super-agenda.el b/org-super-agenda.el index e76f333..a2d6f9d 100644 --- a/org-super-agenda.el +++ b/org-super-agenda.el @@ -261,12 +261,13 @@ Prepended with `org-super-agenda-header-separator'." (pcase s ('none "") (_ (setq s (concat " " s)) - (org-add-props s nil 'face 'org-super-agenda-header - 'keymap org-super-agenda-header-map - ;; NOTE: According to the manual, only `keymap' should be necessary, but in my - ;; testing, it only takes effect in Agenda buffers when `local-map' is set, so - ;; we'll use both. - 'local-map org-super-agenda-header-map) + (add-face-text-property 0 (length s) 'org-super-agenda-header t s) + (org-add-props s nil + 'keymap org-super-agenda-header-map + ;; NOTE: According to the manual, only `keymap' should be necessary, but in my + ;; testing, it only takes effect in Agenda buffers when `local-map' is set, so + ;; we'll use both. + 'local-map org-super-agenda-header-map) (concat org-super-agenda-header-separator s)))) (defsubst org-super-agenda--get-priority-cookie (s) diff --git a/org-super-agenda.info b/org-super-agenda.info index b054ef6..3d10d9a 100644 --- a/org-super-agenda.info +++ b/org-super-agenda.info @@ -505,6 +505,10 @@ File: README.info, Node: 12-pre, Next: 111, Up: Changelog • Option ‘org-super-agenda-date-format’, used to format date headers in the ‘:auto-date’ selector. + *Changed* + • Group headers face is now appended to face list instead of + overriding it. + *Fixed* • :children todo group selection (#75 (https://github.com/alphapapa/org-super-agenda/issues/75)). @@ -673,16 +677,16 @@ Node: Normal selectors12555 Node: Tips17186 Node: Changelog18037 Node: 12-pre18262 -Node: 11118920 -Node: 1119093 -Node: 10320665 -Node: 10220876 -Node: 10121010 -Node: 10021348 -Node: Development21453 -Node: Bugs21855 -Node: Tests22508 -Node: Credits22827 +Node: 11119020 +Node: 1119193 +Node: 10320765 +Node: 10220976 +Node: 10121110 +Node: 10021448 +Node: Development21553 +Node: Bugs21955 +Node: Tests22608 +Node: Credits22927  End Tag Table From cc58fb8379fc3b6befe6e2b410ca05d1f7669f57 Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Tue, 23 Jul 2019 19:56:00 -0500 Subject: [PATCH 5/6] Add: Apply to-do keyword faces to group headers --- README.org | 1 + org-super-agenda.el | 7 +++++-- org-super-agenda.info | 21 +++++++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.org b/README.org index cba9d58..46057f2 100644 --- a/README.org +++ b/README.org @@ -222,6 +222,7 @@ These selectors take one argument alone, or multiple arguments in a list. *Added* + Selector ~:auto-date~, which groups items by their earliest of scheduled date or deadline, formatted according to variable ~org-super-agenda-date-format~. + Option ~org-super-agenda-date-format~, used to format date headers in the ~:auto-date~ selector. ++ To-do keyword faces are applied to keywords in group headers. *Changed* + Group headers face is now appended to face list instead of overriding it. diff --git a/org-super-agenda.el b/org-super-agenda.el index a2d6f9d..275f2ba 100644 --- a/org-super-agenda.el +++ b/org-super-agenda.el @@ -663,7 +663,9 @@ Argument may be a string or list of strings, or `t' to match any keyword, or `nil' to match only non-todo items." :section-name (pcase (car args) ((pred stringp) ;; To-do keyword given - (concat (s-join " and " args) " items")) + (concat (s-join " and " (--map (propertize it 'face (org-get-todo-face it)) + args)) + " items")) ('t ;; Test for any to-do keyword "Any TODO keyword") ('nil ;; Test for not having a to-do keyword @@ -915,7 +917,8 @@ of the arguments to the function." (org-super-agenda--def-auto-group todo "their to-do keyword" :keyword :auto-todo - :key-form (org-find-text-property-in-string 'todo-state item) + :key-form (when-let* ((keyword (org-find-text-property-in-string 'todo-state item))) + (propertize keyword 'face (org-get-todo-face keyword))) :header-form (concat "To-do: " key)) (org-super-agenda--def-auto-group dir-name "their parent heading" diff --git a/org-super-agenda.info b/org-super-agenda.info index 3d10d9a..f7daba0 100644 --- a/org-super-agenda.info +++ b/org-super-agenda.info @@ -504,6 +504,7 @@ File: README.info, Node: 12-pre, Next: 111, Up: Changelog ‘org-super-agenda-date-format’. • Option ‘org-super-agenda-date-format’, used to format date headers in the ‘:auto-date’ selector. + • To-do keyword faces are applied to keywords in group headers. *Changed* • Group headers face is now appended to face list instead of @@ -677,16 +678,16 @@ Node: Normal selectors12555 Node: Tips17186 Node: Changelog18037 Node: 12-pre18262 -Node: 11119020 -Node: 1119193 -Node: 10320765 -Node: 10220976 -Node: 10121110 -Node: 10021448 -Node: Development21553 -Node: Bugs21955 -Node: Tests22608 -Node: Credits22927 +Node: 11119089 +Node: 1119262 +Node: 10320834 +Node: 10221045 +Node: 10121179 +Node: 10021517 +Node: Development21622 +Node: Bugs22024 +Node: Tests22677 +Node: Credits22996  End Tag Table From 375bde4ca72494ac88a2a9738754f047fe45cc4e Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Tue, 23 Jul 2019 22:08:25 -0500 Subject: [PATCH 6/6] Comment: Add TODO --- org-super-agenda.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org-super-agenda.el b/org-super-agenda.el index 275f2ba..5a1cf10 100644 --- a/org-super-agenda.el +++ b/org-super-agenda.el @@ -857,6 +857,8 @@ of the arguments to the function." ,keyword #',fn-name)) (add-to-list 'org-super-agenda-auto-selector-keywords ,keyword))))) +;; TODO: auto-year and auto-month groups. Maybe also auto-quarter, auto-week, etc. Maybe also auto-next-7-days, something like that. + (org-super-agenda--def-auto-group date "their earliest deadline or scheduled date (formatted according to `org-super-agenda-date-format', which see)" :keyword :auto-date