dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

config.org (146451B)


      1 * macOS installation info
      2 On macOS, I use Homebrew to install Emacs from daviderestivo/emacs-head/emacs-head@28.
      3 I install with ~--HEAD --with-dbus --with-cocoa --with-xwidgets --with-native-comp~.
      4 
      5 * Why I choose use-package
      6 - provides bind-key by default
      7 - is mostly just macros that wrap the needed stuff from package.el. can check that with ~macroexpand~.
      8 - adds a bunch of performance improvements
      9 * Let me know when we're at a certain version
     10 #+begin_src emacs-lisp
     11   (when (version<= "30" emacs-version)
     12     (message "Check out C-x w d to make windows dedicated."))
     13 #+end_src
     14 * Install from source
     15 Emacs 29 ships with a way to install packages from source, here's a small wrapper around it.
     16 
     17 #+begin_src emacs-lisp
     18   (cl-defun za/package-vc-install (&key (fetcher "github") repo name rev backend load)
     19     "Install a package from a remote if it's not already installed.
     20   This is a thin wrapper around `package-vc-install' in order to
     21   make non-interactive usage more ergonomic.  Takes the following
     22   named arguments:
     23 
     24   - FETCHER the remote where to get the package (e.g., \"gitlab\").
     25     If omitted, this defaults to \"github\".
     26 
     27   - REPO should be the name of the repository (e.g.,
     28   \"slotThe/arXiv-citation\".
     29 
     30   - NAME, REV, and BACKEND are as in `package-vc-install' (which
     31     see).
     32 
     33   - LOAD is optionally a subdirectory that should be added to `load-path'."
     34     (let* ((url (cond ((string-match-p (rx bos "http" (? ?s) "://") repo)
     35                        repo)
     36                       (t (format "https://www.%s.com/%s" fetcher repo))))
     37            (iname (when name (intern name)))
     38            (pac-name (or iname (intern (file-name-base repo))))
     39            (to-load (when load
     40                       (format "%s/%s"
     41                               (package-desc-dir (package-get-descriptor pac-name))
     42                               load))))
     43       (unless (package-installed-p pac-name)
     44         (package-vc-install url iname rev backend))
     45       (when load
     46         (unless (file-directory-p to-load)
     47           (user-error "Not a readable dir: %s" to-load))
     48         (add-to-list 'load-path to-load))
     49       (message "%s" pac-name)))
     50 #+end_src
     51 
     52 You can use this in use-package with an ~:init~ clause.
     53 
     54 * exec-path-from-shell (macOS)
     55 In macOS, the path is not set correctly (i.e. as it is in the terminal) in the GUI app. This fixes it.
     56 Not needed when using emacs-plus, because it has a custom patch for it. It also defines a [[https://github.com/d12frosted/homebrew-emacs-plus?tab=readme-ov-file#system-appearance-change][custom variable]] which hopefully should be enough to detect if we're running emacs-plus.
     57 
     58 #+begin_src emacs-lisp
     59   (when (and (string-equal system-type "darwin")
     60              (not (boundp 'ns-system-appearance-change-functions)))
     61     (use-package exec-path-from-shell
     62       :config
     63       (add-to-list 'exec-path-from-shell-variables "NOTMUCH_CONFIG")
     64       (exec-path-from-shell-initialize)))
     65 #+end_src
     66 
     67 * Emacs file locations
     68 ** Auto-Save files
     69 By default, auto-save files ("#file#") are placed in the same directory as the file itself.
     70 I want to put this all in some unified place:
     71 
     72 #+begin_src emacs-lisp
     73   (let ((saves-directory "~/.local/share/emacs/saves/"))
     74     (unless (file-directory-p saves-directory)
     75       (make-directory saves-directory))
     76     (setq auto-save-file-name-transforms
     77           `((".*" ,saves-directory t))))
     78 #+end_src
     79 
     80 ** Backup files
     81 By default, backup files (those with a tilde) are saved in the same directory as the currently edited file.
     82 This setting puts them in ~/.local/share/emacs/backups.
     83 
     84 #+begin_src emacs-lisp
     85   (let ((backups-directory "~/.local/share/emacs/backups"))
     86     (unless (file-directory-p backups-directory)
     87       (make-directory backups-directory))
     88     (setq backup-directory-alist `(("." . ,backups-directory)))
     89     (setq backup-by-copying t))
     90 #+end_src
     91 
     92 ** Custom settings file
     93 Both commands are necessary.
     94 First one tells Emacs where to save customizations.
     95 The second one actually loads them.
     96 
     97 #+begin_src emacs-lisp
     98   (setq custom-file (expand-file-name (concat user-emacs-directory "custom.el")))
     99   (load custom-file)
    100 #+end_src
    101 ** Delete by trash
    102 #+begin_src emacs-lisp
    103   (setq delete-by-moving-to-trash t)
    104   (unless (fboundp 'system-move-file-to-trash)
    105     (setq trash-directory "~/.Trash"))
    106 #+end_src
    107 * Daemon
    108 I want to have a way to kill the Emacs daemon.
    109 So, define a function that kills the frame, and with a prefix kills emacs.
    110 
    111 #+begin_src emacs-lisp
    112   (defun za/emacsclient-c-x-c-c (&optional arg)
    113     "If running in emacsclient, make C-x C-c exit frame, and C-u C-x C-c exit Emacs."
    114     (interactive "P") ; prefix arg in raw form
    115     (if arg
    116         (save-buffers-kill-emacs)
    117       (save-buffers-kill-terminal)))
    118 #+end_src
    119 
    120 Then, if I'm in an emacsclient, I want to bind C-x C-c to that function (if not, I just want the default keybinding):
    121 
    122 #+begin_src emacs-lisp
    123   ;; If not running in emacsclient, use the default bindings
    124   (if (daemonp)
    125       (bind-key "C-x C-c" #'za/emacsclient-c-x-c-c))
    126 #+end_src
    127 
    128 Furthermore, I want to set the theme correctly whenever I connect with 'emacsclient':
    129 
    130 #+begin_src emacs-lisp
    131   (if (daemonp)
    132       (add-hook 'after-make-frame-functions #'za/auto-select-theme))
    133 #+end_src
    134 * Sound support
    135 On macOS, you can use afplay:
    136 
    137 #+begin_src emacs-lisp
    138   (defun za/play-sound-file-macos (file &optional volume device)
    139     "Play sound using `afplay` on macOS"
    140     (unless (file-readable-p file)
    141       (user-error "File %s not readable." file))
    142 
    143     ;; the `apply` is required here because I need to build a list of arguments
    144     (apply 'start-process `("afplay" nil
    145                             "afplay"
    146                             ,@(if volume (list "-v" (int-to-string volume)))
    147                             ,file)))
    148 #+end_src
    149 
    150 Then redefine the play-sound-file function where needed:
    151 
    152 #+begin_src emacs-lisp
    153   (cond ((and (not (fboundp 'play-sound-internal))
    154               (eq system-type 'darwin))
    155          (advice-add 'play-sound-file :override #'za/play-sound-file-macos)))
    156 #+end_src
    157 * DISABLED Fix non-dbus macOS notification
    158 macOS version might not be compiled with dbus support; in that case you can use e.g. terminal-notifier.
    159 If you use the ~sender~ option, notifications don't show
    160 unless the app is in the background. [[https://github.com/julienXX/terminal-notifier/issues/68][See this Github issue.]]
    161 
    162 #+begin_src emacs-lisp :tangle no
    163   ;; on mac without dbus:
    164   (org-show-notification-handler
    165    (lambda (str) (start-process "terminal-notifier" nil (executable-find "terminal-notifier")
    166                                 "-title" "Timer done"
    167                                 "-message" str
    168                                 "-group" "org.gnu.Emacs"
    169                                 "-ignoreDnD"
    170                                 "-activate" "org.gnu.Emacs")))
    171 #+end_src
    172 * Custom notification functions
    173 #+begin_src emacs-lisp
    174   (defun za/notify (title message)
    175     "Show notification with TITLE and MESSAGE."
    176     (ignore-errors (require 'notifications))
    177     (cond ((fboundp 'ns-do-applescript)
    178            (ns-do-applescript
    179             (format "display notification \"%s\" with title \"%s\""
    180                     (replace-regexp-in-string "\"" "#" message)
    181                     (replace-regexp-in-string "\"" "#" title))))
    182           ((string= system-type "gnu/linux")
    183            (require 'notifications)
    184            (notifications-notify :title title :body message))
    185           (t (error "No notification handler defined!"))))
    186 
    187   (defun za/send-notification-interactivity-required (&rest _)
    188     "Notify that a function needs action."
    189     (za/notify "Interactivity required" "A function requires interactivity."))
    190 
    191   (defun za/notify-on-interactivity (func &rest r)
    192     "Send a notification whenever FUNC requires interactivity.
    193   Used as :around advice, calling FUNC with arguments R."
    194     (advice-add #'y-or-n-p :before #'za/send-notification-interactivity-required)
    195     (advice-add #'yes-or-no-p :before #'za/send-notification-interactivity-required)
    196     (advice-add #'user-error :before #'za/send-notification-interactivity-required)
    197     (with-demoted-errors "Error in %s" (apply func r))
    198     (advice-remove #'y-or-n-p #'za/send-notification-interactivity-required)
    199     (advice-remove #'yes-or-no-p #'za/send-notification-interactivity-required)
    200     (advice-remove #'user-error #'za/send-notification-interactivity-required))
    201 #+end_src
    202 
    203 * Editing
    204 ** Overwrite selection on typing
    205 Normally, when I select something and start typing, Emacs clears the selection, i.e. it deselects and inserts text after the cursor.
    206 I want to replace the selection.
    207 
    208 #+begin_src emacs-lisp
    209   (delete-selection-mode t)
    210 #+end_src
    211 
    212 ** Strip trailing whitespace
    213 You can show trailing whitespace by setting show-trailing-whitespace to 't'.
    214 But I want to automatically strip trailing whitespace.
    215 Luckily there's already a function for that, I just need to call it in a hook:
    216 
    217 #+begin_src emacs-lisp
    218   (add-hook 'before-save-hook #'delete-trailing-whitespace)
    219 #+end_src
    220 
    221 ** Formatting & indentation
    222 
    223 Show a tab as 8 spaces:
    224 
    225 #+begin_src emacs-lisp
    226   (setq-default tab-width 8)
    227 #+end_src
    228 
    229 Never insert tabs with indentation by default:
    230 
    231 #+begin_src emacs-lisp
    232   (setq-default indent-tabs-mode nil)
    233 #+end_src
    234 
    235 Allow switching between the two easily:
    236 
    237 #+begin_src emacs-lisp
    238   (defun indent-tabs ()
    239     (interactive)
    240     (setq indent-tabs-mode t))
    241   (defun indent-spaces ()
    242     (interactive)
    243     (setq indent-tabs-mode nil))
    244 #+end_src
    245 
    246 Indentation for various modes:
    247 
    248 #+begin_src emacs-lisp
    249   (setq-default sh-basic-offset 2
    250                 c-basic-offset 4)
    251 #+end_src
    252 
    253 ** Wrapping
    254 A function to toggle wrapping:
    255 
    256 #+begin_src emacs-lisp
    257   (defvar-local za/wrapping nil "Wrapping changes per buffer.")
    258 
    259   (defun za/toggle-wrap (&optional enable)
    260     "Toggle line wrapping settings. With ENABLE a positive number, enable wrapping. If ENABLE is negative or zero, disable wrapping."
    261     (interactive "P") ; prefix arg in raw form
    262 
    263     ;; If an argument is provided, prefix or otherwise
    264     (if enable
    265         (let ((enable (cond ((numberp enable)
    266                              enable)
    267                             ((booleanp enable)
    268                              (if enable 1 0))
    269                             ((or (listp enable) (string= "-" enable))
    270                              (prefix-numeric-value enable)))))
    271           ;; If zero or negative, we want to disable wrapping, so pretend it's currently enabled.
    272           ;; And vice versa.
    273           (cond ((<= enable 0) (setq za/wrapping t))
    274                 ((> enable 0) (setq za/wrapping nil)))))
    275 
    276 
    277     (let ((disable-wrapping (lambda ()
    278                               (visual-line-mode -1)
    279                               (toggle-truncate-lines t)))
    280           (enable-wrapping (lambda ()
    281                              (toggle-truncate-lines -1)
    282                              (visual-line-mode))))
    283 
    284       ;; If za/wrapping is not locally set, infer its values from the enabled modes
    285       (unless (boundp 'za/wrapping)
    286         (setq za/wrapping (and visual-line-mode
    287                                (not truncate-lines))))
    288 
    289       ;; Toggle wrapping based on current value
    290       (cond (za/wrapping
    291              (funcall disable-wrapping)
    292              (setq za/wrapping nil)
    293              (message "Wrapping disabled."))
    294             (t
    295              (funcall enable-wrapping)
    296              (setq za/wrapping t)
    297              (message "Wrapping enabled.")))))
    298 #+end_src
    299 
    300 And a keybinding to toggle wrapping:
    301 
    302 #+begin_src emacs-lisp
    303   (bind-key "C-c q w" #'za/toggle-wrap)
    304 #+end_src
    305 
    306 ** Pager toggle
    307 M-x view-mode enables pager behavior.
    308 I want read-only files to automatically use pager mode:
    309 
    310 #+begin_src emacs-lisp
    311   (setq view-read-only t)
    312 #+end_src
    313 ** Prefer newer file loading
    314 #+begin_src emacs-lisp
    315   (setq load-prefer-newer t)
    316 #+end_src
    317 
    318 ** Automatically find tags file
    319 When opening a file in a git repo, try to discover the etags file:
    320 
    321 #+begin_src emacs-lisp
    322   (defun current-tags-file ()
    323     "Get current tags file"
    324     (let* ((tagspath ".git/etags")
    325            (git-root (locate-dominating-file (buffer-file-name) tagspath)))
    326       (if git-root
    327           (expand-file-name tagspath git-root))))
    328 
    329   (setq default-tags-table-function #'current-tags-file)
    330 #+end_src
    331 
    332 There's probably a better way to write this. I need to ask Reddit for feedback at some point.
    333 
    334 ** End sentences with one space
    335 Emacs uses the rather old-fashioned convention of treating a period followed by double spaces as end of sentence. However, it is more common these days to end sentences with a period followed by a single space.
    336 
    337 Let a period followed by a single space be treated as end of sentence:
    338 
    339 #+begin_src emacs-lisp
    340   (setq sentence-end-double-space nil)
    341 #+end_src
    342 * Keybindings
    343 ** Expansion/completion
    344 Use hippie expand instead of dabbrev-expand:
    345 
    346 #+begin_src emacs-lisp
    347   (bind-key "M-/" #'hippie-expand)
    348 #+end_src
    349 
    350 ** Zap up to char
    351 It's more useful for me to be able to delete up to a character instead of to and including a character:
    352 
    353 #+begin_src emacs-lisp
    354   (defun za/zap-up-to-char-icase ()
    355     "Ignore case for zap-up-to-char"
    356     (interactive)
    357     (let ((case-fold-search nil))
    358       (call-interactively #'zap-up-to-char)))
    359   (bind-key "M-z" #'za/zap-up-to-char-icase)
    360 #+end_src
    361 
    362 ** Forward-word and forward-to-word
    363 Change M-f to stop at the start of the word:
    364 
    365 #+begin_src emacs-lisp
    366   (bind-key "M-f" #'forward-to-word)
    367 #+end_src
    368 
    369 Bind ESC M-f to the old functionality of M-f (stop at end of word)
    370 
    371 #+begin_src emacs-lisp
    372   (bind-key "ESC M-f" #'forward-word)
    373 #+end_src
    374 
    375 ** Rectangle insert string
    376 #+begin_src emacs-lisp
    377   (bind-key "C-x r I" #'string-insert-rectangle)
    378   (bind-key "C-x r R" #'replace-rectangle)
    379 #+end_src
    380 
    381 ** Toggle auto-revert-mode
    382 Sometimes I want to toggle auto reverting (or autoread) of buffer:
    383 
    384 #+begin_src emacs-lisp
    385   (bind-key "C-c q a" #'auto-revert-mode)
    386 #+end_src
    387 ** Fast access to view-mode (pager)
    388 I want to bind view-mode to a key for easy access:
    389 
    390 #+begin_src emacs-lisp
    391   (bind-key "C-c q r" 'view-mode)
    392 #+end_src
    393 
    394 ** Kill this buffer
    395 I like to be able to kill a buffer instantly:
    396 
    397 #+begin_src emacs-lisp
    398   (bind-key "s-<backspace>" 'kill-current-buffer)
    399 #+end_src
    400 
    401 ** Delete this file (and kill the buffer)
    402 #+begin_src emacs-lisp
    403   (defun za/delete-this-file ()
    404     "Kill the current buffer and delete its associated file."
    405     (interactive)
    406     (let ((fname (buffer-file-name))
    407           (buf (current-buffer)))
    408       (unless (and fname (file-exists-p fname))
    409         (user-error "Buffer has no associated file."))
    410 
    411       (unless (yes-or-no-p (format "Really delete %s and its buffer?" fname))
    412         (user-error "User cancelled."))
    413 
    414       (delete-file fname 'trash-if-enabled)
    415       (kill-buffer buf)
    416       (message "Deleted %s and killed its buffer." fname)))
    417 
    418   (bind-key "C-c s-<backspace>" #'za/delete-this-file)
    419 #+end_src
    420 
    421 ** Toggle fullscreen
    422 I'll use the keybinding that's standard on macOS:
    423 
    424 #+begin_src emacs-lisp
    425   (bind-key "C-s-f" #'toggle-frame-fullscreen)
    426 #+end_src
    427 
    428 ** Sexp manipulation
    429 When I write lisp, sometimes I want to switch two sexps (e.g. ~(one) (two)~ → ~(two) (one)~), so a key binding is nice for that:
    430 
    431 #+begin_src emacs-lisp
    432   (bind-key "C-S-t" #'transpose-sexps)
    433 #+end_src
    434 
    435 Also, to raise a sexp (e.g. ~(one (two))~ → ~(two)~):
    436 
    437 #+begin_src emacs-lisp
    438   (bind-key "C-S-u" #'raise-sexp)
    439 #+end_src
    440 
    441 ** Dedicated windows
    442 Sometimes I want to avoid Emacs overriding a window's contents.
    443 So I create a keybinding to toggle dedicated on a window:
    444 
    445 #+begin_src emacs-lisp
    446   (defun za/toggle-window-dedicated-p ()
    447     "Toggle set-window-dedicated-p on current window"
    448     (interactive)
    449     (cond ((window-dedicated-p (selected-window))
    450            (set-window-dedicated-p (selected-window) nil)
    451            (message "Window no longer dedicated"))
    452           (t
    453            (set-window-dedicated-p (selected-window) t)
    454            (message "Window marked as dedicated"))))
    455 
    456   (bind-key "C-x 9" #'za/toggle-window-dedicated-p)
    457 
    458 #+end_src
    459 
    460 ** Rotate windows horizontal ↔ vertical
    461 #+begin_src emacs-lisp
    462   (defun za/rotate-windows ()
    463     (interactive)
    464     (if (= (count-windows) 2)
    465         (let* ((this-win-buffer (window-buffer))
    466                (next-win-buffer (window-buffer (next-window)))
    467                (this-win-edges (window-edges (selected-window)))
    468                (next-win-edges (window-edges (next-window)))
    469                (this-win-2nd (not (and (<= (car this-win-edges)
    470                                            (car next-win-edges))
    471                                        (<= (cadr this-win-edges)
    472                                            (cadr next-win-edges)))))
    473                (splitter
    474                 (if (= (car this-win-edges)
    475                        (car (window-edges (next-window))))
    476                     'split-window-horizontally
    477                   'split-window-vertically)))
    478           (delete-other-windows)
    479           (let ((first-win (selected-window)))
    480             (funcall splitter)
    481             (if this-win-2nd (other-window 1))
    482             (set-window-buffer (selected-window) this-win-buffer)
    483             (set-window-buffer (next-window) next-win-buffer)
    484             (select-window first-win)
    485             (if this-win-2nd (other-window 1))))))
    486 #+end_src
    487 
    488 #+begin_src emacs-lisp
    489   (bind-key "C-x 7" #'za/rotate-windows)
    490 #+end_src
    491 
    492 ** Open line like in Vim
    493 I prefer to open-line the way o/O works in Vim:
    494 
    495 #+begin_src emacs-lisp
    496   ;; Autoindent open-*-lines
    497   (defvar za/open-line-newline-and-indent t
    498     "Modify the behavior of the open-*-line functions to cause them to autoindent.")
    499 
    500   (defun za/open-line (prefix)
    501     "Open line like `o`/`O` in Vim. Negative prefix for line above, positive for below."
    502     (interactive "p")
    503     (cond ((< prefix 0)
    504            (beginning-of-line)
    505            (open-line (abs prefix)))
    506           (t
    507            (end-of-line)
    508            (open-line prefix)
    509            (forward-line 1)))
    510     (when za/open-line-newline-and-indent
    511       (indent-according-to-mode)))
    512 
    513   (defun za/open-line-keep-point (prefix)
    514     "Open line like `o`/`O` in Vim but don't move point. Negative prefix for line above, positive for below."
    515     (interactive "p")
    516     (save-mark-and-excursion (za/open-line prefix)))
    517 #+end_src
    518 
    519 And keybindings:
    520 
    521 #+begin_src emacs-lisp
    522   (bind-key "C-o" #'za/open-line)
    523   (bind-key "C-M-o" #'za/open-line-keep-point)
    524 #+end_src
    525 
    526 ** Unfill region/paragraph
    527 Taken from here: https://www.emacswiki.org/emacs/UnfillParagraph
    528 
    529 #+begin_src emacs-lisp
    530   (defun za/unfill-paragraph (&optional region)
    531     "Takes a multi-line paragraph and makes it into a single line of text."
    532     (interactive (progn (barf-if-buffer-read-only) '(t)))
    533     (let ((fill-column (point-max))
    534           ;; This would override `fill-column' if it's an integer.
    535           (emacs-lisp-docstring-fill-column t))
    536       (fill-paragraph nil region)))
    537 
    538   (bind-key "M-Q" #'za/unfill-paragraph)
    539 #+end_src
    540 ** Easily edit my config
    541 Bind a keyboard shortcut to open my config.
    542 The "(interactive)" means that it can be called from a keybinding or from M-x.
    543 
    544 #+begin_src emacs-lisp
    545   (defun za/edit-config-org ()
    546     "Edit my config.org file"
    547     (interactive)
    548     (find-file (expand-file-name "config.org" user-emacs-directory)))
    549 #+end_src
    550 
    551 #+begin_src emacs-lisp
    552   (bind-key "C-c E" 'za/edit-config-org)
    553 #+end_src
    554 ** Visible mode
    555 #+begin_src emacs-lisp
    556   (bind-key (kbd "C-c q v") #'visible-mode)
    557 #+end_src
    558 ** Clone buffer indirectly by default
    559 #+begin_src emacs-lisp
    560   (bind-key (kbd "C-x x n") #'clone-indirect-buffer)
    561 #+end_src
    562 * Custom functions
    563 ** Make region readonly or writable
    564 #+begin_src emacs-lisp
    565   (defun za/set-region-read-only (begin end)
    566     "Sets the read-only text property on the marked region.
    567   Use `set-region-writeable' to remove this property."
    568     ;; See https://stackoverflow.com/questions/7410125
    569     (interactive "r")
    570     (with-silent-modifications
    571       (put-text-property begin end 'read-only t)))
    572 
    573   (defun za/set-region-writeable (begin end)
    574     "Removes the read-only text property from the marked region.
    575   Use `set-region-read-only' to set this property."
    576     ;; See https://stackoverflow.com/questions/7410125
    577     (interactive "r")
    578     (with-silent-modifications
    579       (remove-text-properties begin end '(read-only t))))
    580 #+end_src
    581 ** Insert macro as Lisp
    582 From here: https://www.masteringemacs.org/article/keyboard-macros-are-misunderstood
    583 
    584 #+begin_src emacs-lisp
    585   (use-package kmacro
    586     :ensure nil ; included with Emacs
    587     :bind (:map kmacro-keymap
    588                 ("I" . kmacro-insert-macro))
    589     :config
    590     (defalias 'kmacro-insert-macro 'insert-kbd-macro)
    591 
    592     ;; Add advice to ignore errors on `kmacro-keyboard-macro-p`, it was
    593     ;; messing up because of some entry in `obarray`
    594     (advice-add #'kmacro-keyboard-macro-p :around (lambda (fun sym) "Ignore errors." (ignore-errors (funcall fun sym)))))
    595 #+end_src
    596 ** Show local help at point when idling
    597 #+begin_src emacs-lisp
    598   (defun za/echo-area-tooltips ()
    599     "Show tooltips in the echo area automatically for current buffer."
    600     (setq-local help-at-pt-display-when-idle t
    601                 help-at-pt-timer-delay 0)
    602     (help-at-pt-cancel-timer)
    603     (help-at-pt-set-timer))
    604 #+end_src
    605 
    606 ** Info manual functions
    607 For some reason, these things don't show up in the index:
    608 
    609 #+begin_src emacs-lisp
    610   (defun elisp-info (&optional node)
    611     "Read documentation for Elisp in the info system.
    612   With optional NODE, go directly to that node."
    613     (interactive)
    614     (info (format "(elisp)%s" (or node ""))))
    615 #+end_src
    616 
    617 Though I can also just use ~info-display-manual~.
    618 
    619 ** Radio
    620 Just a wrapper function to my radio script:
    621 
    622 #+begin_src emacs-lisp
    623   (defun radio ()
    624     "Play an internet radio"
    625     (interactive)
    626     (ansi-term "radio" "*radio*"))
    627 #+end_src
    628 
    629 ** no-op
    630 #+begin_src emacs-lisp
    631   (defun za/no-op (&rest args))
    632 #+end_src
    633 
    634 ** Syncthing
    635 Some functions to start/stop syncthing.
    636 #+begin_src emacs-lisp
    637   (defconst za/st-buffer-name "*syncthing*" "Buffer name for the syncthing process.")
    638   (defun za/st ()
    639     "Start syncthing"
    640     (interactive)
    641     (if (get-buffer-process za/st-buffer-name)
    642         (user-error "Syncthing is already running."))
    643     (async-shell-command "syncthing serve --no-browser" za/st-buffer-name))
    644 
    645   (defun za/st-kill ()
    646     "Stop syncthing"
    647     (interactive)
    648     (unless (get-buffer-process za/st-buffer-name)
    649       (user-error "Syncthing is not running."))
    650     (async-shell-command "syncthing cli operations shutdown"))
    651 #+end_src
    652 ** Replace typographic quotes
    653 #+begin_src emacs-lisp
    654   (defun za/replace-typographic-quotes ()
    655     "Replace typographic quotes with plain quotes"
    656     (interactive)
    657     (save-mark-and-excursion
    658       (goto-char (point-min))
    659       (while (re-search-forward (rx (any ?“ ?”)) nil 'noerror)
    660         (replace-match "\""))
    661       (goto-char (point-min))
    662       (while (re-search-forward (rx (any "‘" "’")) nil 'noerror)
    663         (replace-match "'"))))
    664 #+end_src
    665 ** Distraction-free on current buffer
    666 #+begin_src emacs-lisp
    667   (defun za/buffer-focus-no-distractions ()
    668     "Focus on this buffer"
    669     (interactive)
    670     (cond ((or (not (boundp 'za/no-distractions))
    671                (not za/no-distractions))
    672            (olivetti-mode 1)
    673            (line-number-mode 0)
    674            (display-line-numbers-mode 0)
    675            (window-configuration-to-register ?w)
    676            (delete-other-windows)
    677            (setq-local za/tmp/mode-line-format mode-line-format)
    678            (setq-local mode-line-format nil)
    679            (setq-local za/tmp/internal-border-width (frame-parameter nil 'internal-border-width))
    680            (set-frame-parameter nil 'internal-border-width 20)
    681            (setq-local za/no-distractions t)
    682            (message "Window configuration stored in register W"))
    683           (za/no-distractions
    684            (set-frame-parameter nil 'internal-border-width za/tmp/internal-border-width)
    685            (line-number-mode 0)
    686            (display-line-numbers-mode 1)
    687            (setq-local mode-line-format za/tmp/mode-line-format)
    688            (jump-to-register ?w)
    689            (olivetti-mode 0)
    690            (setq-local za/no-distractions nil))))
    691 #+end_src
    692 * Interface
    693 ** Theme
    694 Icons required for some parts of the doom theme:
    695 
    696 #+begin_src emacs-lisp
    697   (use-package all-the-icons)
    698 #+end_src
    699 
    700 Load Doom Emacs themes:
    701 
    702 #+begin_src emacs-lisp
    703   (use-package doom-themes
    704     :config
    705     ;; Global settings (defaults)
    706     (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
    707           doom-themes-enable-italic t) ; if nil, italics is universally disabled
    708 
    709     ;; Enable flashing mode-line on errors
    710     (doom-themes-visual-bell-config)
    711 
    712     ;; Corrects (and improves) org-mode's native fontification.
    713     (doom-themes-org-config))
    714 #+end_src
    715 
    716 Define the themes I want:
    717 
    718 #+begin_src emacs-lisp
    719   (defconst za/dark-theme-name 'doom-one "A symbol representing the name of the dark theme I use.")
    720   (defconst za/light-theme-name 'jokull "A symbol representing the name of the light theme I use.")
    721   ;; I used to use doom-acario-light before writing my own theme
    722 
    723   (defun za/dark-theme ()
    724     "Switch to dark theme"
    725     (interactive)
    726     (mapc #'disable-theme custom-enabled-themes)
    727     (load-theme za/dark-theme-name t)
    728     (add-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode))
    729 
    730   (defun za/light-theme ()
    731     "Switch to light theme"
    732     (interactive)
    733     (mapc #'disable-theme custom-enabled-themes)
    734     (load-theme za/light-theme-name t)
    735     (remove-hook 'pdf-view-mode-hook #'pdf-view-midnight-minor-mode))
    736 #+end_src
    737 
    738 Change theme depending on the current system theme.
    739 The way I check for dark mode is defined in 'dark-mode-p'; currently I use the presence of the ~/.config/dark-theme file to indicate when dark theme is set.
    740 I quote the call to ~file-exists-p~ because I want to evaluate it on-demand, not immediately.
    741 A function ending in '-p' is a predicate, i.e. returns true or false.
    742 If calling a function that's in a variable, you have to use 'funcall'.
    743 To evaluate a quoted form, use 'eval'.
    744 
    745 #+begin_src emacs-lisp
    746   (defun za/auto-select-theme (&rest _)
    747     "Automatically select dark/light theme based on presence of ~/.config/dark-theme"
    748     (let ((dark-mode-p '(file-exists-p "~/.config/dark-theme")))
    749       (if (eval dark-mode-p)
    750           (za/dark-theme)
    751         (za/light-theme))))
    752 
    753   (za/auto-select-theme)
    754 #+end_src
    755 
    756 ** Font
    757 I want Menlo, size 12:
    758 
    759 #+begin_src emacs-lisp
    760   (add-to-list 'default-frame-alist '(font . "Menlo-13"))
    761   (custom-set-faces
    762    ; height = pt * 10
    763    '(fixed-pitch ((t (:family "Menlo" :height 130))))
    764    '(variable-pitch ((t (:family "ETBembo" :height 140))))
    765    '(org-block ((t (:inherit fixed-pitch))))
    766    '(org-table ((t (:foreground "#0087af" :inherit fixed-pitch))))
    767    '(org-indent ((t (:inherit (org-hide fixed-pitch))))))
    768 
    769   (set-face-font 'fixed-pitch "Menlo-13")
    770   (set-face-font 'variable-pitch "ETBembo-14")
    771 #+end_src
    772 
    773 I like nicer list bullets:
    774 
    775 #+begin_src emacs-lisp
    776   (font-lock-add-keywords
    777    'org-mode
    778    `((,(rx bol (* blank) (group ?-) " ")  ; list regexp
    779       1                                   ; first match
    780       '(face nil display "•"))))          ; replace with bullet point, keep same face
    781 #+end_src
    782 ** Cursor
    783 The default box cursor isn't really accurate, because the cursor is actually between letters, not on a letter.
    784 So, I want a bar instead of a box:
    785 
    786 #+begin_src emacs-lisp
    787   (setq-default cursor-type '(bar . 4)
    788                 cursor-in-non-selected-windows 'hollow)
    789 #+end_src
    790 
    791 (I use ~setq-default~ here because cursor-type is automatically buffer-local when it's set)
    792 
    793 ** Matching parentheses
    794 Don't add a delay to show matching parenthesis.
    795 Must come before show-paren-mode enable.
    796 
    797 #+begin_src emacs-lisp
    798   (setq show-paren-delay 0)
    799 #+end_src
    800 
    801 Show matching parentheses:
    802 
    803 #+begin_src emacs-lisp
    804   (show-paren-mode t)
    805 #+end_src
    806 ** Line numbers
    807 Relative line numbers:
    808 
    809 #+begin_src emacs-lisp
    810   (setq display-line-numbers-type 'relative)
    811   (global-display-line-numbers-mode)
    812 #+end_src
    813 
    814 Function to hide them:
    815 
    816 #+begin_src emacs-lisp
    817   (defun za/hide-line-numbers ()
    818     "Hide line numbers"
    819     (display-line-numbers-mode 0))
    820 #+end_src
    821 Don't display them in specific modes.  For each of the modes in
    822 'mode-hooks', add a function to hide line numbers when the mode
    823 activates (which triggers the 'mode'-hook).
    824 
    825 #+begin_src emacs-lisp
    826   (let ((mode-hooks '(doc-view-mode-hook vterm-mode-hook mpc-status-mode-hook mpc-tagbrowser-mode-hook)))
    827     (mapc
    828      (lambda (mode-name)
    829        (add-hook mode-name #'za/hide-line-numbers))
    830      mode-hooks))
    831 #+end_src
    832 ** Modeline
    833 I want to show the time and date in the modeline:
    834 
    835 #+begin_src emacs-lisp
    836   (setq display-time-day-and-date t           ; also the date
    837         display-time-default-load-average nil ; don't show load average
    838         display-time-format "%I:%M%p %e %b (%a)")   ; "HR:MIN(AM/PM) day-of-month Month (Day)"
    839   (display-time-mode 1)                  ; enable time mode
    840 #+end_src
    841 
    842 And to set the modeline format:
    843 
    844 #+begin_src emacs-lisp
    845   (setq-default mode-line-format '("%e" mode-line-front-space mode-line-mule-info mode-line-client mode-line-modified mode-line-remote mode-line-frame-identification mode-line-buffer-identification "   " mode-line-position
    846                                    (vc-mode vc-mode)
    847                                    "  " mode-line-modes mode-line-misc-info mode-line-end-spaces))
    848 #+end_src
    849 
    850 I want to hide certain modes from the modeline.
    851 For that, ~delight~ is a useful package; unlike ~diminish~, it can also change the display of /major/ modes (~diminish~ only does minor modes).
    852 
    853 #+begin_src emacs-lisp
    854     (use-package delight
    855       :config
    856       (delight 'visual-line-mode " ↩" 'simple)
    857       (delight 'auto-revert-mode " AR" 'autorevert)
    858       (delight 'abbrev-mode " Abv" 'abbrev))
    859 #+end_src
    860 ** Transparent title bar
    861 #+begin_src emacs-lisp
    862   (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
    863 #+end_src
    864 ** Tab bar
    865 Only show tab bar if there's more than 1 tab:
    866 
    867 #+begin_src emacs-lisp
    868   (setq tab-bar-show 1)
    869 #+end_src
    870 ** Buffer displaying
    871 
    872 So, this is a bit hard to grok. But basically the alist contains a
    873 regular expression to match a buffer name, then a list of functions to
    874 use in order for displaying the list, and then options for those functions (each of which is an alist).
    875 
    876 #+begin_src emacs-lisp
    877   (setq
    878    ;; Maximum number of side-windows to create on (left top right bottom)
    879    window-sides-slots '(0   ;; left
    880                         1   ;; top
    881                         3   ;; right
    882                         1 ) ;; bottom
    883 
    884    display-buffer-alist `(
    885                           ;; Right side
    886                           (,(rx (or "*Help*" (seq "*helpful " (* anything) "*")))
    887                            (display-buffer-reuse-window display-buffer-in-side-window)
    888                            (side . right)
    889                            (slot . -1)
    890                            (inhibit-same-window . t))
    891                           (,(rx "*Async Shell " (* anything) "*")
    892                            (display-buffer-reuse-window display-buffer-in-side-window)
    893                            (side . right)
    894                            (slot . 0)
    895                            (inhibit-same-window . t))
    896                           (,(rx "magit-process: " (* anything))
    897                            (display-buffer-reuse-window display-buffer-in-side-window)
    898                            (side . right)
    899                            (slot . 0)
    900                            (inhibit-same-window . t))
    901 
    902                           ;; Top side
    903                           (,(rx "*Info*")
    904                            (display-buffer-reuse-window display-buffer-in-side-window)
    905                            (side . top)
    906                            (slot . 0))
    907                           (,(rx "*Man " (* anything) "*")
    908                            (display-buffer-reuse-window display-buffer-in-side-window)
    909                            (side . top)
    910                            (slot . 0))
    911 
    912                           ;; Bottom
    913                           (,(rx "*Flycheck errors*")
    914                            (display-buffer-reuse-window display-buffer-in-side-window)
    915                            (side . bottom)
    916                            (slot . 0))))
    917 #+end_src
    918 
    919 And a way to toggle those side windows:
    920 
    921 #+begin_src emacs-lisp
    922   (bind-key "C-c W" #'window-toggle-side-windows)
    923 #+end_src
    924 
    925 ** Eldoc
    926 When editing Elisp and other supported major-modes, Eldoc will display useful information about the construct at point in the echo area.
    927 
    928 #+begin_src emacs-lisp
    929   (use-package eldoc
    930     :ensure nil ; installed with Emacs
    931     :delight
    932     :config
    933     (global-eldoc-mode 1))
    934 #+end_src
    935 
    936 ** Pulse line
    937 When you switch windows, Emacs can flash the cursor briefly to guide your eyes; I like that.
    938 Set some options for pulsing:
    939 
    940 #+begin_src emacs-lisp
    941   (setq pulse-iterations 10)
    942   (setq pulse-delay 0.05)
    943 #+end_src
    944 
    945 Define the pulse function:
    946 
    947 #+begin_src emacs-lisp
    948   (defun pulse-line (&rest _)
    949     "Pulse the current line."
    950     (pulse-momentary-highlight-one-line (point)))
    951 #+end_src
    952 
    953 Run it in certain cases: scrolling up/down, recentering, switching windows.
    954 'dolist' binds 'command' to each value in the list in turn, and runs the body.
    955 'advice-add' makes the pulse-line function run after 'command'.
    956 
    957 #+begin_src emacs-lisp
    958   (dolist (command '(scroll-up-command scroll-down-command recenter-top-bottom other-window))
    959     (advice-add command :after #'pulse-line))
    960 #+end_src
    961 
    962 And set the pulse color:
    963 
    964 #+begin_src emacs-lisp
    965   (custom-set-faces '(pulse-highlight-start-face ((t (:background "CadetBlue2")))))
    966 #+end_src
    967 
    968 ** Enable all commands
    969 By default, Emacs disables some commands.
    970 I want to have these enabled so I don't get a prompt whenever I try to use a disabled command.
    971 
    972 #+begin_src emacs-lisp
    973   (setq disabled-command-function nil)
    974 #+end_src
    975 ** More extensive apropos
    976 #+begin_src emacs-lisp
    977   (setq apropos-do-all t)
    978 #+end_src
    979 ** Enable recursive minibuffers
    980 #+begin_src emacs-lisp
    981   (setq enable-recursive-minibuffers t
    982         minibuffer-depth-indicate-mode t)
    983 #+end_src
    984 ** View webp and other formats
    985 Emacs handles common image formats internally, but for stuff like webp, you need an external converter:
    986 
    987 #+begin_src emacs-lisp
    988   (setq image-use-external-converter t)
    989 #+end_src
    990 
    991 You also need imagemagick installed.
    992 
    993 ** Repeat mode: easy repeating of commands
    994 #+begin_src emacs-lisp
    995   (repeat-mode 1)
    996 #+end_src
    997 
    998 ** Messages
    999 Hide some messages I don't need.
   1000 
   1001 #+begin_src emacs-lisp
   1002   (recentf-mode)
   1003   (setq inhibit-startup-message t)
   1004 #+end_src
   1005 
   1006 ** Start buffer (dashboard)
   1007 #+begin_src emacs-lisp
   1008   (use-package dashboard
   1009     :custom
   1010     (dashboard-startup-banner 'logo)
   1011     (dashboard-items '((gtd-inbox-counts . 3)
   1012                        (syncthing-status . 3)
   1013                        (recents . 5)
   1014                        (bookmarks . 5)))
   1015 
   1016 
   1017     :bind (:map dashboard-mode-map
   1018                 ("ss" . za/st)
   1019                 ("sk" . za/st-kill)
   1020                 ("J" . org-clock-goto))
   1021     :config
   1022     (add-to-list 'dashboard-item-generators '(gtd-inbox-counts . dashboard-insert-gtd-inbox-counts))
   1023     (add-to-list 'dashboard-item-generators '(syncthing-status . dashboard-insert-syncthing-status))
   1024 
   1025     (defun za/quotes-from-my-site ()
   1026       (let* ((quotes-file (concat za/my-website-dir "content/quotes.md"))
   1027              ;; Reformat quotes for display in dashboard
   1028              (file-contents (with-temp-buffer
   1029                               (insert-file-contents quotes-file)
   1030                               (re-search-forward (rx bol "> "))
   1031                               (delete-region (point-min) (pos-bol))
   1032                               (goto-char (point-min))
   1033                               (save-excursion (replace-regexp (rx bol ">" (* " ") (? "\n")) ""))
   1034                               (save-excursion (replace-regexp (rx eol "\n") "  "))
   1035                               (buffer-substring-no-properties (point-min) (point-max)))))
   1036         ;; Split file into individual quotes
   1037         (split-string file-contents "  ---  ")))
   1038 
   1039     ;; Use my saved quotes in the dashboard (https://alex.balgavy.eu/quotes/)
   1040     (if (boundp 'za/my-website-dir)
   1041         (setq dashboard-footer-messages
   1042               (let* ((quotes (za/quotes-from-my-site)))
   1043                 ;; Run each quote through fill-region for better display
   1044                 (require 's)
   1045                 (mapcar (lambda (quote-line)
   1046                           (with-temp-buffer
   1047                             (insert (s-trim quote-line))
   1048                             (fill-region (point-min) (point-max))
   1049                             (buffer-substring-no-properties (point-min) (point-max))))
   1050                         quotes)))
   1051       (warn "za/my-website-dir not bound, not setting custom dashboard messages")))
   1052 
   1053   (defun dashboard-insert-gtd-inbox-counts (list-size)
   1054     (require 'org-roam)
   1055     (let* ((lines-inbox (za/org-count-headlines-in-file 1 za/org-life-inbox))
   1056            (lines-mobile (if (boundp 'za/org-life-inbox-mobile) (za/org-count-headlines-in-file 1 za/org-life-inbox-mobile) 0))
   1057            (count-docs (length (directory-files za/org-life-doc-inbox nil (rx bos (not ?.)))))
   1058            (item-list))
   1059 
   1060       (when (> lines-inbox 0)
   1061         (push (list :name "Inbox" :count lines-inbox :file za/org-life-inbox) item-list))
   1062       (when (> lines-mobile 0)
   1063         (push (list :name "Mobile" :count lines-mobile :file za/org-life-inbox-mobile) item-list))
   1064       (when (> count-docs 0)
   1065         (push (list :name "Docs" :count count-docs :file za/org-life-doc-inbox) item-list))
   1066 
   1067       (dashboard-insert-section
   1068        ;; Widget title
   1069        "GTD:"
   1070        ;; list generated for dashboard
   1071        item-list
   1072        list-size
   1073        'gtd
   1074        "t"
   1075        ;; decide what to do when clicked ("el" is automatically assigned)
   1076        `(lambda (&rest _)
   1077           (message "%s" (find-file (plist-get ',el :file))))
   1078        ;; show how list is shown in dashboard ("el" is automatically assigned)
   1079        (format "%s: %s" (plist-get el :name) (plist-get el :count)))))
   1080 
   1081   (defun dashboard-insert-syncthing-status (list-size)
   1082     (when (and (get-buffer-process za/st-buffer-name)
   1083                (boundp 'za/syncthing-api-key))
   1084       (let* ((syncstatus (json-parse-string
   1085                           (shell-command-to-string
   1086                            (format "curl -sH 'Authorization: Bearer %s' 'http://localhost:8384/rest/db/completion'" za/syncthing-api-key))))
   1087              (completion (gethash "completion" syncstatus))
   1088              (folders (json-parse-string
   1089                        (shell-command-to-string
   1090                         (format "curl -sH 'Authorization: Bearer %s' 'http://localhost:8384/rest/stats/folder'" za/syncthing-api-key))
   1091                        :false-object nil
   1092                        :null-object nil))
   1093              (org-lastsync (format-time-string "%H:%M:%S (%F)" (date-to-time (gethash "lastScan" (gethash "lifeorg" folders)))))
   1094              (devices (json-parse-string
   1095                        (shell-command-to-string
   1096                         (format "curl -sH 'Authorization: Bearer %s' 'http://localhost:8384/rest/system/connections'" za/syncthing-api-key))
   1097                        :object-type 'alist
   1098                        :false-object nil
   1099                        :null-object nil))
   1100              (connected-devices (length (seq-filter
   1101                                          (lambda (a)
   1102                                            (alist-get 'connected (cdr a))) (alist-get 'connections devices))))
   1103              (item-list `(,(format "Completion: %s%%" completion)
   1104                           ,(format "Connected devices: %s" connected-devices)
   1105                           ,(format "Org last sync: %s" org-lastsync))))
   1106 
   1107         (dashboard-insert-section
   1108          ;; Widget title
   1109          "Syncthing:"
   1110          ;; list generated for dashboard
   1111          item-list
   1112          list-size
   1113          'syncthing-status
   1114          ;; shortcut key for section
   1115          nil
   1116          ;; when clicked
   1117          (lambda (&rest _) ())
   1118          ;; show how list is shown in dashboard ("el" is automatically assigned)
   1119          (format "%s" el)))))
   1120 
   1121   (dashboard-setup-startup-hook)
   1122   (setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*")))
   1123 #+end_src
   1124 
   1125 ** Pixel scroll mode
   1126 #+begin_src emacs-lisp
   1127   (unless (version< emacs-version "29")
   1128     (pixel-scroll-precision-mode))
   1129 #+end_src
   1130 * General packages
   1131 
   1132 ** which-key
   1133 Minor mode for Emacs that displays the key bindings following your currently entered incomplete command (a prefix) in a popup.
   1134 
   1135 #+BEGIN_SRC emacs-lisp
   1136   (use-package which-key
   1137     :delight
   1138     :config
   1139     (which-key-mode))
   1140 #+end_src
   1141 
   1142 ** counsel + ivy + swiper + prescient
   1143 Better incremental completion and selection narrowing.
   1144 And a bunch more.
   1145 Generally makes for nicer interactivity, like ido mode on steroids.
   1146 Switched to this from Helm, it's more lightweight.
   1147 
   1148 *** ivy: generic completion mechanism
   1149 #+begin_src emacs-lisp
   1150   (use-package ivy
   1151     :delight
   1152     :custom
   1153     (ivy-use-virtual-buffers t "extend searching to bookmarks")
   1154     (ivy-height 20 "set height of the ivy window")
   1155     (ivy-count-format "(%d/%d) " "count format, from the ivy help page")
   1156     (ivy-display-style 'fancy)
   1157     (ivy-format-function 'ivy-format-function-line)
   1158     (ivy-use-selectable-prompt t "to let me select exactly what I'm typing as a candidate")
   1159 
   1160     :bind (("C-x b" . ivy-switch-buffer)
   1161            ("C-c v" . ivy-push-view)
   1162            ("C-c V" . ivy-pop-view)
   1163 
   1164            ;; accidentally pressing shift-space deletes input, because
   1165            ;; by default, shift-space is bound to
   1166            ;; ~ivy-restrict-to-matches~ in the ivy minibuffer.
   1167            :map ivy-minibuffer-map
   1168            ("S-SPC" . (lambda () (interactive) (insert ?\s)))
   1169            ("<backtab>" . ivy-restrict-to-matches))
   1170     :config
   1171     (ivy-add-actions
   1172      'counsel-dired
   1173      '(("f" (lambda (dir) (counsel-fzf nil dir)) "Fzf in directory")
   1174        ("g" (lambda (dir) (counsel-ag nil dir)) "Ag in directory")))
   1175     (ivy-add-actions
   1176      'dired
   1177      '(("f" (lambda (dir) (ivy-exit-with-action (counsel-fzf nil dir))) "Fzf in directory")
   1178        ("g" (lambda (dir) (ivy-exit-with-action (counsel-ag nil dir))) "Ag in directory")))
   1179     (ivy-add-actions
   1180      'counsel-describe-function
   1181      '(("d" (lambda (fun) (ivy-exit-with-action (edebug-instrument-function (intern fun)))) "Edebug instrument function")))
   1182     (ivy-mode)
   1183 
   1184     (defun edit-script ()
   1185       "Edit a file in ~/.scripts/"
   1186       (interactive)
   1187       (let ((input (ivy--input)))
   1188         (ivy-quit-and-run (counsel-file-jump nil "~/.scripts/"))))
   1189 
   1190     (defun edit-config ()
   1191       "Edit a file in ~/.dotfiles/"
   1192       (interactive)
   1193       (let ((input (ivy--input)))
   1194         (ivy-quit-and-run (counsel-file-jump nil "~/.dotfiles/")))))
   1195 #+end_src
   1196 
   1197 *** counsel: collection of common Emacs commands enhanced using ivy
   1198 #+begin_src emacs-lisp
   1199   (use-package counsel
   1200     :demand
   1201     :delight
   1202     :config
   1203     (counsel-mode)
   1204     :bind (("M-x" . counsel-M-x)
   1205            ("C-x C-f" . counsel-find-file)
   1206            ("M-y" . counsel-yank-pop)
   1207            ("C-c c" . counsel-compile)
   1208            ("M-s g" . counsel-ag)
   1209            ("M-s f" . counsel-fzf)
   1210            ("C-c b" . counsel-bookmark)
   1211            ("C-c p" . counsel-recentf)
   1212            ("C-c o" . counsel-outline)
   1213            ("C-h f" . counsel-describe-function)
   1214            ("C-h v" . counsel-describe-variable)
   1215            ("C-h o" . counsel-describe-symbol)
   1216            ("C-c g j" . counsel-org-agenda-headlines)))
   1217 #+end_src
   1218 *** swiper: search enhanced using ivy
   1219 #+begin_src emacs-lisp
   1220   (use-package swiper
   1221     :bind (("C-s" . swiper-isearch)
   1222            ("C-r" . swiper-isearch-backward)))
   1223 #+end_src
   1224 *** prescient: scoring system for M-x
   1225 #+begin_src emacs-lisp
   1226   (use-package prescient
   1227     :config (prescient-persist-mode))
   1228 
   1229   (use-package ivy-prescient
   1230     :after counsel
   1231     :custom (ivy-prescient-retain-classic-highlighting t)
   1232     :config (ivy-prescient-mode))
   1233 #+end_src
   1234 
   1235 *** ivy-posframe: ivy in a popup
   1236 I like having ivy in a popup.
   1237 Problem: posframe does not work if emacs is too old and on macos.
   1238 See here: https://github.com/tumashu/posframe/issues/30
   1239 On Mac, ~brew install --HEAD emacs~ doesn't work either.
   1240 Solution: ~brew tap daviderestivo/emacs-head && brew install emacs-head@28 --with-cocoa~
   1241 
   1242 #+begin_src emacs-lisp
   1243   (if (and (version< emacs-version "28") (equal system-type 'darwin))
   1244       (message "ivy-posframe won't work properly, run `brew install daviderestivo/emacs-head/emacs-head@28 --with-cocoa`")
   1245     (use-package ivy-posframe
   1246       :delight
   1247       :custom
   1248       (ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-center)))
   1249       (ivy-posframe-parameters
   1250        '((left-fringe . 8)
   1251          (right-fringe . 8)))
   1252       (ivy-posframe-border-width 3)
   1253       (ivy-truncate-lines nil) ;; otherwise the cursor gets hidden by long lines in posframe
   1254       :custom-face
   1255       (ivy-posframe-border ((t (:inherit mode-line-inactive))))
   1256       :config
   1257       (ivy-posframe-mode 1)))
   1258 #+end_src
   1259 
   1260 [[https://github.com/tumashu/ivy-posframe/issues/123][See here]] for cursor going offscreen in the posframe. Currently 'solved' with ~ivy-truncate-lines~ nil.
   1261 
   1262 ** DISABLED vertico + consult + marginalia + embark + posframe + prescient
   1263 Alternative to counsel/ivy/swiper, will probably switch to this at some point.
   1264 [[https://old.reddit.com/r/emacs/comments/qfrxgb/using_emacs_episode_80_vertico_marginalia_consult/hi6mfh7/][Here]] is a good comparison.
   1265 
   1266 A [[https://old.reddit.com/r/emacs/comments/11lqkbo/weekly_tips_tricks_c_thread/jbe06qv/][comment here to follow]] when I switch to vertico.
   1267 #+begin_src emacs-lisp :tangle no
   1268   (dolist (pack '(vertico consult marginalia embark vertico-posframe vertico-prescient))
   1269     (unless (package-installed-p pack)
   1270       (package-install pack))
   1271     (require pack))
   1272 
   1273   (vertico-mode 1)
   1274   (vertico-posframe-mode 1)
   1275   (marginalia-mode 1)
   1276   (vertico-prescient-mode 1)
   1277   (setq completion-styles '(basic substring partial-completion flex))
   1278 
   1279   (global-set-key (kbd "M-o") #'embark-act)
   1280   (global-set-key (kbd "C-s") #'consult-line)
   1281 
   1282 #+end_src
   1283 ** company: completion mechanism
   1284 #+begin_src emacs-lisp
   1285   (use-package company)
   1286 #+end_src
   1287 
   1288 ** wgrep: writable grep
   1289 #+begin_src emacs-lisp
   1290   (use-package wgrep)
   1291 #+end_src
   1292 ** avy: jump to any position
   1293 This lets me jump to any position in Emacs rather quickly, sometimes it's useful.
   1294 ~avy-goto-char-timer~ lets me type a part of the text before avy kicks in.
   1295 
   1296 #+begin_src emacs-lisp
   1297   (use-package avy
   1298     :custom
   1299     (avy-single-candidate-jump nil "Often I want to perform an action, never jump automatically")
   1300     :bind
   1301     (("C-:" . avy-goto-char-timer)))
   1302 #+end_src
   1303 
   1304 ** calendar
   1305 #+begin_src emacs-lisp
   1306   (use-package calendar
   1307     :ensure nil ; comes with Emacs
   1308     :custom
   1309     (calendar-week-start-day 1))
   1310 #+end_src
   1311 ** calfw: graphical calendar
   1312 Basically provides a way to show the org agenda as a standard GUI calendar app would.
   1313 
   1314 #+begin_src emacs-lisp
   1315   (use-package calfw
   1316     :config
   1317     (use-package calfw-org)
   1318     :custom
   1319     (cfw:org-overwrite-default-keybinding t))
   1320 #+end_src
   1321 
   1322 ** vanish: hide parts of the file
   1323 #+begin_src emacs-lisp
   1324   (use-package vanish
   1325     :init
   1326     (za/package-vc-install :repo "thezeroalpha/vanish.el" :rev "develop")
   1327     (require 'vanish)
   1328     :ensure nil
   1329     :bind (:map vanish-mode-map
   1330                 ("C-c q h h" . vanish-hide-dwim)
   1331                 ("C-c q h u r" . vanish-show-all-regions)
   1332                 ("C-c q h u e" . vanish-elt-unhide)
   1333                 ("C-c q h u u" . vanish-show-all)))
   1334 #+end_src
   1335 ** magit
   1336 #+begin_src emacs-lisp
   1337   (use-package magit)
   1338 #+end_src
   1339 ** vterm
   1340 Emacs has a bunch of built-in terminal emulators.
   1341 And they all suck.
   1342 (OK not really, eshell is alright, but not for interactive terminal programs like newsboat/neomutt)
   1343 
   1344 Also use emacsclient inside vterm as an editor, because that'll open documents in the existing Emacs session.
   1345 And I'm not gonna be a heretic and open Vim inside of Emacs.
   1346 
   1347 #+begin_src emacs-lisp
   1348   (use-package vterm
   1349     :hook
   1350     (vterm-mode . (lambda () (unless server-process (server-start))))
   1351     :bind (("C-c t" . switch-to-vterm))
   1352     :config
   1353     (defun switch-to-vterm ()
   1354       "Switch to a running vterm, or start one and switch to it."
   1355       (interactive)
   1356       (if (get-buffer vterm-buffer-name)
   1357           (switch-to-buffer vterm-buffer-name)
   1358         (vterm))))
   1359 #+end_src
   1360 ** sr-speedbar
   1361 Make speed bar show in the current frame.
   1362 
   1363 #+begin_src emacs-lisp
   1364   (use-package sr-speedbar
   1365     :bind (("C-c F" . za/jump-to-speedbar-or-open)
   1366            :map speedbar-mode-map
   1367            ("q" . sr-speedbar-close))
   1368     :custom
   1369     (sr-speedbar-right-side nil)
   1370 
   1371     :config
   1372     (defun za/jump-to-speedbar-or-open ()
   1373       "Open a speedbar or jump to it if already open."
   1374       (interactive)
   1375       (if (or (not (boundp 'sr-speedbar-exist-p))
   1376               (not (sr-speedbar-exist-p)))
   1377           (sr-speedbar-open))
   1378       (sr-speedbar-select-window)))
   1379 #+end_src
   1380 ** expand-region
   1381 Expand the selected region semantically.
   1382 
   1383 #+begin_src emacs-lisp
   1384   (use-package expand-region
   1385     :bind ("C-=" . er/expand-region))
   1386 #+end_src
   1387 ** flycheck
   1388 Install flycheck:
   1389 
   1390 #+begin_src emacs-lisp
   1391   (use-package flycheck)
   1392 #+end_src
   1393 ** rainbow-mode: visualise hex colors
   1394 'rainbow-mode' lets you visualise hex colors:
   1395 
   1396 #+begin_src emacs-lisp
   1397   (use-package rainbow-mode)
   1398 #+end_src
   1399 ** hl-todo: highlight TODO keywords
   1400 I want to highlight TODO keywords in comments:
   1401 
   1402 #+begin_src emacs-lisp
   1403   (use-package hl-todo
   1404     :custom-face
   1405     (hl-todo ((t (:inherit hl-todo :underline t))))
   1406     :custom
   1407     (hl-todo-keyword-faces '(("TODO"   . "#ff7060")
   1408                              ("FIXME"  . "#caa000")))
   1409     :config
   1410     (global-hl-todo-mode t))
   1411 #+end_src
   1412 ** undo-tree
   1413 Sometimes it's better to look at undo history as a tree:
   1414 
   1415 #+begin_src emacs-lisp
   1416   (use-package undo-tree
   1417     :delight
   1418     :custom
   1419     (undo-tree-history-directory-alist
   1420      (progn (let ((undo-tree-dir (concat user-emacs-directory "undo-tree/")))
   1421               (unless (file-directory-p undo-tree-dir) (make-directory undo-tree-dir))
   1422               `(("." . ,undo-tree-dir)))))
   1423 
   1424     :config
   1425     (global-undo-tree-mode))
   1426 #+end_src
   1427 
   1428 *** TODO undo tree dir should be configurable
   1429 ** eglot
   1430 A good LSP plugin.
   1431 
   1432 #+begin_src emacs-lisp
   1433   (use-package eglot)
   1434 #+end_src
   1435 ** crdt
   1436 Collaborative editing in Emacs:
   1437 
   1438 #+begin_src emacs-lisp
   1439   (use-package crdt)
   1440 #+end_src
   1441 ** git gutter
   1442 General git gutter:
   1443 
   1444 #+begin_src emacs-lisp
   1445   (use-package git-gutter
   1446     :bind (("C-c d n" . git-gutter:next-hunk)
   1447            ("C-c d p" . git-gutter:previous-hunk))
   1448     :config
   1449     (global-git-gutter-mode 1))
   1450 #+end_src
   1451 ** keycast
   1452 In case I want to show what keys I'm pressing.
   1453 
   1454 #+begin_src emacs-lisp
   1455   (use-package keycast)
   1456 #+end_src
   1457 ** ace-window: better window switching
   1458 Window switching with ~other-window~ sucks when I have more than 2 windows open. Too much cognitive load.
   1459 This lets me select a window to jump to using a single key, sort of like ~avy~.
   1460 
   1461 #+begin_src emacs-lisp
   1462   (use-package ace-window
   1463     :custom
   1464     (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) "I prefer using home-row keys instead of numbers")
   1465 
   1466     :custom-face
   1467     ;; I want something a little more contrasty
   1468     (aw-leading-char-face ((t (:inherit font-lock-keyword-face :height 2.0))))
   1469 
   1470     :bind ("M-o" . ace-window))
   1471 #+end_src
   1472 ** decide-mode for dice rolling
   1473 #+begin_src emacs-lisp
   1474   (use-package decide
   1475     :init (za/package-vc-install :repo "lifelike/decide-mode" :name "decide")
   1476     :ensure nil
   1477     :bind ("C-c q ?" . decide-mode))
   1478 #+end_src
   1479 
   1480 ** try: try out different packages
   1481 #+begin_src emacs-lisp
   1482   (use-package try)
   1483 #+end_src
   1484 ** dumb-jump
   1485 "jump to definition" package, minimal configuration with no stored indexes.
   1486 Uses The Silver Searcher ag, ripgrep rg, or grep to find potential definitions of a function or variable under point.
   1487 
   1488 #+begin_src emacs-lisp
   1489   (use-package dumb-jump)
   1490 #+end_src
   1491 
   1492 Enable xref backend:
   1493 
   1494 #+begin_src emacs-lisp
   1495   (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
   1496   (setq xref-show-definitions-function #'xref-show-definitions-completing-read)
   1497 #+end_src
   1498 ** DISABLED command-log-mode
   1499 Simple real-time logger of commands.
   1500 
   1501 #+begin_src emacs-lisp :tangle no
   1502   (use-package command-log-mode)
   1503 #+end_src
   1504 ** package-lint
   1505 Linter for the metadata in Emacs Lisp files which are intended to be packages.
   1506 
   1507 #+begin_src emacs-lisp
   1508   (use-package package-lint)
   1509   (use-package flycheck-package)
   1510   (eval-after-load 'flycheck
   1511     '(flycheck-package-setup))
   1512 #+end_src
   1513 ** prism: change color of text depending on depth
   1514 Prism changes the color of text depending on their depth. Makes it easier to see where something is at a glance.
   1515 
   1516 #+begin_src emacs-lisp
   1517   (use-package prism)
   1518 #+end_src
   1519 ** olivetti: distraction-free writing
   1520 #+begin_src emacs-lisp
   1521   (use-package olivetti
   1522     :diminish)
   1523 #+end_src
   1524 ** nov.el: EPUB support
   1525 #+begin_src emacs-lisp
   1526   (use-package nov)
   1527   (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))
   1528 #+end_src
   1529 ** god-mode: reduce the need to hold down modifier keys
   1530 - All commands are assumed to use the control modifier (C-) unless otherwise indicated.
   1531 - g is used to indicate the meta modifier
   1532 - G is used to indicate both the control and meta modifiers
   1533 #+begin_src emacs-lisp
   1534   (use-package god-mode
   1535     :bind
   1536     (("s-<escape>" . god-mode-all)
   1537      :map god-local-mode-map
   1538      ("z" . repeat)
   1539      ("i" . god-local-mode))
   1540     :hook    (post-command . za/god-mode-update-mode-line)
   1541     :config
   1542     (defun za/god-mode-update-mode-line ()
   1543       "Update the color of the modeline depending on god-mode."
   1544       (cond (god-local-mode
   1545              (set-face-attribute 'mode-line nil :background "#770085"))
   1546             (t
   1547              (let* ((current-theme (car custom-enabled-themes))
   1548                      (theme-settings (get current-theme 'theme-settings)))
   1549                 (dolist (theme-setting theme-settings)
   1550                   (if (and (eq (car theme-setting) 'theme-face)
   1551                            (eq (cadr theme-setting) 'mode-line))
   1552                       (let* ((face-def (caar (last theme-setting)))
   1553                              (properties (car (last face-def)))
   1554                              (bg (plist-get properties :background)))
   1555                         (set-face-attribute 'mode-line nil :background bg)))))))))
   1556 #+end_src
   1557 ** devil: alternative to god-mode that uses a comma
   1558 #+begin_src emacs-lisp
   1559   (use-package devil
   1560     :init
   1561     (za/package-vc-install :repo "susam/devil")
   1562     (require 'devil)
   1563     :custom
   1564     (devil-lighter " \u272A")
   1565     (devil-prompt "\u272A %t")
   1566     :config (global-devil-mode)
   1567     :bind ("C-," . global-devil-mode))
   1568 #+end_src
   1569 ** academic-phrases
   1570 Gives ideas for phrases to use in academic writing.
   1571 #+begin_src emacs-lisp
   1572   (use-package academic-phrases)
   1573 #+end_src
   1574 ** ediff
   1575 #+begin_src emacs-lisp
   1576   (use-package ediff
   1577     :custom
   1578     ((ediff-keep-variants nil "Prompt to remove unmodifid buffers after session")
   1579      (ediff-make-buffers-readonly-at-startup nil "Don't make all buffers read-only at startup")
   1580      (ediff-show-clashes-only t "Only show diff regions where both buffers disagree with ancestor")
   1581      (ediff-split-window-function 'split-window-horizontally "I want long vertical side-by-side windows")
   1582      (ediff-window-setup-function 'ediff-setup-windows-plain "Everything in one frame please")))
   1583 #+end_src
   1584 ** highlight-indent-guides
   1585 #+begin_src emacs-lisp
   1586   (use-package highlight-indent-guides
   1587     :hook (yaml-mode . highlight-indent-guides-mode)
   1588     :custom
   1589     ((highlight-indent-guides-method 'character))
   1590     :custom-face
   1591     (highlight-indent-guides-character-face ((t (:foreground "#adadad")))))
   1592 #+end_src
   1593 ** cc-avy
   1594 #+begin_src emacs-lisp
   1595   (use-package cc-avy
   1596     :ensure nil ; local
   1597     :bind ("C-M-:" . cc/avy-menu))
   1598 #+end_src
   1599 ** annotate
   1600 #+begin_src emacs-lisp
   1601   (use-package annotate
   1602     :custom (annotate-annotation-position-policy :margin)
   1603     :config
   1604     (defun za/annotate-initialize-extra-hooks ()
   1605       (add-hook 'after-save-hook #'annotate-save-annotations t t))
   1606     (defun za/annotate-shutdown-extra-hooks ()
   1607       (remove-hook 'after-save-hook #'annotate-save-annotations t))
   1608     (advice-add 'annotate-initialize :after #'za/annotate-initialize-extra-hooks)
   1609     (advice-add 'annotate-shutdown :after #'za/annotate-shutdown-extra-hooks))
   1610 
   1611 #+end_src
   1612 ** yasnippet
   1613 #+begin_src emacs-lisp
   1614   (use-package yasnippet
   1615     :config (yas-global-mode)
   1616     :delight)
   1617 #+end_src
   1618 * Mode/language specific packages
   1619 ** Org
   1620 *** Custom functions
   1621 **** Get number of headlines in a file
   1622 #+begin_src emacs-lisp
   1623   (defun za/org-count-headlines-in-file (level filename)
   1624     "Count number of level LEVEL headlines in FILENAME. If LEVEL is 0, count all."
   1625     (let ((headline-str (cond ((zerop level) "^\*+")
   1626                               (t (format "^%s " (apply 'concat (make-list level "\\*")))))))
   1627       (save-mark-and-excursion
   1628         (with-temp-buffer
   1629           (insert-file-contents filename)
   1630           (count-matches headline-str (point-min) (point-max))))))
   1631 #+end_src
   1632 
   1633 **** Yank URL
   1634 #+begin_src emacs-lisp
   1635   (defun org-yank-link-url ()
   1636     (interactive)
   1637     (kill-new (org-element-property :raw-link (org-element-context)))
   1638     (message "Link copied to clipboard"))
   1639 #+end_src
   1640 *** Installation
   1641 Install Org and require additional components that I use.
   1642 
   1643 #+begin_src emacs-lisp
   1644   (use-package org
   1645     :custom
   1646     (org-outline-path-complete-in-steps nil "Complete path all at once (needed for completion frameworks")
   1647     (org-format-latex-options (plist-put org-format-latex-options :scale 2.0) "Larger latex previews")
   1648     (org-goto-interface 'outline-path-completion "Use outline path completion for org-goto, instead of its weird interface")
   1649     (org-insert-heading-respect-content t "Insert headings after current subtree")
   1650     (org-id-link-to-org-use-id 'create-if-interactive "If org-store-link is called directly, create an ID.")
   1651     (org-clock-mode-line-total 'today)
   1652     (org-return-follows-link t "Easier link following. Actual enter is still possible with ~C-q C-j~.")
   1653     (org-hide-emphasis-markers t "Don't show italics/bold markers")
   1654     (org-babel-python-command "python3")
   1655     (org-confirm-babel-evaluate nil)
   1656     (org-file-apps '((auto-mode . emacs)
   1657                      (directory . emacs)
   1658                      ("\\.mm\\'" . default)
   1659                      ("\\.x?html?\\'" . default)
   1660                      ("\\.pdf\\'" . emacs)))
   1661     (org-link-elisp-confirm-function #'y-or-n-p)
   1662     (org-link-elisp-skip-confirm-regexp "^org-noter$")
   1663     (org-clock-sound (concat user-emacs-directory "notification.wav"))
   1664     (org-export-backends '(ascii html icalendar latex md odt org pandoc confluence-ext jira))
   1665     (org-catch-invisible-edits 'show-and-error
   1666                                "Sometimes when text is folded away, I might accidentally edit text inside of it. This option prevents that. I wanted to do 'smart', but that has a 'fixme' so it might change in the future...Instead, show what's being edited, but don't perform the edit.")
   1667     (org-src-tab-acts-natively t "a tab in a code block indents the code as it should")
   1668     (org-attach-store-link-p 'attached)
   1669     (org-attach-archive-delete 'query)
   1670     (org-stuck-projects '("/PROJ"
   1671                           ("NEXT" "STARTED")
   1672                           nil nil)
   1673                         "List projects that are stuck (don't have a next action)")
   1674     (org-tag-alist (let ((za/org-tag-energy-levels
   1675                           '((:startgroup)
   1676                             ("sport" . ?h) ; Sport (deep focus, long tasks, no interruptions, at least an hour)
   1677                             ("cruise" . ?l) ; Cruise (shallow focus, can be interrupted, can batch lots of quick tasks together)
   1678                             ("parked" . ?e) ; Parked (take a break, look into the distance, walk the dog, stretch, etc.)
   1679                             ("errand" . ?o) ; Errand (anything that involves me being out of the house)
   1680                             (:endgroup)))
   1681                          (za/org-tag-1-3-5
   1682                           '(; 1-3-5 tagging
   1683                             (:startgroup)
   1684                             ("_1" . ?1) ; 1 big task, 3-4 hrs
   1685                             ("_3" . ?3) ; 3 medium tasks, 1-2 hrs
   1686                             ("_5" . ?5) ; 5 small tasks, 30min-1hr
   1687                             (:endgroup))))
   1688                      `(,@za/org-tag-contexts ,@za/org-tag-energy-levels ,@za/org-tag-1-3-5)))
   1689 
   1690     :bind (("C-c a" . org-agenda)
   1691            ("C-c n" . org-capture)
   1692            ("C-c l" . org-store-link)
   1693            :map org-mode-map
   1694            ("C-M-<return>" . org-insert-todo-heading)
   1695            ("C-c M-y" . org-yank-link-url)
   1696            ("C-c N" . org-noter)
   1697            ("C-M-i" . completion-at-point)
   1698            ("C-c SPC" . org-table-blank-field)
   1699            ("C-c C-w" . za/org-refile-wrapper))
   1700     :hook ((org-mode . abbrev-mode)
   1701            (org-mode . za/echo-area-tooltips)
   1702            (org-mode . org-superstar-mode)
   1703            (org-mode . org-indent-mode)
   1704            (org-mode . za/settings-on-org-mode)
   1705            (org-mode . org-pretty-table-mode)
   1706            (org-mode . variable-pitch-mode))
   1707     :config
   1708     (za/package-vc-install :repo "Fuco1/org-pretty-table")
   1709     (require 'org-pretty-table)
   1710     (delight 'org-pretty-table nil)
   1711 
   1712 
   1713     (za/package-vc-install :repo "https://git.sr.ht/~bzg/org-contrib" :load "lisp/")
   1714     (require 'org-contrib)
   1715     (require 'org-checklist)
   1716     (delight 'org-indent-mode nil 'org-indent)
   1717     (defun za/settings-on-org-mode ()
   1718       "Settings on enabling org mode"
   1719       (za/toggle-wrap t))
   1720 
   1721     (defcustom za/org-inline-images-desired-screen-proportion (/ (float 3) 4)
   1722       "Percentage of the window (as a float) that Org inline images should take up."
   1723       :type 'float)
   1724 
   1725     (defun za/org-display-inline-images-set-width (&rest _)
   1726       "Set `org-image-actual-width` dynamically before displaying images."
   1727       (if (window-system)
   1728           (let* ((total-width (window-pixel-width))
   1729                  (image-width (round (* total-width za/org-inline-images-desired-screen-proportion))))
   1730             (setq-local org-image-actual-width image-width))))
   1731 
   1732     (advice-add 'org-display-inline-images :before #'za/org-display-inline-images-set-width)
   1733 
   1734     (defun za/org-attach-tag (old/org-attach-tag &rest args)
   1735       "Wraps :around org-attach-tag (as OLD/ORG-ATTACH-TAG) with ARGS.
   1736   When inside capture for org-roam, attaching fails at
   1737   org-attach-tag. This function prevents that error interrupting
   1738   org-attach."
   1739       (if ; there's no heading
   1740           (not (org-element-lineage (org-element-at-point)
   1741                                     '(headline inlinetask)
   1742                                     'include-self))
   1743           nil ; there's no point attaching a tag
   1744                                           ; otherwise, normal attach
   1745         (apply old/org-attach-tag args)))
   1746 
   1747     (advice-add #'org-attach-tag :around #'za/org-attach-tag)
   1748     (defun za/org-clear-1-3-5 ()
   1749       "Clears the _1/_3/_5 daily tags from all antries."
   1750       (interactive)
   1751       (let ((number-of-entries
   1752              (length (org-map-entries
   1753                       (lambda ()
   1754                         (let* ((tags-1-3-5 '("_1" "_3" "_5"))
   1755                                (tags-without-1-3-5 (seq-remove (lambda (e) (member e tags-1-3-5))
   1756                                                                org-scanner-tags)))
   1757                           (org-set-tags tags-without-1-3-5)))
   1758                       "_1|_3|_5"
   1759                       'agenda-with-archives))))
   1760         (message "Modified %d entries." number-of-entries)))
   1761 
   1762     (require 'org-tempo)
   1763     (require 'org-habit)
   1764     (require 'org-id)
   1765     (use-package ob-async)
   1766     (use-package ob-rust)
   1767     (org-babel-do-load-languages
   1768      'org-babel-load-languages
   1769      '((emacs-lisp . t)
   1770        (R . t)
   1771        (python . t)
   1772        (ruby . t)
   1773        (shell . t)
   1774        (sqlite . t)
   1775        (rust . t)))
   1776     (use-package inf-ruby)
   1777     (use-package org-superstar
   1778       :custom
   1779       (org-superstar-leading-bullet ?\s))
   1780 
   1781     ;; Linking to emails via notmuch
   1782     (use-package ol-notmuch)
   1783 
   1784     ;; Improved search
   1785     (use-package org-ql)
   1786 
   1787     ;; Tempo expansions
   1788     (add-to-list 'org-structure-template-alist '("se" . "src emacs-lisp"))
   1789     (add-to-list 'org-structure-template-alist '("sb" . "src bibtex"))
   1790     (add-to-list 'org-structure-template-alist '("ss" . "src sh"))
   1791     (add-to-list 'org-structure-template-alist '("sy" . "src yaml")))
   1792 #+end_src
   1793 *** Agenda & GTD
   1794 **** Agenda mode settings
   1795 #+begin_src emacs-lisp
   1796   (use-package org-agenda
   1797     :ensure org
   1798     :bind (:map org-agenda-mode-map
   1799                 ("C-c TAB" . za/org-agenda-goto-narrowed-subtree)
   1800                 ("@" . za/org-agenda-show-context-tags))
   1801     :custom
   1802     (org-agenda-files (list za/org-life-main
   1803                             za/org-life-inbox
   1804                             za/org-life-tickler))
   1805     (org-agenda-text-search-extra-files
   1806      (directory-files za/org-life-dir t (rx bol (not ?.) (* anything) ".org"))
   1807      "I want to search all Org files in the life directory")
   1808 
   1809     :config
   1810     (defun za/org-agenda-show-context-tags ()
   1811       "Show the context tags (e.g. @computer) applicable to the current item."
   1812       (interactive)
   1813       (let* ((tags (org-get-at-bol 'tags))
   1814              (context-tag-p (lambda (tag) (string-prefix-p "@" tag)))
   1815              (context-tags (seq-filter context-tag-p tags)))
   1816         (if context-tags
   1817             (message "Contexts are :%s:"
   1818                      (org-no-properties (mapconcat #'identity context-tags ":")))
   1819           (message "No contexts associated with this line"))))
   1820     (defun za/org-agenda-goto-narrowed-subtree ()
   1821       "Jump to current agenda item and narrow to its subtree."
   1822       (interactive)
   1823       (delete-other-windows)
   1824       (org-agenda-goto)
   1825       (org-narrow-to-subtree)
   1826       (outline-hide-subtree)
   1827       (org-show-children 1)
   1828       (other-window 1)))
   1829 #+end_src
   1830 
   1831 Fix tag display by dynamically calculating the column.
   1832 
   1833 #+begin_src emacs-lisp
   1834   (defun za/settings-org-agenda-mode ()
   1835     "My settings for org agenda mode"
   1836     )
   1837   (add-hook 'org-agenda-mode-hook #'za/settings-org-agenda-mode)
   1838 #+end_src
   1839 
   1840 **** Opening files
   1841 Convenience functions to make opening the main file faster:
   1842 
   1843 #+begin_src emacs-lisp
   1844   (defun gtd () "GTD: main file" (interactive) (find-file za/org-life-main))
   1845   (defun gtd-inbox ()
   1846     "GTD: inbox"
   1847     (interactive)
   1848     (let ((count-docs (length (directory-files za/org-life-doc-inbox nil (rx bos (not ?.))))))
   1849       (find-file za/org-life-inbox)
   1850       (when (> count-docs 0)
   1851         (dired-other-window za/org-life-doc-inbox)
   1852         (dired-revert)
   1853         (other-window 1))))
   1854   (defun gtd-inbox-mobile () "GTD: mobile inbox" (interactive) (find-file za/org-life-inbox-mobile))
   1855   (defun gtd-archive () "GTD: archive" (interactive) (find-file za/org-life-archive))
   1856   (defun gtd-someday () "GTD: someday" (interactive) (find-file za/org-life-someday))
   1857   (defun gtd-tickler () "GTD: tickler" (interactive) (find-file za/org-life-tickler))
   1858 #+end_src
   1859 
   1860 Bind keys to those functions:
   1861 
   1862 #+begin_src emacs-lisp
   1863   (bind-keys :prefix "M-g t"
   1864              :prefix-map za/gtd-files-map
   1865              :prefix-docstring "Visit GTD file"
   1866              ("i" . gtd-inbox)
   1867              ("l" . gtd)
   1868              ("a" . gtd-archive)
   1869              ("s" . gtd-someday)
   1870              ("t" . gtd-tickler))
   1871 #+end_src
   1872 
   1873 To improve jumping to any headline via counsel, filter returned candidates to include source file.
   1874 
   1875 #+begin_src emacs-lisp
   1876   (defun za/counsel-org-agenda-headlines--candidates-with-filename (candidates)
   1877     "Convert CANDIDATES to include source filename for each candidate."
   1878     (mapcar (lambda (candidate)
   1879               (let ((name (nth 0 candidate))
   1880                     (path (nth 1 candidate))
   1881                     (pos (nth 2 candidate)))
   1882                 (list (format "%s/%s" (file-name-nondirectory path) name)
   1883                       path
   1884                       pos)))
   1885             candidates))
   1886 
   1887   (advice-add #'counsel-org-agenda-headlines--candidates :filter-return #'za/counsel-org-agenda-headlines--candidates-with-filename)
   1888 #+end_src
   1889 
   1890 *** Processing inbox
   1891 I made a function for processing the inbox, focusing on one item at a time:
   1892 
   1893 #+begin_src emacs-lisp
   1894   (defun za/gtd-inbox-next-item ()
   1895     (interactive)
   1896     (unless (string= (buffer-file-name) (file-truename za/org-life-inbox))
   1897       (user-error "You're not in your GTD inbox file."))
   1898     (widen)
   1899     (org-first-headline-recenter)
   1900     (org-narrow-to-subtree))
   1901 #+end_src
   1902 
   1903 And a conditional binding:
   1904 
   1905 #+begin_src emacs-lisp
   1906   (bind-key "C-c g n" #'za/gtd-inbox-next-item 'org-mode-map (string= (buffer-file-name) (file-truename za/org-life-inbox)))
   1907 #+end_src
   1908 
   1909 And a function for importing other inboxes:
   1910 
   1911 #+begin_src emacs-lisp
   1912   (defun za/gtd-inbox-import ()
   1913     (interactive)
   1914     (unless (string= (buffer-file-name) (file-truename za/org-life-inbox))
   1915       (user-error "You're not in your GTD inbox file"))
   1916     (when (directory-files za/org-life-dir nil "\\.sync-conflict-")
   1917         (user-error "Sync conflicts found, please fix them"))
   1918     (let ((mobile (if (boundp 'za/org-life-inbox-mobile) (file-truename za/org-life-inbox-mobile) nil))
   1919           (calendar (if (boundp 'za/org-life-calendar-inbox) (file-truename za/org-life-calendar-inbox) nil)))
   1920       (save-mark-and-excursion
   1921         (goto-char (point-max))
   1922         (when mobile
   1923           (insert-file mobile)
   1924           (goto-char (point-max))
   1925           (write-region "" nil mobile))
   1926         (when calendar
   1927           (insert-file calendar)
   1928           (write-region "" nil calendar)
   1929           (goto-char (point-max)))
   1930         (message "Imported other inboxes."))))
   1931 #+end_src
   1932 
   1933 Also with a conditional binding:
   1934 
   1935 #+begin_src emacs-lisp
   1936   (bind-key "C-c g i" #'za/gtd-inbox-import 'org-mode-map (string= (buffer-file-name) (file-truename za/org-life-inbox)))
   1937 #+end_src
   1938 *** Refiling & archiving
   1939 #+begin_src emacs-lisp
   1940   (use-package org-refile
   1941     :ensure org
   1942     :custom
   1943     (org-refile-targets `((,za/org-life-main :maxlevel . 3)
   1944                           (,za/org-life-someday :level . 1)
   1945                           (,za/org-life-tickler :maxlevel . 3))
   1946                         "Where I want to be able to move subtrees (doesn't include inbox because I never refile to that, and the archive has its own keybining)")
   1947     (org-archive-location (concat za/org-life-archive "::datetree/")
   1948                           "I want to archive to a specific file, in a date tree")
   1949     (org-refile-use-outline-path 'file
   1950                                  "Include the destination file as an element in the path to a heading, and to use the full paths as completion targets rather than just the heading text itself")
   1951     (org-outline-path-complete-in-steps nil
   1952                                         "Tell Org that I don’t want to complete in steps; I want Org to generate all of the possible completions and present them at once (necessary for Helm/Ivy)")
   1953     (org-refile-allow-creating-parent-nodes 'confirm
   1954                                             "Allow me to tack new heading names onto the end of my outline path, and if I am asking to create new ones, make me confirm it"))
   1955 #+end_src
   1956 
   1957 *** Quick capture
   1958 Quick capture lets me send something to my inbox very quickly, without thinking about where it should go.
   1959 The inbox is processed later.
   1960 
   1961 Templates for quick capture:
   1962 
   1963 #+begin_src emacs-lisp
   1964   (use-package org-capture
   1965     :ensure org
   1966     :custom
   1967     (org-capture-templates `(("t" "Todo [inbox]" entry
   1968                               (file ,za/org-life-inbox)
   1969                               "* TODO %i%?")
   1970 
   1971                              ("s" "Save for read/watch/listen" entry
   1972                               (file+headline ,za/org-life-someday "Read/watch/listen")
   1973                               "* TODO %?[[%^{link}][%^{description}]] %^G"))))
   1974 #+end_src
   1975 
   1976 *** Todo & custom agenda views
   1977 Todo keywords based on the GTD system (pipe separates incomplete from complete).
   1978 Apart from the logging-on-done configured [[*Logging][below]], I also want to log a note & timestamp when I start waiting on something.
   1979 In ~org-todo-keywords~, ~@~ means note+timestamp, ~!~ means timestamp, ~@/!~ means note+timestamp on state entry and timestamp on leave.
   1980 
   1981 #+begin_src emacs-lisp
   1982   (custom-set-variables '(org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "STARTED(s)" "WAITING(w@)" "PROJ(p)" "|" "DONE(d)" "CANCELLED(c)")))
   1983                         '(org-todo-keyword-faces '(("TODO" . org-todo)
   1984                                                    ("NEXT" . org-todo)
   1985                                                    ("WAITING" . org-todo)
   1986                                                    ("STARTED" . org-todo)
   1987                                                    ("PROJ" . org-todo)
   1988                                                    ("DONE" . org-done)
   1989                                                    ("CANCELLED" . org-done))))
   1990 #+end_src
   1991 
   1992 
   1993 Something is a habit if: it has a HABIT tag, STYLE is habit, LOGGING is logrepeat, it has a scheduled repeater from today.
   1994 
   1995 #+begin_src emacs-lisp
   1996   (defun za/mark-as-habit ()
   1997     "This function makes sure that the current heading has:
   1998   (1) a HABIT tag
   1999   (2) todo set to TODO
   2000   (3) LOGGING property set to logrepeat
   2001   (4) a scheduled repeater from today"
   2002     (interactive)
   2003     (org-back-to-heading t)
   2004     (org-set-property "TODO" "TODO")
   2005     (org-set-property "LOGGING" "logrepeat")
   2006     (org-set-property "STYLE" "habit")
   2007     (org-toggle-tag "HABIT" 'on)
   2008     (org-schedule nil))
   2009 #+end_src
   2010 
   2011 +I decided that projects will not be TODO items, but their progress will be tracked with a progress cookie ([x/y]). This function converts an item to a project: it adds a PROJECT tag, sets the progress indicator to count all checkboxes in sub-items (only TODO items), and removes any existing TODO keywords. Finally, PROJECT tags shouldn't be inherited (i.e. subtasks shouldn't be marked as projects).+
   2012 In the end, I want NEXT items that are part of a project to be shown as such (so inherit that PROJECT tag), but projects themselves will have a PROJ todo keyword.
   2013 This function converts an item to a project.
   2014 
   2015 #+begin_src emacs-lisp
   2016   (defun za/mark-as-project ()
   2017     "This function makes sure that the current heading has
   2018       (1) the tag PROJECT
   2019       (2) the todo keyword PROJ
   2020       (3) the property COOKIE_DATA set to \"todo recursive\"
   2021       (4) a progress indicator"
   2022     (interactive)
   2023     (org-back-to-heading t)
   2024     ;; Step 1: clear out everything
   2025     (org-set-property "TODO" "")
   2026 
   2027     ;; org-set-property errors via org-priority if you try to clear
   2028     ;; priority of an item that doesn't have priority. Stupid design,
   2029     ;; but I can't change that so we gotta jump through hoops:
   2030     (let ((have-priority (org-element-property :priority (org-element-at-point))))
   2031       (when have-priority
   2032         (org-set-property "PRIORITY" "")))
   2033 
   2034     ;; Step 2: set info (stats cookie, todo, tag, properties drawer)
   2035     (forward-whitespace 1)
   2036     (insert "[/] ")
   2037     (org-set-property "TODO" "PROJ")
   2038     (org-toggle-tag "PROJECT" 'on)
   2039     (org-set-property "COOKIE_DATA" "todo recursive")
   2040     (org-update-statistics-cookies nil))
   2041 #+end_src
   2042 
   2043 And a keybinding for it:
   2044 
   2045 #+begin_src emacs-lisp
   2046   (bind-key "C-c g p" #'za/mark-as-project 'org-mode-map)
   2047 #+end_src
   2048 
   2049 Want all tags to be inherited:
   2050 
   2051 #+begin_src emacs-lisp
   2052   (custom-set-variables '(org-tags-exclude-from-inheritance nil))
   2053 #+end_src
   2054 
   2055 Define a function to skip items if they're part of a project (i.e. one of their parents has a "PROJECT" tag).
   2056 +The problem is, the "PROJECT" tag isn't inherited. So, we temporarily disable excluding from inheritance, just for the ~org-get-tags~ call. Then check if "PROJECT" is one of the tags.+ That tag is now inherited.
   2057 
   2058 #+begin_src emacs-lisp
   2059   (defun za/skip-if-in-project ()
   2060     "Skip items that are part of a project but not a project themselves."
   2061     (let ((skip (save-excursion (org-end-of-subtree t)))
   2062           (keep nil)
   2063           (item-tags (let ((org-use-tag-inheritance t)) (org-get-tags)))
   2064           (item-tags-without-inherited (let ((org-use-tag-inheritance nil)) (org-get-tags))))
   2065       (if (and (member "PROJECT" item-tags)
   2066                (not (member "PROJECT" item-tags-without-inherited)))
   2067           skip
   2068         keep)))
   2069 #+end_src
   2070 
   2071 Also, define a function to skip tasks (trees) that are not habits (i.e. don't have the STYLE property ~habit~):
   2072 
   2073 #+begin_src emacs-lisp
   2074   (defun za/skip-unless-habit ()
   2075     "Skip trees that are not habits"
   2076     (let ((skip (save-excursion (org-end-of-subtree t)))
   2077           (keep nil))
   2078       (if (string= (org-entry-get nil "STYLE") "habit")
   2079           keep
   2080         skip)))
   2081 #+end_src
   2082 
   2083 And one to skip tasks that /are/ habits:
   2084 
   2085 #+begin_src emacs-lisp
   2086   (defun za/skip-if-habit ()
   2087     "Skip trees that are not habits"
   2088     (let ((skip (save-excursion (org-end-of-subtree t)))
   2089           (keep nil))
   2090       (if (string= (org-entry-get nil "STYLE") "habit")
   2091           skip
   2092         keep)))
   2093 #+end_src
   2094 
   2095 Skip ones with a habit tag:
   2096 
   2097 #+begin_src emacs-lisp
   2098   (defun za/skip-if-has-habit-tag ()
   2099     (let ((skip (save-excursion (org-end-of-subtree t)))
   2100           (keep nil)
   2101           (item-tags-without-inherited (let ((org-use-tag-inheritance nil)) (org-get-tags))))
   2102       (if (or (member "HABIT" item-tags-without-inherited)
   2103               (member "flatastic" item-tags-without-inherited))
   2104           skip
   2105         keep)))
   2106 #+end_src
   2107 
   2108 And another function, to skip tasks that are blocked:
   2109 
   2110 #+begin_src emacs-lisp
   2111   (defun za/skip-if-blocked ()
   2112     "Skip trees that are blocked by previous tasks"
   2113     (let ((skip (save-excursion (org-end-of-subtree t)))
   2114           (keep nil))
   2115       (if (org-entry-blocked-p)
   2116           skip
   2117         keep)))
   2118 #+end_src
   2119 
   2120 For listing tasks without a context - skip if it has a context tag:
   2121 
   2122 #+begin_src emacs-lisp
   2123   (defun za/skip-if-has-context ()
   2124     (let ((skip (save-excursion (org-end-of-subtree t)))
   2125           (keep nil)
   2126           (item-tags-without-inherited (let ((org-use-tag-inheritance nil)) (org-get-tags)))
   2127           (context-tag-p (lambda (s) (eq (aref s 0) ?@))))
   2128       (if (cl-some context-tag-p item-tags-without-inherited)
   2129           skip
   2130         keep)))
   2131 #+end_src
   2132 
   2133 For listing tasks without an energy level - skip if it has an energy level:
   2134 
   2135 #+begin_src emacs-lisp
   2136   (defun za/skip-if-has-energy-level ()
   2137     (let ((skip (save-excursion (org-end-of-subtree t)))
   2138           (keep nil)
   2139           (item-tags-without-inherited (let ((org-use-tag-inheritance nil)) (org-get-tags)))
   2140           (energy-tag-p (lambda (s) (member s '("sport" "cruise" "parked" "errand")))))
   2141       (if (cl-some energy-tag-p item-tags-without-inherited)
   2142           skip
   2143         keep)))
   2144 #+end_src
   2145 
   2146 #+begin_src emacs-lisp
   2147   (defun za/skip-if-scheduled-in-future ()
   2148     (let* ((skip (save-excursion (org-end-of-subtree t)))
   2149            (keep nil)
   2150            (scheduled-time (org-get-scheduled-time (point))))
   2151       (if (and scheduled-time (time-less-p (current-time) scheduled-time))
   2152           skip
   2153         keep)))
   2154 #+end_src
   2155 
   2156 Create custom agenda view based on those keywords.
   2157 Agenda views are made up of blocks, appearing in the order that you declare them.
   2158 The first two strings are what shows up in the agenda dispatcher (the key to press and the description).
   2159 
   2160 #+begin_src emacs-lisp
   2161   (setq org-agenda-custom-commands
   2162         '(("n" "Next actions"
   2163            todo "NEXT" ((org-agenda-overriding-header "Next actions:")
   2164                         (org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled 'deadline 'timestamp))
   2165                         (org-agenda-sorting-strategy '(priority-down alpha-up))))
   2166           ("q" "Query" (lambda (&rest _) (call-interactively #'org-ql-search)))
   2167 
   2168           ("W" "Waiting"
   2169            ((todo "WAITING" ((org-agenda-overriding-header "Waiting:")))))
   2170           ("S" . "Saved for later...")
   2171           ("Sw" "Saved to watch"
   2172            ((tags-todo "WATCH" ((org-agenda-overriding-header "To watch:")
   2173                                 (org-agenda-files `(,za/org-life-someday ,@org-agenda-files))))))
   2174 
   2175           ("Sr" "Saved to read"
   2176            ((tags-todo "READ" ((org-agenda-overriding-header "To read:")
   2177                                (org-agenda-files `(,za/org-life-someday ,@org-agenda-files))))))
   2178           ("Sl" "Saved to listen"
   2179            ((tags-todo "LISTEN" ((org-agenda-overriding-header "To listen:")
   2180                                  (org-agenda-files `(,za/org-life-someday ,@org-agenda-files))))))
   2181 
   2182           ("a" . "Agenda with schedule only...")
   2183           ("aw" "This week"
   2184            ((agenda "" ((org-agenda-span 'week)))))
   2185           ("aD" "Today"
   2186            ((agenda "" ((org-agenda-span 'day)))))
   2187           ("ad" "Today (no habits)"
   2188            ((agenda "" ((org-agenda-span 'day)
   2189                         (org-agenda-skip-function 'za/skip-if-has-habit-tag)))))
   2190           ("at" "Tomorrow (no habits)"
   2191            ((agenda "" ((org-agenda-span 'day)
   2192                         (org-agenda-start-day "+1d")
   2193                         (org-agenda-skip-function 'za/skip-if-has-habit-tag)))))
   2194           ("aT" "Tomorrow"
   2195            ((agenda "" ((org-agenda-span 'day)
   2196                         (org-agenda-start-day "+1d")))))
   2197 
   2198           ("w" "Week Agenda + Next Actions"
   2199            ((agenda "" ((org-agenda-overriding-header "Week agenda:")))
   2200             (todo "NEXT" ((org-agenda-overriding-header "Next actions:")))))
   2201 
   2202           ("o" "Month agenda"
   2203            ((agenda "" ((org-agenda-overriding-header "Month agenda:")
   2204                         (org-agenda-span 'month)))))
   2205 
   2206           ("d" "Day Agenda + Habits graph + Waiting"
   2207            ((agenda "" ((org-agenda-overriding-header "Day:")
   2208                         (org-agenda-span 'day)
   2209                         (org-habit-show-habits nil)
   2210                         (org-agenda-skip-function 'za/skip-if-has-habit-tag)))
   2211             (todo "STARTED" ((org-agenda-overriding-header "In progress:")))
   2212             (todo "WAITING" ((org-agenda-overriding-header "Waiting:")))
   2213             (agenda "" ((org-agenda-overriding-header "Habits:")
   2214                         (org-agenda-span 'day)
   2215                         (org-agenda-use-time-grid nil)
   2216                         (org-agenda-skip-function 'za/skip-unless-habit)
   2217                         (org-habit-show-habits t) (org-habit-show-habits-only-for-today nil)
   2218                         (org-habit-show-all-today t)))))
   2219           ("D" "Day Agenda with habit tags + Habits + Waiting"
   2220            ((agenda "" ((org-agenda-overriding-header "Day:")
   2221                         (org-agenda-span 'day)
   2222                         (org-habit-show-habits nil)))
   2223             (todo "STARTED" ((org-agenda-overriding-header "In progress:")))
   2224             (todo "WAITING" ((org-agenda-overriding-header "Waiting:")))
   2225             (agenda "" ((org-agenda-overriding-header "Habits:")
   2226                         (org-agenda-span 'day)
   2227                         (org-agenda-use-time-grid nil)
   2228                         (org-agenda-skip-function 'za/skip-unless-habit)
   2229                         (org-habit-show-habits t) (org-habit-show-habits-only-for-today nil)
   2230                         (org-habit-show-all-today t)))))
   2231 
   2232 
   2233           ("k" "Kanban view"
   2234            ((todo "DONE" ((org-agenda-overriding-header "Done:") (org-agenda-sorting-strategy '(deadline-up priority-down alpha-up))))
   2235             (todo "STARTED" ((org-agenda-overriding-header "In progress:") (org-agenda-sorting-strategy '(deadline-up priority-down alpha-up))))
   2236             (todo "NEXT" ((org-agenda-overriding-header "To do:") (org-agenda-sorting-strategy '(deadline-up priority-down alpha-up))))))
   2237 
   2238           ("p" "Projects"
   2239            ((todo "PROJ" ((org-agenda-overriding-header "Projects:")
   2240                           (org-agenda-prefix-format '((todo . " %i %-22(let ((deadline (org-entry-get nil \"DEADLINE\"))) (if deadline deadline \"\"))")))
   2241                           (org-agenda-dim-blocked-tasks nil)
   2242                           (org-agenda-sorting-strategy '((todo deadline-up alpha-down)))))))
   2243           ("1" "1-3-5"
   2244            ((tags "_1" ((org-agenda-overriding-header "Big tasks:")
   2245                         (org-agenda-skip-function 'za/skip-if-scheduled-in-future)
   2246                         (org-agenda-sorting-strategy '(todo-state-down deadline-up priority-down alpha-up))))
   2247             (tags "_3" ((org-agenda-overriding-header "Medium tasks:")
   2248                         (org-agenda-skip-function 'za/skip-if-scheduled-in-future)
   2249                         (org-agenda-sorting-strategy '(todo-state-down deadline-up priority-down alpha-up))))
   2250             (tags "_5" ((org-agenda-overriding-header "Small tasks:")
   2251                         (org-agenda-skip-function 'za/skip-if-scheduled-in-future)
   2252                         (org-agenda-sorting-strategy '(todo-state-down deadline-up priority-down alpha-up))))))
   2253 
   2254           ;; Useful thread for opening calfw: https://github.com/kiwanami/emacs-calfw/issues/18
   2255           ("c" "Calendar view" (lambda (&rest _)
   2256                                  (interactive)
   2257                                  (let ((org-agenda-skip-function 'za/skip-if-habit))
   2258                                    (cfw:open-org-calendar))))
   2259           ("f" . "Find & fix...")
   2260           ("f@" "Next actions missing context"
   2261            todo "NEXT" ((org-agenda-overriding-header "Missing context:")
   2262                         (org-agenda-sorting-strategy '(priority-down alpha-up))
   2263                         (org-agenda-skip-function 'za/skip-if-has-context)))
   2264           ("fe" "Next actions missing energy"
   2265            todo "NEXT" ((org-agenda-overriding-header "Missing energy level:")
   2266                         (org-agenda-sorting-strategy '(priority-down alpha-up))
   2267                         (org-agenda-skip-function 'za/skip-if-has-energy-level)))
   2268           ("ff" "Finished tasks that aren't in a project"
   2269            ((tags "TODO=\"DONE\"|TODO=\"CANCELLED\"" ((org-agenda-overriding-header "Finished tasks:")
   2270                                                       (org-agenda-skip-function 'za/skip-if-in-project)))))
   2271           ("ft" "Tasks without a scheduled time"
   2272            alltodo "" ((org-agenda-overriding-header "Missing scheduled time:")
   2273                        (org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled 'deadline 'timestamp))))))
   2274 #+end_src
   2275 
   2276 In calfw, I don't want to show habits:
   2277 
   2278 #+begin_src emacs-lisp
   2279   (add-hook 'cfw:calendar-mode-hook (setq-local org-agenda-skip-function 'za/skip-if-habit))
   2280 #+end_src
   2281 
   2282 *** Automatically mark next project item as NEXT
   2283 Unless the current item is a project, when a project item is done, the next item in the project should be marked "NEXT".
   2284 I tried org-edna but I couldn't get it working after an hour of effort. So a bit of lisp is the easier solution.
   2285 
   2286 #+begin_src emacs-lisp
   2287   (defun za/gtd-auto-next ()
   2288     "Automatically mark project item as next."
   2289     (save-excursion
   2290       (org-back-to-heading)
   2291       (when (buffer-narrowed-p)
   2292         (widen))
   2293       (when (and (member org-state org-done-keywords)
   2294                  (not (member "PROJECT" (org-get-tags nil 'local)))
   2295                  (member "PROJECT" (let ((org-use-tag-inheritance t))
   2296                                      (org-get-tags nil))))
   2297         (when (org-goto-sibling)
   2298           (org-entry-put (point) "TODO" "NEXT")))))
   2299 
   2300   (add-hook #'org-after-todo-state-change-hook #'za/gtd-auto-next)
   2301 #+end_src
   2302 
   2303 *** Logging for tasks
   2304 I want to log into the LOGBOOK drawer (useful when I want to take quick notes):
   2305 
   2306 #+begin_src emacs-lisp
   2307   (setq org-log-into-drawer "LOGBOOK")
   2308 #+end_src
   2309 
   2310 I also want to log when I finish a task (useful for archiving).
   2311 Furthermore, when I'm done, I want to add a note (any important
   2312 workarounds/tips). And when I reschedule, I want to know the reason.
   2313 I can disable logging on state change for a specific task by adding ~:LOGGING: nil~ to the ~:PROPERTIES:~ drawer.
   2314 
   2315 #+begin_src emacs-lisp
   2316   (setq org-log-done 'time
   2317         org-log-reschedule 'note)
   2318 #+end_src
   2319 
   2320 I want to hide drawers on startup. This variable has options:
   2321 - 'overview': Top-level headlines only.
   2322 - 'content': All headlines.
   2323 - 'showall': No folding on any entry.
   2324 - 'show2levels: Headline levels 1-2.
   2325 - 'show3levels: Headline levels 1-3.
   2326 - 'show4levels: Headline levels 1-4.
   2327 - 'show5levels: Headline levels 1-5.
   2328 - 'showeverything: Show even drawer contents.
   2329 
   2330 #+begin_src emacs-lisp
   2331   (setq org-startup-folded 'content)
   2332 #+end_src
   2333 
   2334 *** Task ordering
   2335 Some tasks should be ordered, i.e. they should be done in steps.
   2336 Those have the ~:ORDERED: t~ setting in ~:PROPERTIES:~, and it should be enforced:
   2337 
   2338 #+begin_src emacs-lisp
   2339   (setq org-enforce-todo-dependencies t)
   2340 #+end_src
   2341 
   2342 Furthermore, tasks that are ordered and can't be done yet because of previous steps should be dimmed in the agenda:
   2343 
   2344 #+begin_src emacs-lisp
   2345   (setq org-agenda-dim-blocked-tasks t)
   2346 #+end_src
   2347 
   2348 I might also want to set ~org-enforce-todo-checkbox-dependencies~, but not convinced on that one yet.
   2349 
   2350 *** Time tracking & effort
   2351 Time tracking should be done in its own drawer:
   2352 
   2353 #+begin_src emacs-lisp
   2354   (setq org-clock-into-drawer "CLOCK")
   2355 #+end_src
   2356 
   2357 And to customize how clock tables work:
   2358 
   2359 #+begin_src emacs-lisp
   2360   (setq org-clocktable-defaults '(:lang "en" :scope agenda-with-archives  :wstart 1 :mstart 1 :compact t :maxlevel nil))
   2361   (setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel nil))
   2362 #+end_src
   2363 
   2364 I want to set effort in hours:minutes:
   2365 
   2366 #+begin_src emacs-lisp
   2367   (add-to-list 'org-global-properties '("Effort_ALL" . "0:05 0:10 0:15 0:20 0:30 0:45 1:00 1:30 2:00 4:00 6:00 8:00"))
   2368 #+end_src
   2369 
   2370 I want column view to look like this:
   2371 
   2372 | To do        | Task      | Tags | Sum of time elapsed | Sum of time estimated (effort) |
   2373 |--------------+-----------+------+---------------------+--------------------------------|
   2374 | todo keyword | task name | tags | sum of clock        | sum of estimated time          |
   2375 | ...          | ...       | ...  | ...                 | ...                            |
   2376 
   2377 #+begin_src emacs-lisp
   2378   (setq org-columns-default-format "%7TODO (To Do) %32ITEM(Task) %TAGS(Tags) %11CLOCKSUM_T(Clock) %10Difficulty(Difficulty) %8Effort(Effort){:}")
   2379 #+end_src
   2380 
   2381 Fix column alignment in agenda.
   2382 
   2383 #+begin_src emacs-lisp
   2384   (set-face-attribute 'org-column nil
   2385                       :height (face-attribute 'default :height)
   2386                       :family (face-attribute 'default :family))
   2387   (set-face-attribute 'org-agenda-date-today nil
   2388                       :height (face-attribute 'default :height))
   2389 #+end_src
   2390 
   2391 *** Calculate time since timestamp
   2392 #+begin_src emacs-lisp
   2393   (defun za/org-time-since ()
   2394     "Print the amount of time between the timestamp at point and the current date and time."
   2395     (interactive)
   2396     (unless (org-at-timestamp-p 'lax)
   2397       (user-error "Not at timestamp"))
   2398 
   2399     (when (org-at-timestamp-p 'lax)
   2400       (let ((timestamp (match-string 0)))
   2401         (with-temp-buffer
   2402           (insert timestamp
   2403                   "--"
   2404                   (org-time-stamp '(16)))
   2405           (org-evaluate-time-range)))))
   2406 #+end_src
   2407 
   2408 Also a method to add overlays with that timestamp:
   2409 
   2410 #+begin_src emacs-lisp
   2411   (defvar-local za/org-timestamp-overlays--list nil "Buffer-local list of overlays with timestamps")
   2412   (defvar-local za/org-timestamp-overlays--show nil "Buffer-local boolean to show overlays.")
   2413   (defun za/org-timestamp-overlays-clear ()
   2414     "Clear all overlays with timestamps in current buffer."
   2415     (dolist (ov za/org-timestamp-overlays--list)
   2416       (delete-overlay ov))
   2417     (setq-local za/org-timestamp-overlays--list nil))
   2418 
   2419   (defun za/org-timestamp-overlays-add ()
   2420     "Add overlays for active timestamps in current buffer."
   2421     (let ((markup-string (lambda (s) (propertize (format "{%s}" s)
   2422                                                  'face 'org-habit-ready-future-face))))
   2423       (save-excursion
   2424         (let* ((beg (point-min))
   2425                (end (point-max)))
   2426           (goto-char beg)
   2427           (while (re-search-forward (org-re-timestamp 'active) end t)
   2428             (let ((ov (make-overlay (point) (point))))
   2429               (overlay-put ov 'before-string (funcall markup-string (za/org-time-since)))
   2430               (add-to-list 'za/org-timestamp-overlays--list ov)))))))
   2431 
   2432   (defun za/org-timestamp-overlays-redraw ()
   2433     "Redraw all overlays for active timestamps."
   2434     (za/org-timestamp-overlays-clear)
   2435     (za/org-timestamp-overlays-add))
   2436 
   2437   (defun za/org-timestamp-hook-fn (&rest _)
   2438     (za/org-timestamp-overlays-redraw))
   2439 
   2440   (bind-key "C-c q p" #'tmp/p)
   2441   (defun za/org-timestamp-overlays-toggle (&optional prefix)
   2442     "With no prefix, toggle showing timestamp overlay.
   2443   With PREFIX = 0, redraw overlays.
   2444   With PREFIX > 0, show overlays.
   2445   With PREFIX < 0, hide overlays."
   2446     (interactive "P")
   2447     (let ((overlays-hide (lambda ()
   2448                            (za/org-timestamp-overlays-clear)
   2449                            (remove-hook 'org-cycle-hook #'za/org-timestamp-hook-fn)
   2450                            (setq za/org-timestamp-overlays--show nil)
   2451                            (message "Overlays hidden.")))
   2452           (overlays-show (lambda ()
   2453                            (za/org-timestamp-overlays-redraw)
   2454                            (add-hook 'org-cycle-hook #'za/org-timestamp-hook-fn)
   2455                            (setq za/org-timestamp-overlays--show t)
   2456                            (message "Overlays showing.")))
   2457           (overlays-redraw-maybe (lambda ()
   2458                                    (when za/org-timestamp-overlays--show
   2459                                      (za/org-timestamp-overlays-redraw)
   2460                                      (message "Redrawing overlays."))))
   2461           (prefix-num (prefix-numeric-value prefix)))
   2462       (cond ((not prefix)
   2463              (cond (za/org-timestamp-overlays--show
   2464                     (funcall overlays-hide))
   2465                    (t
   2466                     (funcall overlays-show))))
   2467             ((zerop prefix-num)
   2468              )
   2469             ((> prefix-num 0)
   2470              (funcall overlays-show))
   2471             ((< prefix-num 0)
   2472              (funcall overlays-hide)))))
   2473 
   2474 #+end_src
   2475 
   2476 Bind a key:
   2477 
   2478 #+begin_src emacs-lisp
   2479   (bind-key "C-c q d" #'za/org-timestamp-overlays-toggle 'org-mode-map)
   2480   (bind-key "C-c q d" #'za/org-timestamp-overlays-toggle 'org-agenda-mode-map)
   2481 #+end_src
   2482 *** Priorities: how important something is
   2483 I usually have a lot of 'next' actions, so I prefer 4 priority levels instead of the default 3: A (urgent, ASAP), B (important),  C (if you have nothing else, do this), D (do in free time):
   2484 
   2485 #+begin_src emacs-lisp
   2486   (setq org-priority-highest ?A
   2487         org-priority-lowest ?D
   2488         org-priority-default ?C)
   2489 #+end_src
   2490 
   2491 Faces for priorities in agenda:
   2492 
   2493 #+begin_src emacs-lisp
   2494   (setq org-priority-faces `((?A . (:foreground ,(face-foreground 'error)))
   2495                              (?B . (:foreground ,(face-foreground 'org-todo)))
   2496                              (?C . (:foreground ,(face-foreground 'font-lock-constant-face) :weight semi-light))
   2497                              (?D . (:foreground ,(face-foreground 'font-lock-string-face) :slant italic :weight light))))
   2498 #+end_src
   2499 
   2500 And to be able to bulk-set priorities in agenda:
   2501 
   2502 #+begin_src emacs-lisp
   2503   (setq org-agenda-bulk-custom-functions '((?P (lambda nil (org-agenda-priority 'set)))))
   2504 #+end_src
   2505 *** Energy requirement: how difficult something is
   2506 #+begin_src emacs-lisp
   2507   (add-to-list 'org-global-properties '("Difficulty_ALL" . "low medium high"))
   2508 #+end_src
   2509 *** Org export backends
   2510 #+begin_src emacs-lisp
   2511   (use-package ox-pandoc)
   2512 #+end_src
   2513 
   2514 *** org publishing
   2515 I decided, after trying many different things, to settle on org-publish.
   2516 
   2517 #+begin_src emacs-lisp
   2518   (defconst za/org-roam-top-name "Top" "The name of the top-level Org-roam node.")
   2519   (defun za/org-roam-sitemap-function (title list)
   2520     "Customized function to generate sitemap for org-roam, almost the same as `org-publish-sitemap-default`."
   2521     (concat "#+TITLE: " title "\n\n"
   2522             (format "[[file:%s][%s]]\n\n"
   2523                     (file-name-nondirectory (org-roam-node-file
   2524                                              (org-roam-node-from-title-or-alias za/org-roam-top-name)))
   2525                     "Click here for entrypoint.")))
   2526   ;; (org-list-to-org list)))  <-- this is taken care of by Zola
   2527 
   2528 #+end_src
   2529 
   2530 To make this work with Zola, I need to export Github-flavored markdown (fenced code blocks with language):
   2531 
   2532 #+begin_src emacs-lisp
   2533   (require 'ox-publish)
   2534   (require 'ox-md)
   2535 
   2536   (use-package ox-gfm
   2537     :init
   2538     (with-eval-after-load 'org (require 'ox-gfm)))
   2539 #+end_src
   2540 
   2541 First difficulty: Zola needs front matter with ~+++...+++~.
   2542 The default Markdown backend doesn't provide that, so need to customize it by advising the default ~org-md-template~.
   2543 
   2544 #+begin_src emacs-lisp
   2545   (defun za/org-md-template-zola (contents info)
   2546     "Markdown template compatible with Zola (generates the necessary front matter from CONTENTS and INFO)."
   2547     (let ((title (org-md-plain-text (org-element-interpret-data (plist-get info :title)) info)))
   2548       (concat "+++\n"
   2549               (format "title = \"%s\"\n" (string-replace "\"" "'" title))
   2550 
   2551               ;; If the note contains a math org-roam tag
   2552               (when (member "math" (plist-get info :filetags))
   2553                 "template = \"page-math.html\"\n")
   2554 
   2555               "+++\n"
   2556               (format "# %s\n" title)
   2557               contents)))
   2558 #+end_src
   2559 
   2560 Second difficulty: links need to be reformatted and changed for static data (like images).
   2561 This function filters the return value of ~org-md-link~.
   2562 
   2563 #+begin_src emacs-lisp
   2564   (defun za/org-md-link-zola (linkstr)
   2565     "A filter function for the return value of
   2566           `org-md-link` (LINKSTR) to generate a link compatible with Zola."
   2567     (cond ((string-match-p (rx ".md") linkstr)
   2568            (string-replace "](" "](@/org-roam/" linkstr))
   2569           ((string-match-p (rx "](" (? (* alnum) "://") "/") linkstr)
   2570            (replace-regexp-in-string (rx "](" (? (* alnum) "://") "/" (* any) "/org-roam/data") "](/org-roam-data" linkstr))
   2571           (t linkstr)))
   2572 #+end_src
   2573 
   2574 A wrapper to set the right image link:
   2575 
   2576 #+begin_src emacs-lisp
   2577   (defun za/org-html--format-image (args)
   2578     "Modify source image link to work with my Org roam setup"
   2579     (let ((source (nth 0 args))
   2580           (_attributes (nth 1 args))
   2581           (_info (nth 2 args)))
   2582       (list (replace-regexp-in-string (rx bos "data/") "/org-roam-data/" source)
   2583             _attributes
   2584             _info)))
   2585 #+end_src
   2586 
   2587 And here's the custom publish function that adds/removes the necessary advice:
   2588 
   2589 #+begin_src emacs-lisp
   2590   (defun za/org-gfm-publish-to-gfm-zola (plist filename pub-dir)
   2591     "Run `org-gfm-publish-to-gfm`, advising the necessary
   2592   functions to generate Zola-compatible markdown."
   2593     (let* ((org-export-output-file-name-locked (lambda (extension &rest _)
   2594                                                  (concat (plist-get plist :publishing-directory)
   2595                                                          "locked-"
   2596                                                          (file-name-base filename)
   2597                                                          extension)))
   2598            (node (car (seq-filter
   2599                        (lambda (node) (file-equal-p (org-roam-node-file node) filename))
   2600                        (org-roam-node-list))))
   2601            (locked-p (cond ((file-equal-p filename
   2602                                           (file-name-concat (plist-get plist :base-directory) (plist-get plist :sitemap-filename)))
   2603                             nil)
   2604                            (t
   2605                             (member "locked" (org-roam-node-tags node)))))
   2606            (advice '((org-gfm-inner-template :override za/org-md-template-zola)
   2607                      (org-md-link :filter-return za/org-md-link-zola)
   2608                      (org-html--format-image :filter-args za/org-html--format-image)
   2609                      (org-gfm-table :override org-md--convert-to-html)))) ; Zola uses CommonMark, so doesn't support Markdown tables
   2610 
   2611       (dolist (orig-type-new advice) (apply #'advice-add orig-type-new))
   2612       (unwind-protect
   2613           (cond (locked-p
   2614                  (advice-add #'org-export-output-file-name :override org-export-output-file-name-locked)
   2615                  (unwind-protect
   2616                      (org-gfm-publish-to-gfm plist filename pub-dir)
   2617                    (advice-remove #'org-export-output-file-name org-export-output-file-name-locked)))
   2618                 (t
   2619                  (org-gfm-publish-to-gfm plist filename pub-dir)))
   2620         (dolist (orig-type-new advice)
   2621           (advice-remove (nth 0 orig-type-new)
   2622                          (nth 2 orig-type-new))))))
   2623 #+end_src
   2624 
   2625 Finally, the list of things we can publish with their respective publishin functions:
   2626 
   2627 #+begin_src emacs-lisp
   2628   (if (boundp 'za/my-website-dir)
   2629       (setq org-publish-project-alist
   2630             `(
   2631               ("org-notes"
   2632                :base-directory ,za/org-roam-dir
   2633                :base-extension "org"
   2634                :publishing-directory ,(concat za/my-website-dir "content/org-roam/")
   2635                :publishing-function za/org-gfm-publish-to-gfm-zola
   2636                :recursive t
   2637                :sitemap-filename "_index.md"
   2638                :sitemap-title "Org Roam"
   2639                :sitemap-function za/org-roam-sitemap-function
   2640                :auto-sitemap t)
   2641 
   2642               ("org-notes-data"
   2643                :base-directory ,(concat za/org-roam-dir "/data")
   2644                :base-extension any
   2645                :publishing-directory ,(concat za/my-website-dir "static/org-roam-data/")
   2646                :recursive t
   2647                :publishing-function org-publish-attachment)
   2648 
   2649               ("org-roam" :components ("org-notes" "org-notes-data"))))
   2650     (warn "za/my-website-dir not bound, not setting org publishing targets."))
   2651 #+end_src
   2652 
   2653 And a function to rsync to my VPS:
   2654 
   2655 #+begin_src emacs-lisp
   2656   (defun za/publish-upload-to-website ()
   2657     "Upload my website to my VPS"
   2658     (interactive)
   2659     (async-shell-command (format "cd %s && zola build && yes|publish" za/my-website-dir) "*Async Shell publish*"))
   2660 #+end_src
   2661 *** Rebuild org cache
   2662 
   2663 #+begin_src emacs-lisp
   2664   (defun za/force-org-rebuild-cache ()
   2665     "Rebuild the `org-mode' and `org-roam' cache."
   2666     (interactive)
   2667     (org-id-update-id-locations)
   2668     ;; Note: you may need `org-roam-db-clear-all'
   2669     ;; followed by `org-roam-db-sync'
   2670     (org-roam-db-sync)
   2671     (org-roam-update-org-id-locations))
   2672 #+end_src
   2673 *** Sync with Flatastic
   2674 API work is handled via an external ruby script.
   2675 
   2676 #+begin_src emacs-lisp
   2677   (defun za/org-flatastic-sync-tasks ()
   2678     "Add tasks from flatastic to inbox"
   2679     (interactive)
   2680     (unless (json-available-p)
   2681       (user-error "JSON not available"))
   2682     (unless (boundp 'za/org-life-inbox)
   2683       (user-error "Please set za/org-life-inbox"))
   2684     (let* ((api-data (json-parse-string
   2685                       (progn
   2686                         (require 'exec-path-from-shell)
   2687                         (exec-path-from-shell-copy-envs
   2688                          '("FLATASTIC_API_KEY" "FLATASTIC_USER_ID"))
   2689                         (shell-command-to-string "~/.local/share/rbenv/shims/ruby ~/.scripts/flatastic.rb"))
   2690                       :object-type 'alist))
   2691            (format-data-as-org (lambda (l)
   2692                                  (format "* TODO %s :flatastic:\n  SCHEDULED: <%s>\n  Points: %d\n"
   2693                                          (alist-get 'description l)
   2694                                          (alist-get 'scheduled_due_date l)
   2695                                          (alist-get 'point_value l))))
   2696            (org-flatastic-items (mapcar format-data-as-org api-data)))
   2697       (with-current-buffer (find-file-noselect za/org-life-inbox)
   2698         (goto-char (point-max))
   2699         (insert "\n" (string-join org-flatastic-items "\n")))
   2700       (message "Synced %d Flatastic tasks to inbox" (length api-data))))
   2701 #+end_src
   2702 *** Link to Thunderbird messages
   2703 Create a custom link to open thunderbird emails by ID:
   2704 
   2705 #+begin_src emacs-lisp
   2706   (org-link-set-parameters
   2707    "thunderbird"
   2708    :follow #'za/org-link-thunderbird-follow)
   2709 
   2710   (defun za/org-link-thunderbird-follow (messageid)
   2711     "Open the message with id `messageid` in Thunderbird"
   2712     (shell-command (format "thunderbird mid:%s" (shell-quote-argument messageid))))
   2713 #+end_src
   2714 *** Inverse refile
   2715 #+begin_src emacs-lisp
   2716   (defun za/org-refile-to-point (refloc)
   2717     "Prompt for a heading and refile it to point."
   2718     (interactive (list (org-refile-get-location "Heading: ")))
   2719     (let* ((file (nth 1 refloc))
   2720            (pos (nth 3 refloc)))
   2721       (save-excursion
   2722         (with-current-buffer (find-file-noselect file 'noward)
   2723           (save-excursion
   2724             (save-restriction
   2725               (widen)
   2726               (goto-char pos)
   2727               (org-copy-subtree 1 t))))
   2728         (org-paste-subtree nil nil nil t))))
   2729 
   2730 
   2731   (defun za/org-refile-wrapper (arg)
   2732     "Wrap org-refile so that it does the inverse with a negative argument"
   2733     (interactive "P")
   2734     (if (minusp (prefix-numeric-value arg))
   2735         (call-interactively #'za/org-refile-to-point)
   2736       (org-refile arg)))
   2737 
   2738 #+end_src
   2739 
   2740 *** org-caldav
   2741 This lets me sync my Org agenda to my CalDAV server.
   2742 The main reason is because Orgzly doesn't have a calendar view and can't (yet) search for events on a specific day, so if someone asks "are you free on that day", it's a bit hard for me to answer if I don't have my computer with me.
   2743 This way, I can just check my calendar.
   2744 
   2745 #+begin_src emacs-lisp
   2746   (if (and (boundp 'za/caldav-url)
   2747            (boundp 'za/caldav-org-calendar-id)
   2748            (boundp 'za/org-life-calendar-inbox))
   2749       (use-package org-caldav
   2750         :init
   2751         (defconst za/org-life-calendar-inbox (concat za/org-life-dir "calendar-inbox.org"))
   2752         :custom
   2753         (org-caldav-url za/caldav-url)
   2754         (org-caldav-calendar-id za/caldav-org-calendar-id)
   2755         (org-caldav-inbox za/org-life-calendar-inbox)
   2756         (org-caldav-files (cons (car (split-string org-archive-location "::")) org-agenda-files))
   2757         (org-caldav-sync-todo nil)
   2758         (org-icalendar-include-todo nil)
   2759         (org-icalendar-use-deadline '(event-if-todo event-if-not-todo todo-due))
   2760         (org-icalendar-use-scheduled '(todo-start event-if-todo event-if-not-todo))
   2761         (org-caldav-exclude-tags '("HABIT")
   2762                                  "I don't want to export habits, because those will just clutter up my calendar. The calendar is supposed to be for one-off stuff, or rarely repeating stuff. Yes, I have to manually add the HABIT tag to every habit. Perhaps nicer would be to exclude based on the property ~:STYLE: habit~, but I haven't figured that one out yet.")
   2763         (org-caldav-todo-percent-states '((0 "TODO")
   2764                                           (0 "WAITING")
   2765                                           (1 "NEXT")
   2766                                           (2 "STARTED")
   2767                                           (0 "PROJ")
   2768                                           (100 "DONE")
   2769                                           (100 "CANCELLED")))
   2770         :config
   2771         (defun za/caldav-after-sync-notify () (za/notify "org-caldav sync complete" "Finished syncing"))
   2772         (advice-add #'org-caldav-sync :after #'za/caldav-after-sync-notify)
   2773         (advice-add #'org-caldav-sync :around #'za/notify-on-interactivity))
   2774     (warn "za/caldav-url, za/caldav-org-calendar-id, za/org-life-calendar-inbox not bound, not using org-caldav."))
   2775 #+end_src
   2776 
   2777 Maybe check [[https://old.reddit.com/r/orgmode/comments/8rl8ep/making_orgcaldav_useable/e0sb5j0/][this]] for a way to sync on save.
   2778 
   2779 *** org-ref
   2780 #+begin_src emacs-lisp
   2781   (use-package org-ref)
   2782 #+end_src
   2783 *** org-roam
   2784 #+begin_src emacs-lisp
   2785   (use-package org-roam
   2786     :custom
   2787     (org-roam-directory za/org-roam-dir)
   2788     (org-roam-completion-everywhere t)
   2789     (org-roam-dailies-capture-templates
   2790      '(("d" "default" entry
   2791         "* %U\n%?"
   2792         :target (file+head "%<%Y-%m-%d>.org"
   2793                            "#+title: %<%Y-%m-%d>\n"))))
   2794     :config
   2795                                           ; can't use nil because org-roam-ui checks for boundp on this and
   2796                                           ; errors if bound but nil.
   2797     (with-eval-after-load 'org-roam-dailies
   2798       (makunbound 'org-roam-dailies-directory))
   2799     (defun za/org-roam-dailies-goto-latest-note ()
   2800       (interactive)
   2801       (unless (boundp 'org-roam-dailies-directory)
   2802         (za/org-roam-dailies-select-dir))
   2803       (let* ((dailies (org-roam-dailies--list-files))
   2804              (latest-note (car (last dailies))))
   2805         (unless latest-note
   2806           (user-error "Can't find latest note"))
   2807         (find-file latest-note)
   2808         (run-hooks 'org-roam-dailies-find-file-hook)))
   2809     (org-roam-setup)
   2810     (bind-keys :prefix "C-c w"
   2811                :prefix-map za/org-roam-map
   2812                :prefix-docstring "Org roam"
   2813                ("n" . org-roam-capture)
   2814                ("f" . org-roam-node-find)
   2815                ("w" . org-roam-buffer-toggle)
   2816                ("i" . org-roam-node-insert))
   2817     (bind-keys :prefix "C-c j"
   2818                :prefix-map za/org-roam-dailies-map
   2819                :prefix-docstring "Org roam dailies"
   2820                ("s" . za/org-roam-dailies-select-dir)
   2821                ("n" . org-roam-dailies-capture-today)
   2822                ("j" . org-roam-dailies-goto-today)
   2823                ("+" . org-roam-dailies-goto-tomorrow)
   2824                (">" . org-roam-dailies-goto-next-note)
   2825                ("-" . org-roam-dailies-goto-yesterday)
   2826                ("<" . org-roam-dailies-goto-previous-note)
   2827                ("g" . org-roam-dailies-goto-date)
   2828                ("l" . za/org-roam-dailies-goto-latest-note)
   2829                ("." . org-roam-dailies-find-directory))
   2830 
   2831     (defun za/org-roam-dailies--daily-note-p (&optional file)
   2832       "Replacement of default function. Return t if FILE is an Org-roam daily-note, nil otherwise.
   2833   If FILE is not specified, use the current buffer's file-path."
   2834       (when-let ((path (expand-file-name
   2835                         (or file
   2836                             (buffer-file-name (buffer-base-buffer)))))
   2837                  (directory (expand-file-name org-roam-dailies-directory org-roam-directory)))
   2838         (setq path (expand-file-name path))
   2839         (save-match-data
   2840           (and
   2841            ;; (org-roam-file-p path) ; don't want this, dailies might not be in org-roam path
   2842            (org-roam-descendant-of-p path directory)))))
   2843     (advice-add #'org-roam-dailies--daily-note-p :override #'za/org-roam-dailies--daily-note-p)
   2844 
   2845     (defun za/org-roam-dailies-select-dir ()
   2846       "Select an org-roam-dailies folder."
   2847       (interactive)
   2848       (let* ((choices (cons '(?0 nil) za/org-roam-dailies-dirs))
   2849              (choice (nth 1 (read-multiple-choice "org-roam-dailies dir" choices))))
   2850         (if choice
   2851             (progn (setq org-roam-dailies-directory choice)
   2852                    (message "Selected org-roam-dailies directory: %s" org-roam-dailies-directory))
   2853           (makunbound 'org-roam-dailies-directory))))
   2854 
   2855     (defun za/org-roam-dailies-calendar-mark-entries-p ()
   2856       "Only mark dailies entries in calendar if a dailies directory is set."
   2857       (boundp 'org-roam-dailies-directory))
   2858     (advice-add #'org-roam-dailies-calendar-mark-entries :before-while #'za/org-roam-dailies-calendar-mark-entries-p)
   2859 
   2860     ;; Before doing anything journal-related, check that a journal is
   2861     ;; selected, or prompt for one.
   2862     (defun za/org-roam-dailies--capture-check-non-nil-dailies-dir (&rest _)
   2863       (unless (boundp 'org-roam-dailies-directory)
   2864         (za/org-roam-dailies-select-dir))
   2865       (unless (boundp 'org-roam-dailies-directory)
   2866         (user-error "No org-roam-dailies-directory selected!")))
   2867 
   2868     (advice-add #'org-roam-dailies--capture :before #'za/org-roam-dailies--capture-check-non-nil-dailies-dir)
   2869     (advice-add #'org-roam-dailies-goto-date :before #'za/org-roam-dailies--capture-check-non-nil-dailies-dir)
   2870     (require 'org-roam-export))
   2871 #+end_src
   2872 
   2873 *** org-roam-ui
   2874 #+begin_src emacs-lisp
   2875   (use-package org-roam-ui)
   2876 #+end_src
   2877 *** org-download
   2878 Drag-and-drop images to Emacs Org mode.
   2879 
   2880 #+begin_src emacs-lisp
   2881   (use-package org-download
   2882     :custom
   2883     (org-download-method 'attach)
   2884     (org-download-backend t))
   2885 #+end_src
   2886 
   2887 *** org-sticky-header
   2888 Displays in the header-line the Org heading for the node that’s at the top of the window.
   2889 
   2890 #+begin_src emacs-lisp
   2891   (use-package org-sticky-header)
   2892 #+end_src
   2893 *** org-timestone
   2894 #+begin_src emacs-lisp
   2895   (use-package org-timestone
   2896     :init (za/package-vc-install :repo "thezeroalpha/org-timestone.el")
   2897     :ensure nil
   2898     :after org
   2899     :bind (:map org-mode-map
   2900                 ("C-c C-t" . org-timestone-org-todo-wrapper)))
   2901 #+end_src
   2902 *** org-noter
   2903 #+begin_src emacs-lisp
   2904   (use-package org-noter
   2905     :config
   2906     ;; Fix disabling of line wrap by no-opping set-notes-scroll
   2907     (advice-add 'org-noter--set-notes-scroll :override 'za/no-op))
   2908 #+end_src
   2909 *** el-easydraw
   2910 Lets you draw stuff in org mode documents.
   2911 
   2912 #+begin_src emacs-lisp :tangle no
   2913   (za/package-vc-install :repo "misohena/el-easydraw" :name "edraw")
   2914   (with-eval-after-load 'org
   2915     (require 'edraw-org)
   2916     (edraw-org-setup-default)
   2917     (bind-key "C-c q c" #'edraw-color-picker-insert-color))
   2918 #+end_src
   2919 *** ox-jira
   2920 #+begin_src emacs-lisp
   2921   (use-package ox-jira)
   2922 #+end_src
   2923 *** org-confluence
   2924 ox-confluence with some custom code to remove the theme & create expandable drawers.
   2925 Add to confluence by pressing ~ctrl + shift + d~ when editing a page and inserting confluence wiki text.
   2926 
   2927 #+begin_src emacs-lisp
   2928   (require 'ox-confluence)
   2929   (org-export-define-derived-backend 'confluence-ext 'confluence
   2930     :translate-alist '((drawer . za/org-confluence-drawer))
   2931     :filters-alist '((:filter-src-block . za/org-confluence--code-block-remove-theme))
   2932     :menu-entry
   2933     '(?F "Export to Confluence (ext)"
   2934          ((?F "As Confluence buffer (ext)" za/org-confluence-export-as-confluence))))
   2935 
   2936   (defun za/org-confluence-export-as-confluence
   2937       (&optional async subtreep visible-only body-only ext-plist)
   2938     (interactive)
   2939     (org-export-to-buffer 'confluence-ext "*org CONFLUENCE Export*"
   2940       async subtreep visible-only body-only ext-plist (lambda () (text-mode))))
   2941 
   2942   (defun za/org-confluence--code-block-remove-theme (block _backend _info)
   2943     "Remove the theme from the block"
   2944     (replace-regexp-in-string (rx "\{code:theme=Emacs" (? "|")) "\{code:" block))
   2945 
   2946 
   2947   (defun za/org-confluence-drawer (drawer contents info)
   2948     "Handle custom drawers"
   2949     (let* ((name (org-element-property :drawer-name drawer)))
   2950       (concat
   2951        (format "\{expand:%s\}\n" name)
   2952        contents
   2953        "\{expand\}")))
   2954 #+end_src
   2955 *** TODO the path for org-roam export and data export should be configurable, not hard-coded
   2956 
   2957 ** Mail mode for neomutt
   2958 When editing a message from neomutt, I want to use mail mode.
   2959 Even though I won't be sending the email from there, I like the syntax highlighting :)
   2960 
   2961 #+begin_src emacs-lisp
   2962   (add-to-list 'auto-mode-alist '("/neomutt-" . mail-mode))
   2963 #+end_src
   2964 ** DISABLED Semantic mode
   2965 Disabled for now, don't use it much.
   2966 SemanticDB is written into ~/.emacs.d/semanticdb/.
   2967 
   2968 #+begin_src emacs-lisp :tangle no
   2969   (use-package semantic
   2970     :bind (:map semantic-mode-map
   2971                 ("C-c , ." . semantic-ia-show-summary))
   2972     :custom
   2973     (semantic-default-submodes '(global-semantic-idle-scheduler-mode ; reparse buffer when idle
   2974                                  global-semanticdb-minor-mode ; maintain database
   2975                                  global-semantic-idle-summary-mode  ; show information (e.g. types) about tag at point
   2976                                  global-semantic-stickyfunc-mode))) ; show current func in header line
   2977 
   2978 
   2979 #+end_src
   2980 
   2981 ** Bib(la)tex
   2982 #+begin_src emacs-lisp
   2983   (use-package bibtex
   2984     :config
   2985     (bibtex-set-dialect "biblatex"))
   2986 #+end_src
   2987 
   2988 ** Python
   2989 In Python, I want to enable flycheck and semantic mode:
   2990 
   2991 #+begin_src emacs-lisp
   2992   (add-hook 'python-mode-hook #'flycheck-mode)
   2993   ;;(add-hook 'python-mode-hook #'semantic-mode)
   2994 #+end_src
   2995 
   2996 ** Elisp
   2997 #+begin_src emacs-lisp
   2998   (use-package emacs-lisp
   2999     :ensure nil ; preinstalled
   3000     :hook ((emacs-lisp-mode . flycheck-mode)
   3001            (emacs-lisp-mode . rainbow-mode)
   3002            (emacs-lisp-mode . outline-minor-mode)
   3003            (emacs-lisp-mode . company-mode)))
   3004 #+end_src
   3005 ** lean-mode
   3006 Specifically for the Lean prover.
   3007 I also install company-lean and helm-lean, which are suggested on the [[https://github.com/leanprover/lean-mode][Github page]].
   3008 Then I map company-complete only for lean-mode.
   3009 
   3010 #+begin_src emacs-lisp
   3011   (use-package lean-mode
   3012     :config
   3013     (use-package company-lean)
   3014     :bind (:map lean-mode-map
   3015                 ("S-SPC" . company-complete)))
   3016 #+end_src
   3017 
   3018 ** sh-mode
   3019 #+begin_src emacs-lisp :results value
   3020   (use-package sh-script
   3021     :hook (sh-mode . flycheck-mode))
   3022 #+end_src
   3023 
   3024 ** anki-editor
   3025 Some extra keybindings that are not set up by default.
   3026 anki-editor doesn't provide a keymap so I have to set one up here:
   3027 
   3028 #+begin_src emacs-lisp
   3029   (use-package anki-editor
   3030     :init
   3031     (defvar anki-editor-mode-map (make-sparse-keymap))
   3032     (add-to-list 'minor-mode-map-alist (cons 'anki-editor-mode
   3033                                              anki-editor-mode-map))
   3034     :custom
   3035     (anki-editor-use-math-jax t)
   3036 
   3037     :bind (:map anki-editor-mode-map
   3038                 ("C-c t" . org-property-next-allowed-value)
   3039                 ("C-c i" . anki-editor-insert-note)
   3040                 ("C-c p" . anki-editor-push-notes)
   3041                 ("C-c c" . anki-editor-cloze-dwim)))
   3042 #+end_src
   3043 ** pdf-tools
   3044 A better replacement for DocView:
   3045 
   3046 #+begin_src emacs-lisp
   3047   (use-package pdf-tools
   3048     :init
   3049     (pdf-tools-install)
   3050 
   3051     :custom
   3052     (pdf-annot-default-annotation-properties '((t
   3053                                                 (label . "Alex Balgavy"))
   3054                                                (text
   3055                                                 (icon . "Note")
   3056                                                 (color . "#0088ff"))
   3057                                                (highlight
   3058                                                 (color . "yellow"))
   3059                                                (squiggly
   3060                                                 (color . "orange"))
   3061                                                (strike-out
   3062                                                 (color . "red"))
   3063                                                (underline
   3064                                                 (color . "blue"))))
   3065     :bind (:map pdf-isearch-minor-mode-map
   3066                 ("C-s" . isearch-forward)
   3067                 :map pdf-view-mode-map
   3068                 ;; Save position & jump back
   3069                 ("C-SPC" . (lambda () (interactive) (message "Position saved") (pdf-view-position-to-register ?x)))
   3070                 ("C-u C-SPC" . (lambda () (interactive) (pdf-view-jump-to-register ?x))))
   3071     :hook
   3072     (pdf-annot-list-mode . pdf-annot-list-follow-minor-mode)
   3073     (pdf-annot-edit-contents-minor-mode . org-mode)
   3074     (pdf-view-mode . (lambda () (display-line-numbers-mode 0)))
   3075 
   3076     :config
   3077     ;; The arrow tooltip does not show properly when jumping to a
   3078     ;; location. Maybe this is a Mac-only thing. See here:
   3079     ;; https://github.com/politza/pdf-tools/issues/145
   3080     ;; This ~:override~ advice fixes it, color is customized via ~tooltip~ face
   3081     (advice-add #'pdf-util-tooltip-arrow :override #'za/pdf-util-tooltip-arrow)
   3082     (defun za/pdf-util-tooltip-arrow (image-top &optional timeout)
   3083       "Fix up `pdf-util-tooltip-arrow`, the original doesn't show the arrow."
   3084       (pdf-util-assert-pdf-window)
   3085       (when (floatp image-top)
   3086         (setq image-top
   3087               (round (* image-top (cdr (pdf-view-image-size))))))
   3088       (let* (x-gtk-use-system-tooltips ;allow for display property in tooltip
   3089              (dx (+ (or (car (window-margins)) 0)
   3090                     (car (window-fringes))))
   3091              (dy image-top)
   3092              (pos (list dx dy dx (+ dy (* 2 (frame-char-height)))))
   3093              (vscroll
   3094               (pdf-util-required-vscroll pos))
   3095              (tooltip-frame-parameters
   3096               `((border-width . 0)
   3097                 (internal-border-width . 0)
   3098                 ,@tooltip-frame-parameters))
   3099              (tooltip-hide-delay (or timeout 3)))
   3100         (when vscroll
   3101           (image-set-window-vscroll vscroll))
   3102         (setq dy (max 0 (- dy
   3103                            (cdr (pdf-view-image-offset))
   3104                            (window-vscroll nil t)
   3105                            (frame-char-height))))
   3106         (when (overlay-get (pdf-view-current-overlay) 'before-string)
   3107           (let* ((e (window-inside-pixel-edges))
   3108                  (xw (pdf-util-with-edges (e) e-width)))
   3109             (cl-incf dx (/ (- xw (car (pdf-view-image-size t))) 2))))
   3110         (pdf-util-tooltip-in-window "\u2192" dx dy))))
   3111 #+end_src
   3112 
   3113 *** TODO this clobbers register x. Find a way to not clobber a register
   3114 ** virtualenvwrapper
   3115 Like virtualenvwrapper.sh, but for Emacs.
   3116 
   3117 #+begin_src emacs-lisp
   3118   (use-package virtualenvwrapper
   3119     :custom
   3120     (venv-location "~/.config/virtualenvs")
   3121 
   3122     :config
   3123     (venv-initialize-interactive-shells)
   3124     (venv-initialize-eshell))
   3125 #+end_src
   3126 
   3127 ** ledger
   3128 #+begin_src emacs-lisp
   3129   (use-package ledger-mode
   3130     :mode ("\\.ledger\\'")
   3131     :hook (ledger-mode . company-mode)
   3132     :custom
   3133     (ledger-clear-whole-transactions t)
   3134     (ledger-reconcile-default-commodity "eur")
   3135     (ledger-reports
   3136      '(("unreconciled" "%(binary) [[ledger-mode-flags]] -f %(ledger-file) --start-of-week=1 reg --uncleared")
   3137        ("net-worth-changes" "%(binary) [[ledger-mode-flags]] -f %(ledger-file) reg ^Assets ^Liabilities -R -M -X eur --effective -n")
   3138        ("budget-last-month" "%(binary) -f %(ledger-file) --start-of-week=1 --effective -X eur --period \"last month\" budget ^expenses:budgeted")
   3139        ("budget-this-month" "%(binary) -f %(ledger-file) --start-of-week=1 --effective -X eur --period \"this month\" budget ^expenses:budgeted")
   3140        ("expenses-this-month-vs-budget" "%(binary) -f %(ledger-file) --start-of-week=1 --effective --period \"this month\" --period-sort \"(amount)\" bal ^expenses:budgeted --budget -R")
   3141        ("expenses-last-month-vs-budget" "%(binary) -f %(ledger-file) --start-of-week=1 --effective --period \"last month\" --period-sort \"(amount)\" bal ^expenses:budgeted --budget -R")
   3142        ("expenses-last-month" "%(binary) -f %(ledger-file) --start-of-week=1 --effective --period \"last month\" --period-sort \"(amount)\" bal ^expenses -X eur -R")
   3143        ("expenses-this-month" "%(binary) -f %(ledger-file) --start-of-week=1 --effective --period \"this month\" --period-sort \"(amount)\" bal ^expenses -X eur -R")
   3144        ("expenses-vs-income-this-month" "%(binary) -f %(ledger-file) --start-of-week=1 --effective --period \"this month\" --period-sort \"(amount)\" bal ^income ^expenses -X eur -R")
   3145        ("expenses-vs-income-last-month" "%(binary) -f %(ledger-file) --start-of-week=1 --effective --period \"last month\" --period-sort \"(amount)\" bal ^expenses ^income -X eur -R")
   3146        ("bal-assets-czk" "%(binary) -f %(ledger-file) --start-of-week=1 bal Assets Liabilities -X czk -R")
   3147        ("bal-assets" "%(binary) -f %(ledger-file) --start-of-week=1 bal Assets Liabilities -R")
   3148        ("bal" "%(binary) -f %(ledger-file) --start-of-week=1 bal -B -R")
   3149        ("bal-assets-eur" "%(binary) -f %(ledger-file) --start-of-week=1 bal Assets Liabilities -X eur -R")
   3150        ("monthly-balance-abn-checking" "%(binary) -f %(ledger-file) --start-of-week=1 --effective reg --monthly 'Assets:ABN Checking' -R")
   3151        ("monthly-expenses" "%(binary) -f %(ledger-file) --monthly register ^expenses --effective --collapse -X eur -R")
   3152        ("reg" "%(binary) -f %(ledger-file) --start-of-week=1 reg -R")
   3153        ("payee" "%(binary) -f %(ledger-file) --start-of-week=1 reg @%(payee) -R")
   3154        ("account" "%(binary) -f %(ledger-file) --start-of-week=1 reg %(account) -R")
   3155        ("reg-org-table" "%(binary) -f %(ledger-file) csv --csv-format '|%(scrub(date))|%(scrub(display_account))|%(scrub(payee))|%(scrub(display_amount))|%(scrub(display_total))|
   3156   ' %(account) -R")))
   3157     :config
   3158     (with-eval-after-load 'ledger-mode
   3159       (setq ledger-amount-regex
   3160             (rx
   3161              (group (or (= 2 " ") ?\t (seq " " ?\t)))
   3162              (zero-or-more (any " " ?\t))
   3163              (opt "=")
   3164              (zero-or-more space)
   3165              (opt "-")
   3166              (opt "(")
   3167              (one-or-more (opt (group
   3168                                 (one-or-more (any "A-Z" "$(_£€₹"))
   3169                                 (zero-or-more blank)))
   3170                           (group (opt "-")
   3171                                  (or (one-or-more (any "0-9"))
   3172                                      (+\? (any "0-9" ",."))))
   3173                           (opt (group (any ",.")
   3174                                       (one-or-more (any "0-9" ")"))))
   3175                           (opt (group (zero-or-more blank)
   3176                                       (one-or-more (any "\"_£€₹" word))))
   3177                           (opt (zero-or-more (any blank))
   3178                                (any "*+/-")
   3179                                (zero-or-more (any blank))))
   3180              (opt ")")
   3181              (opt (group (zero-or-more (any blank))
   3182                          (any "=@{")
   3183                          (opt "@")
   3184                          (+? (not (any ?\xA ";")))))
   3185              (opt (group (or (seq (one-or-more (any blank)) ";" (+\? nonl))
   3186                              (zero-or-more (any blank)))))
   3187              eol))))
   3188 #+end_src
   3189 
   3190 #+RESULTS:
   3191 : ((\.[pP][dD][fF]\' . pdf-view-mode) (/neomutt- . mail-mode) (\.odc\' . archive-mode) (\.odf\' . archive-mode) (\.odi\' . archive-mode) (\.otp\' . archive-mode) (\.odp\' . archive-mode) (\.otg\' . archive-mode) (\.odg\' . archive-mode) (\.ots\' . archive-mode) (\.ods\' . archive-mode) (\.odm\' . archive-mode) (\.ott\' . archive-mode) (\.odt\' . archive-mode) (\.epub\' . nov-mode) (\.[Ss][Aa][Ss]\' . SAS-mode) (\.Sout\' . S-transcript-mode) (\.[Ss]t\' . S-transcript-mode) (\.Rd\' . Rd-mode) (DESCRIPTION\' . conf-colon-mode) (/Makevars\(\.win\)?\' . makefile-mode) (\.[Rr]out\' . ess-r-transcript-mode) (CITATION\' . ess-r-mode) (NAMESPACE\' . ess-r-mode) (\.[rR]profile\' . ess-r-mode) (\.[rR]\' . ess-r-mode) (/R/.*\.q\' . ess-r-mode) (\.[Jj][Aa][Gg]\' . ess-jags-mode) (\.[Bb][Mm][Dd]\' . ess-bugs-mode) (\.[Bb][Oo][Gg]\' . ess-bugs-mode) (\.[Bb][Uu][Gg]\' . ess-bugs-mode) (\.lean$ . lean-mode) (\.ledger\' . ledger-mode) (/git-rebase-todo\' . git-rebase-mode) (\.\(?:md\|markdown\|mkd\|mdown\|mkdn\|mdwn\)\' . markdown-mode) (\.gpg\(~\|\.~[0-9]+~\)?\' nil epa-file) (\.elc\' . elisp-byte-code-mode) (\.zst\' nil jka-compr) (\.dz\' nil jka-compr) (\.xz\' nil jka-compr) (\.lzma\' nil jka-compr) (\.lz\' nil jka-compr) (\.g?z\' nil jka-compr) (\.bz2\' nil jka-compr) (\.Z\' nil jka-compr) (\.vr[hi]?\' . vera-mode) (\(?:\.\(?:rbw?\|ru\|rake\|thor\|jbuilder\|rabl\|gemspec\|podspec\)\|/\(?:Gem\|Rake\|Cap\|Thor\|Puppet\|Berks\|Brew\|Vagrant\|Guard\|Pod\)file\)\' . ruby-mode) (\.re?st\' . rst-mode) (\.py[iw]?\' . python-mode) (\.m\' . octave-maybe-mode) (\.less\' . less-css-mode) (\.scss\' . scss-mode) (\.cs\' . csharp-mode) (\.awk\' . awk-mode) (\.\(u?lpc\|pike\|pmod\(\.in\)?\)\' . pike-mode) (\.idl\' . idl-mode) (\.java\' . java-mode) (\.m\' . objc-mode) (\.ii\' . c++-mode) (\.i\' . c-mode) (\.lex\' . c-mode) (\.y\(acc\)?\' . c-mode) (\.h\' . c-or-c++-mode) (\.c\' . c-mode) (\.\(CC?\|HH?\)\' . c++-mode) (\.[ch]\(pp\|xx\|\+\+\)\' . c++-mode) (\.\(cc\|hh\)\' . c++-mode) (\.\(bat\|cmd\)\' . bat-mode) (\.[sx]?html?\(\.[a-zA-Z_]+\)?\' . mhtml-mode) (\.svgz?\' . image-mode) (\.svgz?\' . xml-mode) (\.x[bp]m\' . image-mode) (\.x[bp]m\' . c-mode) (\.p[bpgn]m\' . image-mode) (\.tiff?\' . image-mode) (\.gif\' . image-mode) (\.png\' . image-mode) (\.jpe?g\' . image-mode) (\.webp\' . image-mode) (\.te?xt\' . text-mode) (\.[tT]e[xX]\' . tex-mode) (\.ins\' . tex-mode) (\.ltx\' . latex-mode) (\.dtx\' . doctex-mode) (\.org\' . org-mode) (\.dir-locals\(?:-2\)?\.el\' . lisp-data-mode) (\.eld\' . lisp-data-mode) (eww-bookmarks\' . lisp-data-mode) (tramp\' . lisp-data-mode) (/archive-contents\' . lisp-data-mode) (places\' . lisp-data-mode) (\.emacs-places\' . lisp-data-mode) (\.el\' . emacs-lisp-mode) (Project\.ede\' . emacs-lisp-mode) (\.\(scm\|sls\|sld\|stk\|ss\|sch\)\' . scheme-mode) (\.l\' . lisp-mode) (\.li?sp\' . lisp-mode) (\.[fF]\' . fortran-mode) (\.for\' . fortran-mode) (\.p\' . pascal-mode) (\.pas\' . pascal-mode) (\.\(dpr\|DPR\)\' . delphi-mode) (\.\([pP]\([Llm]\|erl\|od\)\|al\)\' . perl-mode) (Imakefile\' . makefile-imake-mode) (Makeppfile\(?:\.mk\)?\' . makefile-makepp-mode) (\.makepp\' . makefile-makepp-mode) (\.mk\' . makefile-bsdmake-mode) (\.make\' . makefile-bsdmake-mode) (GNUmakefile\' . makefile-gmake-mode) ([Mm]akefile\' . makefile-bsdmake-mode) (\.am\' . makefile-automake-mode) (\.texinfo\' . texinfo-mode) (\.te?xi\' . texinfo-mode) (\.[sS]\' . asm-mode) (\.asm\' . asm-mode) (\.css\' . css-mode) (\.mixal\' . mixal-mode) (\.gcov\' . compilation-mode) (/\.[a-z0-9-]*gdbinit . gdb-script-mode) (-gdb\.gdb . gdb-script-mode) ([cC]hange\.?[lL]og?\' . change-log-mode) ([cC]hange[lL]og[-.][0-9]+\' . change-log-mode) (\$CHANGE_LOG\$\.TXT . change-log-mode) (\.scm\.[0-9]*\' . scheme-mode) (\.[ckz]?sh\'\|\.shar\'\|/\.z?profile\' . sh-mode) (\.bash\' . sh-mode) (/PKGBUILD\' . sh-mode) (\(/\|\`\)\.\(bash_\(profile\|history\|log\(in\|out\)\)\|z?log\(in\|out\)\)\' . sh-mode) (\(/\|\`\)\.\(shrc\|zshrc\|m?kshrc\|bashrc\|t?cshrc\|esrc\)\' . sh-mode) (\(/\|\`\)\.\([kz]shenv\|xinitrc\|startxrc\|xsession\)\' . sh-mode) (\.m?spec\' . sh-mode) (\.m[mes]\' . nroff-mode) (\.man\' . nroff-mode) (\.sty\' . latex-mode) (\.cl[so]\' . latex-mode) (\.bbl\' . latex-mode) (\.bib\' . bibtex-mode) (\.bst\' . bibtex-style-mode) (\.sql\' . sql-mode) (\(acinclude\|aclocal\|acsite\)\.m4\' . autoconf-mode) (\.m[4c]\' . m4-mode) (\.mf\' . metafont-mode) (\.mp\' . metapost-mode) (\.vhdl?\' . vhdl-mode) (\.article\' . text-mode) (\.letter\' . text-mode) (\.i?tcl\' . tcl-mode) (\.exp\' . tcl-mode) (\.itk\' . tcl-mode) (\.icn\' . icon-mode) (\.sim\' . simula-mode) (\.mss\' . scribe-mode) (\.f9[05]\' . f90-mode) (\.f0[38]\' . f90-mode) (\.indent\.pro\' . fundamental-mode) (\.\(pro\|PRO\)\' . idlwave-mode) (\.srt\' . srecode-template-mode) (\.prolog\' . prolog-mode) (\.tar\' . tar-mode) (\.\(arc\|zip\|lzh\|lha\|zoo\|[jew]ar\|xpi\|rar\|cbr\|7z\|squashfs\|ARC\|ZIP\|LZH\|LHA\|ZOO\|[JEW]AR\|XPI\|RAR\|CBR\|7Z\|SQUASHFS\)\' . archive-mode) (\.oxt\' . archive-mode) (\.\(deb\|[oi]pk\)\' . archive-mode) (\`/tmp/Re . text-mode) (/Message[0-9]*\' . text-mode) (\`/tmp/fol/ . text-mode) (\.oak\' . scheme-mode) (\.sgml?\' . sgml-mode) (\.x[ms]l\' . xml-mode) (\.dbk\' . xml-mode) (\.dtd\' . sgml-mode) (\.ds\(ss\)?l\' . dsssl-mode) (\.js[mx]?\' . javascript-mode) (\.har\' . javascript-mode) (\.json\' . js-json-mode) (\.[ds]?va?h?\' . verilog-mode) (\.by\' . bovine-grammar-mode) (\.wy\' . wisent-grammar-mode) (\.erts\' . erts-mode) ([:/\]\..*\(emacs\|gnus\|viper\)\' . emacs-lisp-mode) (\`\..*emacs\' . emacs-lisp-mode) ([:/]_emacs\' . emacs-lisp-mode) (/crontab\.X*[0-9]+\' . shell-script-mode) (\.ml\' . lisp-mode) (\.ld[si]?\' . ld-script-mode) (ld\.?script\' . ld-script-mode) (\.xs\' . c-mode) (\.x[abdsru]?[cnw]?\' . ld-script-mode) (\.zone\' . dns-mode) (\.soa\' . dns-mode) (\.asd\' . lisp-mode) (\.\(asn\|mib\|smi\)\' . snmp-mode) (\.\(as\|mi\|sm\)2\' . snmpv2-mode) (\.\(diffs?\|patch\|rej\)\' . diff-mode) (\.\(dif\|pat\)\' . diff-mode) (\.[eE]?[pP][sS]\' . ps-mode) (\.\(?:PDF\|EPUB\|CBZ\|FB2\|O?XPS\|DVI\|OD[FGPST]\|DOCX\|XLSX?\|PPTX?\|pdf\|epub\|cbz\|fb2\|o?xps\|djvu\|dvi\|od[fgpst]\|docx\|xlsx?\|pptx?\)\' . doc-view-mode-maybe) (configure\.\(ac\|in\)\' . autoconf-mode) (\.s\(v\|iv\|ieve\)\' . sieve-mode) (BROWSE\' . ebrowse-tree-mode) (\.ebrowse\' . ebrowse-tree-mode) (#\*mail\* . mail-mode) (\.g\' . antlr-mode) (\.mod\' . m2-mode) (\.ses\' . ses-mode) (\.docbook\' . sgml-mode) (\.com\' . dcl-mode) (/config\.\(?:bat\|log\)\' . fundamental-mode) (/\.\(authinfo\|netrc\)\' . authinfo-mode) (\.\(?:[iI][nN][iI]\|[lL][sS][tT]\|[rR][eE][gG]\|[sS][yY][sS]\)\' . conf-mode) (\.la\' . conf-unix-mode) (\.ppd\' . conf-ppd-mode) (java.+\.conf\' . conf-javaprop-mode) (\.properties\(?:\.[a-zA-Z0-9._-]+\)?\' . conf-javaprop-mode) (\.toml\' . conf-toml-mode) (\.desktop\' . conf-desktop-mode) (/\.redshift\.conf\' . conf-windows-mode) (\`/etc/\(?:DIR_COLORS\|ethers\|.?fstab\|.*hosts\|lesskey\|login\.?de\(?:fs\|vperm\)\|magic\|mtab\|pam\.d/.*\|permissions\(?:\.d/.+\)?\|protocols\|rpc\|services\)\' . conf-space-mode) (\`/etc/\(?:acpid?/.+\|aliases\(?:\.d/.+\)?\|default/.+\|group-?\|hosts\..+\|inittab\|ksysguarddrc\|opera6rc\|passwd-?\|shadow-?\|sysconfig/.+\)\' . conf-mode) ([cC]hange[lL]og[-.][-0-9a-z]+\' . change-log-mode) (/\.?\(?:gitconfig\|gnokiirc\|hgrc\|kde.*rc\|mime\.types\|wgetrc\)\' . conf-mode) (/\.mailmap\' . conf-unix-mode) (/\.\(?:asound\|enigma\|fetchmail\|gltron\|gtk\|hxplayer\|mairix\|mbsync\|msmtp\|net\|neverball\|nvidia-settings-\|offlineimap\|qt/.+\|realplayer\|reportbug\|rtorrent\.\|screen\|scummvm\|sversion\|sylpheed/.+\|xmp\)rc\' . conf-mode) (/\.\(?:gdbtkinit\|grip\|mpdconf\|notmuch-config\|orbital/.+txt\|rhosts\|tuxracer/options\)\' . conf-mode) (/\.?X\(?:default\|resource\|re\)s\> . conf-xdefaults-mode) (/X11.+app-defaults/\|\.ad\' . conf-xdefaults-mode) (/X11.+locale/.+/Compose\' . conf-colon-mode) (/X11.+locale/compose\.dir\' . conf-javaprop-mode) (\.~?[0-9]+\.[0-9][-.0-9]*~?\' nil t) (\.\(?:orig\|in\|[bB][aA][kK]\)\' nil t) ([/.]c\(?:on\)?f\(?:i?g\)?\(?:\.[a-zA-Z0-9._-]+\)?\' . conf-mode-maybe) (\.[1-9]\' . nroff-mode) (\.art\' . image-mode) (\.avs\' . image-mode) (\.bmp\' . image-mode) (\.cmyk\' . image-mode) (\.cmyka\' . image-mode) (\.crw\' . image-mode) (\.dcr\' . image-mode) (\.dcx\' . image-mode) (\.dng\' . image-mode) (\.dpx\' . image-mode) (\.fax\' . image-mode) (\.heic\' . image-mode) (\.hrz\' . image-mode) (\.icb\' . image-mode) (\.icc\' . image-mode) (\.icm\' . image-mode) (\.ico\' . image-mode) (\.icon\' . image-mode) (\.jbg\' . image-mode) (\.jbig\' . image-mode) (\.jng\' . image-mode) (\.jnx\' . image-mode) (\.miff\' . image-mode) (\.mng\' . image-mode) (\.mvg\' . image-mode) (\.otb\' . image-mode) (\.p7\' . image-mode) (\.pcx\' . image-mode) (\.pdb\' . image-mode) (\.pfa\' . image-mode) (\.pfb\' . image-mode) (\.picon\' . image-mode) (\.pict\' . image-mode) (\.rgb\' . image-mode) (\.rgba\' . image-mode) (\.tga\' . image-mode) (\.wbmp\' . image-mode) (\.webp\' . image-mode) (\.wmf\' . image-mode) (\.wpg\' . image-mode) (\.xcf\' . image-mode) (\.xmp\' . image-mode) (\.xwd\' . image-mode) (\.yuv\' . image-mode) (\.tgz\' . tar-mode) (\.tbz2?\' . tar-mode) (\.txz\' . tar-mode) (\.tzst\' . tar-mode))
   3192 
   3193 org-capture lets me add transactions from anywhere in Emacs:
   3194 
   3195 Budget throws an error when there's multiple commodities involved.
   3196 See discussion here: https://github.com/ledger/ledger/issues/1450#issuecomment-390067165
   3197 
   3198 #+begin_src emacs-lisp
   3199   (defconst za/ledger-budget-fix-string
   3200     "-X eur -F '%(justify(scrub(get_at(display_total, 0)), 20, -1, true, false)) %(justify(get_at(display_total, 1) ? -scrub(get_at(display_total, 1)) : 0.0, 20,            20 + 1 + 20, true, false)) %(justify(get_at(display_total, 1) ? (get_at(display_total, 0) ?           -(scrub(get_at(display_total, 1) + get_at(display_total, 0))) :           -(scrub(get_at(display_total, 1)))) : -(scrub(get_at(display_total, 0))), 20,            20 + 1 + 20 + 1 + 20, true, false))%(get_at(display_total, 1) and (abs(quantity(scrub(get_at(display_total, 0))) /           quantity(scrub(get_at(display_total, 1)))) >= 1) ?  \" \" : \" \")%(justify((get_at(display_total, 1) ?           (100% * (get_at(display_total, 0) ? scrub(get_at(display_total, 0)) : 0.0)) /              -scrub(get_at(display_total, 1)) : \"na\"),            5, -1, true, false))  %(!options.flat ? depth_spacer : \"\")%-(partial_account(options.flat))\n%/%$2 %$3 %$4 %$6\n%/%(prepend_width ? \" \" * int(prepend_width) : \"\")    ----------------     ----------------     ---------------- -----\n'"
   3201     "Append this to a ledger budget to fix errors with multiple commodities.")
   3202 #+end_src
   3203 
   3204 ** Notmuch
   3205 #+begin_src emacs-lisp
   3206   (use-package notmuch
   3207     :custom
   3208     (notmuch-saved-searches
   3209      `((:name "inbox: personal" :query ,(format "folder:/%s/ tag:inbox" za/email-personal) :key ,(kbd "ip") :search-type 'tree)
   3210        (:name "inbox: school" :query ,(format "folder:/%s/ tag:inbox" za/email-vu) :key ,(kbd "is") :search-type 'tree)
   3211        (:name "archive: personal" :query ,(format "folder:/%s/ tag:archive" za/email-personal) :key ,(kbd "ap") :search-type 'tree)
   3212        (:name "archive: school" :query ,(format "folder:/%s/ tag:archive" za/email-vu) :key ,(kbd "as") :search-type 'tree))
   3213      "Define some saved searches (i.e. mailboxes)")
   3214     (notmuch-hello-sections
   3215      '(notmuch-hello-insert-header
   3216        notmuch-hello-insert-saved-searches
   3217        notmuch-hello-insert-search
   3218        notmuch-hello-insert-alltags
   3219        notmuch-hello-insert-footer)
   3220      "Define the main screen sections")
   3221     (notmuch-search-oldest-first nil "Show newest mail first")
   3222     (notmuch-archive-tags '("-inbox" "+archive"))
   3223     (notmuch-tagging-keys '(("a" notmuch-archive-tags "Archive")
   3224                             ("r" notmuch-show-mark-read-tags "Mark read")
   3225                             ("u" notmuch-show-mark-unread-tags "Mark unread")
   3226                             ("d" notmuch-delete-tags "Delete")))
   3227 
   3228     :bind (("C-c m" . notmuch)
   3229            :map notmuch-show-mode-map
   3230            ("C-c M-y" . shr-copy-url))
   3231     ;; Run notmuch-hook script on hello refresh, to move messages to
   3232     ;; folders according to their tags:
   3233     :hook (notmuch-hello-refresh . za/notmuch-hook-tags2folders)
   3234     :init (setenv "NOTMUCH_CONFIG" "/Users/alex/.config/notmuch/config")
   3235     :config
   3236     (setq notmuch-show-mark-unread-tags '("+unread"))
   3237     (setq notmuch-delete-tags '("-inbox" "+trash"))
   3238     (defun za/notmuch-hook-tags2folders ()
   3239       "Run notmuch-hook to organise email in folders based on tags."
   3240       (start-process "notmuch-hook" nil "notmuch-hook" "--tags2folders")))
   3241 #+end_src
   3242 
   3243 ** MPC
   3244 #+begin_src emacs-lisp
   3245   (use-package mpc
   3246     :custom
   3247     (mpc-browser-tags '(AlbumArtist Album Genre Playlist)
   3248                       "Set the windows I want to show")
   3249 
   3250     :bind (:map mpc-mode-map
   3251                 ("a" . mpc-playlist-add)
   3252                 ("P" . mpc-playlist)
   3253                 ("x" . mpc-playlist-delete)
   3254                 ("p" . mpc-toggle-play)
   3255                 ("t" . mpc-select-toggle)
   3256                 ("f" . za/mpc-seek-forward-20-seconds)
   3257                 ("b" . za/mpc-seek-backward-20-seconds))
   3258     :config
   3259     (defun za/mpc-seek-forward-20-seconds ()
   3260       "Seek forward 20 seconds"
   3261       (interactive)
   3262       (mpc-seek-current "+20"))
   3263 
   3264     (defun za/mpc-seek-backward-20-seconds ()
   3265       "Seek backward 20 seconds"
   3266       (interactive)
   3267       (mpc-seek-current "-20")))
   3268 #+end_src
   3269 ** Dired
   3270 #+begin_src emacs-lisp
   3271   (use-package dired
   3272     :ensure nil ; installed with Emacs
   3273     :bind (:map dired-mode-map
   3274                 ;; 'i' expands subdirs, so I want to be able to close them too.
   3275                 ("M-k" . dired-kill-subdir))
   3276     :custom
   3277     (dired-listing-switches "-alhv")
   3278     (dired-dwim-target t "If I have another dired window open, use that as target")
   3279     ;; By default, hide details (show again by pressing oparen):
   3280     :hook (dired-mode . dired-hide-details-mode))
   3281 #+end_src
   3282 
   3283 ** ess: statistics (R, SAS...)
   3284 #+begin_src emacs-lisp
   3285   (use-package ess)
   3286 #+end_src
   3287 
   3288 ** help mode
   3289 #+begin_src emacs-lisp
   3290   (use-package help-mode
   3291     :ensure nil ; included with Emacs
   3292     :hook (help-mode . za/settings-on-help-mode)
   3293     :config
   3294     (defun za/settings-on-help-mode ()
   3295       "Settings on enabling help mode"
   3296       (za/toggle-wrap t)))
   3297 #+end_src
   3298 ** helpful
   3299 An alternative to the built-in Emacs help that provides much more contextual information.
   3300 I use counsel, so I use the keybindings in [[*counsel + ivy + swiper]].
   3301 I just augment the functions counsel uses.
   3302 Also, counsel doesn't provide some keybindings that I can get from helpful.
   3303 
   3304 #+begin_src emacs-lisp
   3305   (use-package helpful
   3306     :custom
   3307     (counsel-describe-symbol-function #'helpful-symbol)
   3308     (counsel-describe-function-function #'helpful-callable)
   3309     (counsel-describe-variable-function #'helpful-variable)
   3310 
   3311     :bind (("C-h k" . helpful-key)
   3312            ("C-h C" . helpful-command)
   3313            :map helpful-mode-map
   3314            ("l" . za/helpful-previous)
   3315            ("r" . za/helpful-next))
   3316 
   3317     :hook (helpful-mode . za/settings-on-helpful-mode)
   3318     :config
   3319 
   3320     (defun za/settings-on-helpful-mode ()
   3321       "Settings on enabling helpful mode"
   3322       (za/toggle-wrap t))
   3323 
   3324     ;; Then, a way to jump forward and backward in the window:
   3325     (defvar za/helpful-buffer-ring-size 20
   3326       "How many buffers are stored for use with `helpful-next'.")
   3327 
   3328     (defvar za/helpful--buffer-ring (make-ring za/helpful-buffer-ring-size)
   3329       "Ring that stores the current Helpful buffer history.")
   3330 
   3331     (defun za/helpful--buffer-index (&optional buffer)
   3332       "If BUFFER is a Helpful buffer, return it’s index in the buffer ring."
   3333       (let ((buf (or buffer (current-buffer))))
   3334         (and (eq (buffer-local-value 'major-mode buf) 'helpful-mode)
   3335              (seq-position (ring-elements za/helpful--buffer-ring) buf #'eq))))
   3336 
   3337     (defun za/helpful--new-buffer-a (help-buf)
   3338       "Update the buffer ring according to the current buffer and HELP-BUF."
   3339       :filter-return #'helpful--buffer
   3340       (let ((buf-ring za/helpful--buffer-ring))
   3341         (let ((newer-buffers (or (za/helpful--buffer-index) 0)))
   3342           (dotimes (_ newer-buffers) (ring-remove buf-ring 0)))
   3343         (when (/= (ring-size buf-ring) za/helpful-buffer-ring-size)
   3344           (ring-resize buf-ring za/helpful-buffer-ring-size))
   3345         (ring-insert buf-ring help-buf)))
   3346 
   3347     (advice-add #'helpful--buffer :filter-return #'za/helpful--new-buffer-a)
   3348 
   3349     (defun za/helpful--next (&optional buffer)
   3350       "Return the next live Helpful buffer relative to BUFFER."
   3351       (let ((buf-ring za/helpful--buffer-ring)
   3352             (index (or (za/helpful--buffer-index buffer) -1)))
   3353         (cl-block nil
   3354           (while (> index 0)
   3355             (cl-decf index)
   3356             (let ((buf (ring-ref buf-ring index)))
   3357               (if (buffer-live-p buf) (cl-return buf)))
   3358             (ring-remove buf-ring index)))))
   3359 
   3360 
   3361     (defun za/helpful--previous (&optional buffer)
   3362       "Return the previous live Helpful buffer relative to BUFFER."
   3363       (let ((buf-ring za/helpful--buffer-ring)
   3364             (index (1+ (or (za/helpful--buffer-index buffer) -1))))
   3365         (cl-block nil
   3366           (while (< index (ring-length buf-ring))
   3367             (let ((buf (ring-ref buf-ring index)))
   3368               (if (buffer-live-p buf) (cl-return buf)))
   3369             (ring-remove buf-ring index)))))
   3370 
   3371     (defun za/helpful-next ()
   3372       "Go to the next Helpful buffer."
   3373       (interactive)
   3374       (when-let (buf (za/helpful--next))
   3375         (funcall helpful-switch-buffer-function buf)))
   3376 
   3377     (defun za/helpful-previous ()
   3378       "Go to the previous Helpful buffer."
   3379       (interactive)
   3380       (when-let (buf (za/helpful--previous))
   3381         (funcall helpful-switch-buffer-function buf))))
   3382 #+end_src
   3383 ** Tex-mode
   3384 #+begin_src emacs-lisp
   3385   (use-package tex-mode
   3386     :ensure nil ; installed with Emacs
   3387     :hook (tex-mode . za/settings-on-tex-mode)
   3388     :config
   3389     (defun za/settings-on-tex-mode ()
   3390       "Settings on enabling helpful mode"
   3391       (setq comment-add 0)))
   3392 #+end_src
   3393 ** Quail
   3394 #+begin_src emacs-lisp
   3395   (use-package quail
   3396     :ensure nil) ; provided by Emacs
   3397 #+end_src
   3398 ** Markdown
   3399 #+begin_src emacs-lisp
   3400   (use-package markdown-mode)
   3401 #+end_src
   3402 ** vdirel (contacts)
   3403 #+begin_src emacs-lisp
   3404   (use-package vdirel
   3405     :config
   3406     (vdirel-switch-repository "~/.local/share/contacts/default"))
   3407 #+end_src
   3408 ** Yaml
   3409 #+begin_src emacs-lisp
   3410   (use-package yaml-mode
   3411     :commands yaml-mode
   3412     :init
   3413     (add-hook 'yaml-mode-hook
   3414               (lambda ()
   3415                 (setq-local outline-regexp (rx (* blank)))
   3416                 (outline-minor-mode))))
   3417 #+end_src
   3418 ** calc
   3419 #+begin_src emacs-lisp
   3420   (use-package calc
   3421     :config
   3422     (setq math-additional-units
   3423      ;; elements:
   3424      ;; - symbol identifying the unit,
   3425      ;; - expression indicatingv alue of unit or nil for fundamental units
   3426      ;; - textual description
   3427      '((b nil "Bit")
   3428        (B "b * 8" "Bytes")
   3429        (KiB "1024 * B" "Kibibyte")
   3430        (MiB "1024 * KiB" "Mebibyte")
   3431        (GiB "1024 * MiB" "Gibibyte")
   3432        (TiB "1024 * GiB" "Tebibyte")
   3433        (PiB "1024 * TiB" "Pebibyte")
   3434        (EiB "1024 * PiB" "Exbibyte")
   3435        (ZiB "1024 * EiB" "Zebibyte")
   3436        (YiB "1024 * ZiB" "Yobibyte")
   3437        (KB "1000 * B" "Kilobyte")
   3438        (MB "1000 * KB" "Megabyte")
   3439        (GB "1000 * MB" "Gigabyte")
   3440        (TB "1000 * GB" "Terabyte")
   3441        (PB "1000 * TB" "Petabyte")
   3442        (EB "1000 * PB" "Exabyte")
   3443        (ZB "1000 * EB" "Zettabyte")
   3444        (YB "1000 * ZB" "Yottabyte")
   3445        (Kib "1024 * b" "Kibibit")
   3446        (Mib "1024 * Kib" "Mebibit")
   3447        (Gib "1024 * Mib" "Gibibit")
   3448        (Kb "1000 * b" "Kilobit")
   3449        (Mb "1000 * Kb" "Megabit")
   3450        (Gb "1000 * Mb" "Gigabit")))
   3451     (setq math-units-table nil))
   3452 #+end_src
   3453 ** casual
   3454 #+begin_src emacs-lisp
   3455   (use-package casual
   3456     :bind (:map calc-mode-map ("C-o" . 'casual-calc-tmenu)))
   3457 #+end_src
   3458 ** casual-dired
   3459 #+begin_src emacs-lisp
   3460   (use-package casual-dired
   3461     :bind (:map dired-mode-map
   3462                 ("C-o" . 'casual-dired-tmenu)
   3463                 ("C-u C-o" . 'dired-display-file)))
   3464 #+end_src
   3465 
   3466 ** json
   3467 #+begin_src emacs-lisp
   3468   (use-package json-mode)
   3469 #+end_src
   3470 ** rust
   3471 #+begin_src emacs-lisp
   3472   (use-package rust-mode)
   3473 #+end_src
   3474 * Override some faces
   3475 #+begin_src emacs-lisp
   3476   (with-eval-after-load 'org-faces
   3477     (set-face-attribute 'org-table nil :inherit 'fixed-pitch)
   3478     (set-face-attribute 'org-block nil :inherit 'fixed-pitch))
   3479 #+end_src
   3480 * Shortdoc
   3481 Set a better keybinding (I'm never gonna use ~view-hello-file~ anyways):
   3482 
   3483 #+begin_src emacs-lisp
   3484   (bind-key "C-h h" #'shortdoc-display-group)
   3485 #+end_src
   3486 * Upcoming new features
   3487 In a new version of use-package, I can use the :vc keyword, so check for when that's available.
   3488 See [[https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=2ce279680bf9c1964e98e2aa48a03d6675c386fe][commit]] and [[https://tony-zorman.com/posts/use-package-vc.html][article]].
   3489 
   3490 #+begin_src emacs-lisp
   3491   (when (fboundp 'use-package-vc-install)
   3492     (user-error "use-package :vc keyword now available!"))
   3493 #+end_src
   3494 * References
   3495 Here's a list of good articles I encountered about configging emacs:
   3496 - [[https://karthinks.com/software/batteries-included-with-emacs/][Batteries included with Emacs]]
   3497 - [[https://karthinks.com/software/more-batteries-included-with-emacs/][More batteries included with emacs]]
   3498 
   3499 For Org mode, [[https://www.youtube.com/playlist?list=PLVtKhBrRV_ZkPnBtt_TD1Cs9PJlU0IIdE][Rainer König's tutorials]] are the best.
   3500 [[https://emacs.cafe/emacs/orgmode/gtd/2017/06/30/orgmode-gtd.html][Here's a good reference for setting up gtd in org mode]]