Advising Emacs

geekery emacs

One of the delightful and surprising things about Emacs, as you get to know it better, is the depth of customisation which is available. Emacs can be a completely different editor for different people and for different purposes. Being able to tweak things on the fly and try them out before you commit to them, or even as a temporary fix to solve the particular problem you have right now, is empowering. The more you delve into it and try things out, the better you understand what is possible and the more comfortable you get with writing elisp. Recently I discovered the ‘advice’ system in Emacs, and now every problem looks like a situation for some well-placed advice!

Learning to simplify

Most of what I’ve learned comes from reading articles by Emacs users or scrutinising their Emacs config files. I’ve found a treasure trove of handy snippets and videos about Emacs which are produced by Protesilaos Stavrou (“also known as Prot”, according to his videos). As far as I understand it from his YouTube channel, he has only been using Emacs for a year or so, but he seems to have made incredible progress in understanding it, and adapting it to his needs. He has even released a pair of beautiful themes, modus-operandi (a light theme) and modus-vivendi (dark) which meet accessibility standards for visual contrast. I’ve started using these as they are extremely well-thought out and include specific configuration for a huge range of packages.

His setup is ‘vanilla’ Emacs, in the sense that he does not use a framework like Doom Emacs or Spacemacs. While he does use some third party packages, his approach is to see how close he can get to the behaviour he prefers by leveraging Emacs’ built-in tools and tweaking them with his own customisations. While I am not planning to move away from Doom (because I love it), following the same approach has been a very useful one for me, especially since I disabled evil bindings in Doom. Reading his posts, watching his videos and looking at his comprehensive and well-annotated dotfiles, I have worked through my own configuration, thinking about how to simplify and improve my everyday experience with Emacs, and have learned some useful stuff along the way.

Advising the case-changing functions

Boon has some useful bindings for changing the case of words (or regions), but I also wanted to get more familiar with Emacs’ own bindings and commands for doing this. I found them initially frustrating, because the commands require you to move first to the beginning of a word before changing the case. This makes sense in that the cursor (or ‘point’ as it is known in Emacs) then ends up at the end of the word and you can continue typing or editing your text. However, it feels awkward to me, as I usually want to type a word lower case (I actually don’t have a caps lock key anymore) and then immediately upcase the preceding word.

Looking around, I found an example of how to fix this behaviour, and in the process, learned about Emacs’ advice system. This is a kind of arbitrary hook system, which works on any function. The idea is that you write some code, and then ‘advise’ Emacs to run that code before of after a particular function (which can be built-in or third party). In this case, I am advising the case changing functions in order to modify how the point is moved before they do their work. I could of course just write my own wrapper function around the case functions and bind a key to that, but the power of advice is that it is called on whenever and however the advised function is called. So if some third party bit of code also calls on the built-in case functions, my advice will also automatically be applied. It is as if I have changed the built-in command.

The example I found used the old form of advice (which still works I think, but is outdated), so I found this article by John SJ Anderson which was very helpful in understanding how to use the new advice syntax, and also let me clean up the code a bit. This is what I ended up with:

(defun my/word-start-dwim (&rest args)
  "Go back to the beginning of the word if the point is not there.
Ignores `ARGS'. This function enables dwim case changes."
  (unless (looking-back "\\W")
    (backward-word)))

(advice-add 'upcase-word :before #'my/word-start-dwim)
(advice-add 'downcase-word :before #'my/word-start-dwim)
(advice-add 'capitalize-word :before #'my/word-start-dwim)

The command now ‘does what I mean’: if I am at the end of the word or in the middle of it the point moves to the start of the word, but if I am at the start of the word already (i.e. there is a non-word character before the point) it doesn’t move the point. I use these commands all the time now, as they are so much more useful to me than before. I’m sure that it could be improved, and there is some edge case which will make it fail, but so far it has worked the way I expect.

Hey navi - put my windows back!

Prot has some very nifty customisations for imenu and outline-minor-mode which I have re-purposed. I have a binding to open a vertical ilist buffer, which has a linked outline of the headings in an org-mode buffer, so I can get an overview and move around large files easily. His customisation to outline-minor-mode does a similar thing for code buffers, using comment markers to form the outline. Unfortunately, I could not get his method to work for me for some reason, but I found outshine and navi, which together gave me the behaviour I wanted. Navi provides a nice side window with a navigable outline, but it irritated me that when I quit the window it left behind a split window, rather than putting my windows back is it found them. Sounds like a job for advice!

(use-package! navi
  :init
  ;; put windows back as they were after navi quits
  (advice-add 'navi-quit-and-switch :after #'winner-undo)
  :bind ("<f10>" . navi-search-and-switch))

Here, I just advise the navi quit window command that it should call winner-undo, which then puts the windows back how they were before. If you use use-package but not Doom, just drop the exclamation mark and it should work.

Moving around with isearch

Prot also convinced me of the power of isearch — not just for searching and replacing text, but also for moving around the buffer. I used a few of his customisations, but also modified my Ergodox to map the keys for forward and reverse search (C-s and C-r respectively) to a pair of big keys on the inner column of the keyboard. The right-hand one searches forward and the left one searches backward. Having these keys so accessible has also greatly increased the amount I use isearch, and it has come to be one of the main ways I move my point around a buffer, when moving longer distances.

Emacs really is a tinkerer’s paradise!