There are a lot of possibilities, so here are some examples to spark your imagination. Please do share your own examples by posting them on the issue tracker, and I will add them here. It will be very helpful to others to see your creativity!
Note: You can test any of these examples by evaluating the whole let
form in Emacs (or, if you open this file in Emacs, by executing the code block with C-c C-c
).
The :face
and :transformer
keywords can be used to apply faces to items in groups, or transform their strings before display. For example:
(let ((org-agenda-span 'day)
(org-super-agenda-groups
'((:name "Time grid items in all-uppercase with RosyBrown1 foreground"
:time-grid t
:transformer (--> it
(upcase it)
(propertize it 'face '(:foreground "RosyBrown1"))))
(:name "Priority >= C items underlined, on black background"
:face (:background "black" :underline t)
:not (:priority>= "C")
:order 100))))
(org-agenda nil "a"))
Of course, this is a contrived example, but you get the idea.
By setting the Org property agenda-group
and using the :auto-group
selector, you can automatically sort agenda items into groups. By default, this property is inherited, so you can set it for an entire subtree of items at once. For example, if you had this Org file:
* Tasks
** TODO Take over the universe
DEADLINE: <2017-08-01 Tue>
:PROPERTIES:
:agenda-group: grandiose plans
:END:
*** TODO Take over the world
DEADLINE: <2017-07-29 Sat>
*** TODO Take over the moon
DEADLINE: <2017-07-30 Sun>
*** TODO Take over Mars
DEADLINE: <2017-07-31 Mon>
** Recurring
:PROPERTIES:
:agenda-group: recurring
:END:
*** TODO Pay Internet bill
DEADLINE: <2017-07-28 Fri>
You could use an agenda command like this:
(let ((org-super-agenda-groups
'((:auto-group t))))
(org-agenda-list))
And you’d get an agenda looking like this:
The property name can be customized. This becomes handy if you already have, for instance, an Org property named ProjectId
attached to your items. Instead of duplicating this Org property and renaming it to agenda-group
, you can set org-super-agenda-group-property-name
to an appropriate value:
(let ((org-super-agenda-group-property-name "ProjectId")
(org-super-agenda-groups
'((:auto-group t))))
(org-agenda-list))
This causes the :auto-group
selector to sort your agenda items into groups that reflect your ProjectIds
.
Note: This selector might become obsolete in the future. A more flexible way to group by properties is the :auto-property selector.
In the same way, items can automatically be grouped by their category (which is usually the filename of the buffer they’re in).
(let ((org-super-agenda-groups
'((:auto-category t))))
(org-agenda-list))
This example groups items by the name of the directory their file is in:
(let ((org-super-agenda-groups
'((:auto-map (lambda (item)
(-when-let* ((marker (or (get-text-property 0 'org-marker item)
(get-text-property 0 'org-hd-marker item)))
(file-path (->> marker marker-buffer buffer-file-name))
(directory-name (->> file-path file-name-directory directory-file-name file-name-nondirectory)))
(concat "Directory: " directory-name)))))))
(org-agenda-list))
Conveniently, a function is already defined which does that, so you can simply pass the function name (note that, since the list is already quoted, the symbol is not quoted again, nor is #'
used):
(let ((org-super-agenda-groups
'((:auto-map org-super-agenda--dir-name))))
(org-agenda-list))
This example could also be written by using the :file-path
selector, which would return the whole path.
This example groups items by the value of their ProjectId
property (a more flexible way to group by property than this example):
(let ((org-super-agenda-groups
'((:auto-property "ProjectId"))))
(org-agenda-list))
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.
(defun emacs-p (item)
(s-matches? "emacs" item))
(let ((org-super-agenda-groups
'((:pred emacs-p))))
(org-agenda-list))
(let ((org-super-agenda-groups
'((:pred
;; A list of two functions
(emacs-p (lambda (item)
(s-matches? "Lisp" item)))))))
(org-agenda-list))
Of course, this example would be better written using the :regexp
selector, but you get the idea (better examples would be appreciated).
Here’s an example of a date-oriented, forward-looking agenda grouping.
(let ((org-super-agenda-groups
'((:log t) ; Automatically named "Log"
(:name "Schedule"
:time-grid t)
(:name "Today"
:scheduled today)
(:habit t)
(:name "Due today"
:deadline today)
(:name "Overdue"
:deadline past)
(:name "Due soon"
:deadline future)
(:name "Unimportant"
:todo ("SOMEDAY" "MAYBE" "CHECK" "TO-READ" "TO-WATCH")
:order 100)
(:name "Waiting..."
:todo "WAITING"
:order 98)
(:name "Scheduled earlier"
:scheduled past))))
(org-agenda-list))
When the agenda log mode is activated, these groups separate out tasks that you worked on or completed today. The :order-multi
sets the :order
for each subgroup to 1
, which makes it display below any groups without a defined :order
(although there are no other groups in this example).
(let ((org-super-agenda-groups
'((:order-multi (1 (:name "Done today"
:and (:regexp "State \"DONE\""
:log t))
(:name "Clocked today"
:log t))))))
(org-agenda-list))
If you’d prefer them at the top of the agenda, you could use this:
(let ((org-super-agenda-groups
'((:name "Done today"
:and (:regexp "State \"DONE\""
:log t))
(:name "Clocked today"
:log t))))
(org-agenda-list))
Let’s say it’s approaching the start of a new school year, and you want to see all tasks with a deadline before school starts. You might use something like this:
(let ((org-super-agenda-groups
'((:deadline (before "2017-09-01"))
(:discard (:anything t)))))
(org-todo-list))
Of course, you could also write that as a standard agenda command with the advanced searching syntax, and it would execute faster.
What if you wanted to group tasks that are due before the end of the current month? You could use something like this:
(-let* (((sec minute hour day month year dow dst utcoff) (decode-time))
(last-day-of-month (calendar-last-day-of-month month year))
(target-date
;; A hack that seems to work fine. Yay, Postel!
(format "%d-%02d-%02d" year month (1+ last-day-of-month)))
(org-super-agenda-groups
`((:deadline (before ,target-date))
(:discard (:anything t)))))
(org-todo-list))
With the :children
selector you can select items that have children. Assuming items without children aren’t considered projects, you can view projects like this:
(let ((org-super-agenda-groups
'((:name "Projects"
:children t)
(:discard (:anything t)))))
(org-todo-list))
You might want to put that at the end of a daily/weekly agenda view using a custom command that runs a series of agenda commands, like this:
(let ((org-agenda-custom-commands
'(("u" "Super view"
((agenda "" ((org-super-agenda-groups
'((:name "Today"
:time-grid t)))))
(todo "" ((org-agenda-overriding-header "Projects")
(org-super-agenda-groups
'((:name none ; Disable super group header
:children todo)
(:discard (:anything t)))))))))))
(org-agenda nil "u"))
Note that the :children
matcher may be quite slow in views like org-todo-list
(i.e. the todo
agenda command in the list above), especially if used to match to-do items. It would be faster to use org-agenda-skip-function
. In a daily/weekly agenda it should perform well enough.
This shows TO-READ
to-do items with the tags :book:
or :books:
.
(let ((org-super-agenda-groups
'((:discard (:not ; Is it easier to read like this?
(:and
(:todo "TO-READ" :tag ("book" "books"))))))))
(org-todo-list))
Remember that items that are not matched by a group selector fall through to the next selector or to the catch-all group. So you might think that this simpler command would work:
(let ((org-super-agenda-groups
'((:and (:todo "TO-READ" :tag ("book" "books"))))))
(org-todo-list))
But while it would indeed group together those items, it would also display all other to-do items in the Other items
section below, so you must :discard
the items you don’t want. So another way to write this query would be to select the items you want and discard everything else:
(let ((org-super-agenda-groups
'((:name "Books to read"
:and (:todo "TO-READ" :tag ("book" "books")))
(:discard (:anything t)))))
(org-todo-list))
Note that you could run part of this query with a standard agenda command, and it would be faster. But since the org-tags-view
and org-todo-list
can only select by tags or todo-keywords, respectively, the other part of the selection must be done with grouping. Here are two examples (note that they each produce the same results):
(let ((org-super-agenda-groups
'((:discard (:not (:todo "TO-READ"))))))
(org-tags-view nil "books|book"))
;; These commands produce the same results
(let ((org-super-agenda-groups
'((:discard (:not (:tag ("book" "books")))))))
(org-todo-list "TO-READ"))
Of course, the most canonical (and probably fastest) way to write this query is to use org-search-view
, like this:
(org-search-view t "+{:book\\|books:} +TO-READ")
Or if you’re inputting the string manually after pressing C-c a S
, you’d input +{:book\|books:} +TO-READ
. But if you’re like me, and you forget the advanced searching syntax, you might find these more “lispy” grouping/selecting constructs easier to use, even if they can be slower on large datasets.
And note that even if you use the built-in searching with org-search-view
, you might still want to use this package to group results, perhaps like this:
(let ((org-super-agenda-groups
'((:name "Computer books"
:tag ("computer" "computers" "programming" "software"))
;; All other books would be displayed here
)))
(org-search-view t "+{:book\\|books:} +TO-READ"))
This shows all to-do items with the :Emacs:
tag, and groups together anything related to Org. You can see the use of the rx
macro by backquoting the list and unquoting the rx
form.
(let ((org-super-agenda-groups
`((:name "Org-related"
:tag "Org"
:regexp ("org-mode"
,(rx bow "org" eow))))))
(org-tags-view t "Emacs"))
The origami package works “out-of-the-box” with org-super-agenda
. Just activate origami-mode
in the agenda buffer and use the command origami-toggle-node
to fold groups. You can bind, e.g. TAB
to that command in the header map, and then you can easily collapse groups as if they were an outline.
You could even fold certain groups by default, perhaps like this (this use-package
form should probably go inside a (use-package org-super-agenda ...)
form’s :config
section):
(use-package origami
:general (:keymaps 'org-super-agenda-header-map
"TAB" #'origami-toggle-node)
:config
(defvar ap/org-super-agenda-auto-show-groups
'("Schedule" "Bills" "Priority A items" "Priority B items"))
(defun ap/org-super-agenda-origami-fold-default ()
"Fold certain groups by default in Org Super Agenda buffer."
(forward-line 3)
(cl-loop do (origami-forward-toggle-node (current-buffer) (point))
while (origami-forward-fold-same-level (current-buffer) (point)))
(--each ap/org-super-agenda-auto-show-groups
(goto-char (point-min))
(when (re-search-forward (rx-to-string `(seq bol " " ,it)) nil t)
(origami-show-node (current-buffer) (point)))))
:hook ((org-agenda-mode . origami-mode)
(org-agenda-finalize . ap/org-super-agenda-origami-fold-default)))
(setq org-super-agenda-groups
'((:name "Next Items"
:time-grid t
:tag ("NEXT" "outbox"))
(:name "Important"
:priority "A")
(:name "Quick Picks"
:effort< "0:30")
(:priority<= "B"
:scheduled future
:order 1)))
(setq spacemacs-theme-org-agenda-height nil
org-agenda-time-grid '((daily today require-timed) "----------------------" nil)
org-agenda-skip-scheduled-if-done t
org-agenda-skip-deadline-if-done t
org-agenda-include-deadlines t
org-agenda-include-diary t
org-agenda-block-separator nil
org-agenda-compact-blocks t
org-agenda-start-with-log-mode t)
(setq org-agenda-custom-commands
'(("z" "Super zaen view"
((agenda "" ((org-agenda-span 'day)
(org-super-agenda-groups
'((:name "Today"
:time-grid t
:date today
:todo "TODAY"
:scheduled today
:order 1)))))
(alltodo "" ((org-agenda-overriding-header "")
(org-super-agenda-groups
'((:name "Next to do"
:todo "NEXT"
:order 1)
(:name "Important"
:tag "Important"
:priority "A"
:order 6)
(:name "Due Today"
:deadline today
:order 2)
(:name "Due Soon"
:deadline future
:order 8)
(:name "Overdue"
:deadline past
:order 7)
(:name "Assignments"
:tag "Assignment"
:order 10)
(:name "Issues"
:tag "Issue"
:order 12)
(:name "Projects"
:tag "Project"
:order 14)
(:name "Emacs"
:tag "Emacs"
:order 13)
(:name "Research"
:tag "Research"
:order 15)
(:name "To read"
:tag "Read"
:order 30)
(:name "Waiting"
:todo "WAITING"
:order 20)
(:name "trivial"
:priority<= "C"
:tag ("Trivial" "Unimportant")
:todo ("SOMEDAY" )
:order 90)
(:discard (:tag ("Chore" "Routine" "Daily")))))))))))