From feee8c590e108a9a75324112f2348f021743892f Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 11 Sep 2022 20:17:00 -0400 Subject: [PATCH] Tweak emacs agenda and mu4e settings --- doom.d/config.el | 120 ++++++++++++++++++++++++++++++++++++++++- doom.d/config.org | 135 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 253 insertions(+), 2 deletions(-) diff --git a/doom.d/config.el b/doom.d/config.el index d1f0dc3..f7de963 100644 --- a/doom.d/config.el +++ b/doom.d/config.el @@ -279,6 +279,121 @@ work if it thinks it needs to." (use-package! anki-editor) +(after! org + (setq org-agenda-custom-commands + '(("p" . "Project Views")))) + +(defun org-compare--get-marker (entry) + "Return the marker for ENTRY. + +This marker points to the location of the headline referenced by +ENTRY." + (get-text-property 1 'org-marker entry)) + +(defvar org-compare-random-refresh nil + "Whether `org-compare-randomly' should refresh its keys. + +See the docs for `org-compare-randomly' for more information.") + +(defun org-compare-randomly--update-sort-key (entry table generator) + "Return sort key for ENTRY in TABLE, generating it if necessary. +For internal use by `org-compare-randomly-by'." + (let* ((marker (org-compare--get-marker entry)) + (hash-key `(,(marker-buffer marker) . ,(marker-position marker)))) + (or (gethash hash-key table) + (puthash hash-key (funcall generator entry) table)))) + +(defun org-compare-randomly-by (generator) + "Return a random comparator using GENERATOR. + +The comparator returned is like `org-compare-randomly', except +the distribution of random keys is controlled by GENERATOR and +may thus be non-uniform. + +The function GENERATOR is called with a single argument, an +agenda entry, when that entry lacks a sort key. It should return +a number, which is then used for all comparisons until the key +list is cleared; see `org-compare-randomly' for more details on +this. + +Subsequent calls to `org-compare-randomly-by' produce comparators +with independent sets of sort keys." + (let ((table (make-hash-table :test #'equal))) + (lambda (x y) + (when org-compare-random-refresh + (clrhash table) + (setq org-compare-random-refresh nil)) + (let ((x-val (org-compare-randomly--update-sort-key x table generator)) + (y-val (org-compare-randomly--update-sort-key y table generator))) + (cond + ((= x-val y-val) nil) + ((< x-val y-val) -1) + ((> x-val y-val) +1)))))) + +(defun org-compare-randomly () + "Return a comparator implementing a random shuffle. + +When given distinct agenda entries X and Y, the resulting +comparator has an equal chance of returning +1 and -1 (and a +miniscule chance of returning nil). Subsequent calls will produce +results consistent with a total ordering. + +To accomplish this, a hash table of randomly-generated sort keys +is maintained. This table will persist until the comparator is +called when the variable `org-compare-random-refresh' is non-nil. +This means that setting this variable as part of a custom agenda +command using this comparator as `org-agenda-cmp-user-defined' +will cause the sort order to change whenever the agenda is +refreshed; otherwise, it will persist until Emacs is restarted. + +Note that if you don't want the sort order to change on refresh, +you need to be careful that the comparator is created when the +custom agenda command is defined, not when it's called, e.g. + + (add-to-list + 'org-agenda-custom-commands + `(\"y\" \"Example Agenda\" + ((todo + \"\" + ((org-agenda-cmp-user-defined ',(org-compare-randomly)) + (org-agenda-sorting-strategy '(user-defined-up))))))) + +\(Notice the use of backquote.) + +Comparators resulting from different calls to this function have +independent key tables." + (org-compare-randomly-by (lambda (_) (random)))) + +(after! org + (add-to-list 'org-agenda-custom-commands + '("pr" "Random Project TODOs" + ((tags "proj/TODO" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))) + (tags "proj/STRT" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))) + (tags "proj/PROJ" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))) + (todo "IDEA" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))))))) + +(after! org + (setq org-agenda-dim-blocked-tasks nil)) + +(after! org + (setq org-tag-alist '(("proj" . ?p)))) + (use-package! magit-todos :hook (magit-mode . magit-todos-mode)) @@ -403,7 +518,10 @@ work if it thinks it needs to." (setq mu4e-maildir-shortcuts '((:maildir "/nathan@mccarty.io/Folders/Notifications/Github" :key ?h) (:maildir "/nathan@mccarty.io/Folders/Notifications/Gitlab" :key ?l) - (:maildir "/nathan@mccarty.io/Folders/Notifications/SourceHut" :key ?s)))) + (:maildir "/nathan@mccarty.io/Folders/Notifications/SourceHut" :key ?s) + (:maildir "/nathan@mccarty.io/Folders/Archival/Receipts/2022" :key ?r) + (:maildir "/nathan@mccarty.io/Folders/Job Search" :key ?j) + (:maildir "/nathan@mccarty.io/Folders/Archival/Informed Delivery" :key ?i)))) (after! mu4e (mu4e-alert-enable-mode-line-display)) diff --git a/doom.d/config.org b/doom.d/config.org index 9d36a9b..14c7257 100644 --- a/doom.d/config.org +++ b/doom.d/config.org @@ -1,3 +1,4 @@ +# -*- lexical-binding: t; -*- #+title: Nathan's Doom Emacs Configuration #+author: Nathan McCarty #+PROPERTY: header-args:emacs-lisp :tangle yes @@ -398,6 +399,135 @@ Flash cards from within emacs. #+begin_src emacs-lisp (use-package! anki-editor) #+end_src +** org-agenda customization +Empty out the list and define our prefixes first +#+begin_src emacs-lisp +(after! org + (setq org-agenda-custom-commands + '(("p" . "Project Views")))) +#+end_src +*** Random project selection +First some library code +#+begin_src emacs-lisp +(defun org-compare--get-marker (entry) + "Return the marker for ENTRY. + +This marker points to the location of the headline referenced by +ENTRY." + (get-text-property 1 'org-marker entry)) + +(defvar org-compare-random-refresh nil + "Whether `org-compare-randomly' should refresh its keys. + +See the docs for `org-compare-randomly' for more information.") + +(defun org-compare-randomly--update-sort-key (entry table generator) + "Return sort key for ENTRY in TABLE, generating it if necessary. +For internal use by `org-compare-randomly-by'." + (let* ((marker (org-compare--get-marker entry)) + (hash-key `(,(marker-buffer marker) . ,(marker-position marker)))) + (or (gethash hash-key table) + (puthash hash-key (funcall generator entry) table)))) + +(defun org-compare-randomly-by (generator) + "Return a random comparator using GENERATOR. + +The comparator returned is like `org-compare-randomly', except +the distribution of random keys is controlled by GENERATOR and +may thus be non-uniform. + +The function GENERATOR is called with a single argument, an +agenda entry, when that entry lacks a sort key. It should return +a number, which is then used for all comparisons until the key +list is cleared; see `org-compare-randomly' for more details on +this. + +Subsequent calls to `org-compare-randomly-by' produce comparators +with independent sets of sort keys." + (let ((table (make-hash-table :test #'equal))) + (lambda (x y) + (when org-compare-random-refresh + (clrhash table) + (setq org-compare-random-refresh nil)) + (let ((x-val (org-compare-randomly--update-sort-key x table generator)) + (y-val (org-compare-randomly--update-sort-key y table generator))) + (cond + ((= x-val y-val) nil) + ((< x-val y-val) -1) + ((> x-val y-val) +1)))))) + +(defun org-compare-randomly () + "Return a comparator implementing a random shuffle. + +When given distinct agenda entries X and Y, the resulting +comparator has an equal chance of returning +1 and -1 (and a +miniscule chance of returning nil). Subsequent calls will produce +results consistent with a total ordering. + +To accomplish this, a hash table of randomly-generated sort keys +is maintained. This table will persist until the comparator is +called when the variable `org-compare-random-refresh' is non-nil. +This means that setting this variable as part of a custom agenda +command using this comparator as `org-agenda-cmp-user-defined' +will cause the sort order to change whenever the agenda is +refreshed; otherwise, it will persist until Emacs is restarted. + +Note that if you don't want the sort order to change on refresh, +you need to be careful that the comparator is created when the +custom agenda command is defined, not when it's called, e.g. + + (add-to-list + 'org-agenda-custom-commands + `(\"y\" \"Example Agenda\" + ((todo + \"\" + ((org-agenda-cmp-user-defined ',(org-compare-randomly)) + (org-agenda-sorting-strategy '(user-defined-up))))))) + +\(Notice the use of backquote.) + +Comparators resulting from different calls to this function have +independent key tables." + (org-compare-randomly-by (lambda (_) (random)))) +#+end_src + +Then add our custom command, one section for "TODO"s and another for top level "PROJ"s +#+begin_src emacs-lisp +(after! org + (add-to-list 'org-agenda-custom-commands + '("pr" "Random Project TODOs" + ((tags "proj/TODO" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))) + (tags "proj/STRT" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))) + (tags "proj/PROJ" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))) + (todo "IDEA" + ((org-agenda-max-entries 5) + (org-agenda-cmp-user-defined (org-compare-randomly)) + (org-compare-random-refresh t) + (org-agenda-sorting-strategy '(user-defined-up)))))))) +#+end_src +*** Don't dim blocked tasks +Not only does this feature have performance issues, its not useful with how I use org +#+begin_src emacs-lisp +(after! org + (setq org-agenda-dim-blocked-tasks nil)) +#+end_src +** Set tags alist +#+begin_src emacs-lisp +(after! org + (setq org-tag-alist '(("proj" . ?p)))) +#+end_src * General Modes ** Magit Further configuration for magit @@ -627,7 +757,10 @@ Setup the maildirs we want to see, we'll show our notifications (setq mu4e-maildir-shortcuts '((:maildir "/nathan@mccarty.io/Folders/Notifications/Github" :key ?h) (:maildir "/nathan@mccarty.io/Folders/Notifications/Gitlab" :key ?l) - (:maildir "/nathan@mccarty.io/Folders/Notifications/SourceHut" :key ?s)))) + (:maildir "/nathan@mccarty.io/Folders/Notifications/SourceHut" :key ?s) + (:maildir "/nathan@mccarty.io/Folders/Archival/Receipts/2022" :key ?r) + (:maildir "/nathan@mccarty.io/Folders/Job Search" :key ?j) + (:maildir "/nathan@mccarty.io/Folders/Archival/Informed Delivery" :key ?i)))) #+end_src Tell it to enable the modeline display #+begin_src emacs-lisp