Make the Emacs Diary work as an Outline (outline-minor-mode)
If you are new to the topic of the Emacs Diary and the Calendar, please refer to yesterday’s video introduction: https://protesilaos.com/codelog/2021-04-14-emacs-diary-calendar/.
One of the major upsides of the Emacs Diary is that it is a regular file
that you can navigate using whatever tools you like: Isearch and M-x
occur
, Swiper or consult-line
, and the like. It also is possible to
extend it so that it uses the foldable headings that are familiar to
users of Org mode; a feature that comes from outline-mode
. We can add
that to any major mode by means of outline-minor-mode
.
To make the outline work, we need to specify a pattern of what
constitutes a heading within the file. This is controlled by the
buffer-local variable outline-regexp
. The best candidate for headings
is the Diary’s own comments. It feels natural for users of Elisp to set
those to ;;
, so we have this:
(setq diary-comment-start ";;")
(setq diary-comment-end "")
Then we need to write the pattern for the headings. I prefer that
heading levels start with at least three semicolons and a space, which
lets us still use comments for their intended purpose (the same way it
is in elisp-mode
):
;; The pattern evaluates to ";;+\\{2,\\} [^ \t\n]"
(setq outline-regexp (format "%s+\\{2,\\} [^ \t\n]" diary-comment-start))
We can test our pattern in a new buffer with M-x re-builder
and some
text samples:
;; A regular comment. This should not be matched.
;;; A heading level 1
;;;; A heading level 2
;;;;; A heading level 3
Now that we got everything in order, we just need to integrate with with
diary-mode
:
(defun my-diary-extras-setup ()
"Additional setup for Diary mode buffers."
(when (derived-mode-p 'diary-mode)
(setq outline-regexp (format "%s+\\{2,\\} [^ \t\n]" diary-comment-start))))
(add-hook 'diary-mode-hook #'my-diary-extras-setup)
Next time we access the diary-file
and diary-mode
gets enabled, we
will get headings function the way we want. It then is up to you to
specify the key bindings you want to make navigating the outline easier.
For example:
(with-eval-after-load 'outline
(let ((map outline-minor-mode-map))
(define-key map (kbd "C-c C-n") 'outline-next-visible-heading)
(define-key map (kbd "C-c C-p") 'outline-previous-visible-heading)
(define-key map (kbd "C-c C-f") 'outline-forward-same-level)
(define-key map (kbd "C-c C-b") 'outline-backward-same-level)
(define-key map (kbd "C-c C-u") 'outline-up-heading)
(define-key map (kbd "C-c C-a") 'outline-show-all)
(define-key map (kbd "C-c C-v") 'outline-move-subtree-down)
(define-key map (kbd "C-c M-v") 'outline-move-subtree-up)
(define-key map (kbd "<C-tab>") 'outline-cycle))) ; This is from Emacs28
Finally we need to enable outline-minor-mode
:
(add-hook 'diary-mode-hook #'outline-minor-mode)
Or M-x outline-minor-mode
.
With those in place, we have set the foundations to make the Diary
buffer very easy to navigate, all while retaining its overall
simplicity. I am using Daniel Mendler’s consult
library, which
includes the consult-outline
command (among many others): it lets you
jump to a heading using minibuffer completion and requires no extra
setup the way the built-in imenu
does (though Imenu is also great in
its own right).
Personally, I also change the fontification of the Diary buffer, but will not bother you with the technicalities. Please refer to my dotemacs instead: https://protesilaos.com/emacs/dotemacs (specifically the section Calendar and Diary (and prot-diary.el)).