even more emacs advice
In my last emacs advice post, I explained what Emacs advice is for, and showed a simple example of advising a package installation function so that fresh package metadata would always be downloaded before it ran. (If you haven’t read that post, you’ll get a lot more out of this one if you read that one first. It’s ok. Go ahead, we’ll wait for you.)

Okay, so I said this next post would involve a more complex example. Let’s set up the problem first: I make pretty frequent use of an Emacs package called dired. Dired lets you interact with the file system from inside Emacs — it lists files and directories, and supports operations like copying, deleting, and renaming. It’s pretty nifty.
I also use a second package called
all-the-icons. It adds
pretty file icons to dired buffers. The screenshot at the left is from
a dired listing of the top level of the repo for this blog. The icons
on the left side come from all-the-icons
. Not at all essential, but
they make things a little more pretty, which is also pretty nifty.
One of the best parts of dired
is a special mode called
wdired
,
which gives you a writable dired buffer. Essentially, you can batch
rename a whole bunch of files, using all the text-editing abilities of
Emacs, and then when you exit this special mode, Emacs applies all the
changes for you. I find it super useful (and extremely nifty).
But, there’s an issue with how these three modes interact with each
other — those pretty icons are actually present as special
characters in the dired
buffer, and when you flip into and out of
wdired
mode, Emacs gets confused about the filename changes you’re
trying to make.
Clearly, what we need to do is disable all-the-icons-mode
when we
enter wdired
and then turn it back on after we exit wdired
. What’s
that look like?
First, we need a couple utility functions to turn all-the-icons-mode
on and off. (Technically, we don’t need these; they’re simple enough
they could just be lambda functions inlined into the advice — but we
will get a minor benefit from them before we’re done). Those utility
functions look like this:
(defun genehack/disable-all-the-icons ()
"Disable all-the-icons."
(all-the-icons-dired-mode 0))
(defun genehack/restore-all-the-icons ()
"Restore all-the-icons."
(all-the-icons-dired-mode 1))
Now, how do we figure out what functions we need to advise to solve
our problem?
The documentation
mentions using the C-x C-q
binding in dired
buffers, which runs
dired-toggle-read-only
— but checking the documentation for that
function shows us that the real work is done by
wdired-change-to-wdired-mode
, so that’s where we’ll put the “turn
off icons” advice.
When we’re in wdired
mode, the screen shows a little bit of help
text at the bottom: “Press C-c C-c when finished or C-c ESC to abort
changes”. So to figure out which functions exit wdired
, we can just
activate the mode, and then use the built-in documentation
capabilities of emacs to investigate. Pressing C-h k
will prompt for
a keybinding, and then show you what function is bound to it. Using
that shows that in wdired
mode, C-c C-c
is bound to
wdired-finish-edit
and C-c ESC
is bound to wdired-abort-changes
.
We need to advise both those functions to handle both ways wdired
mode might be exited. (That’s where having those utility functions
comes in handy — we avoid some code duplication.)
Here’s the advice:
(advice-add 'wdired-change-to-wdired-mode
:before #'genehack/disable-all-the-icons)
(advice-add 'wdired-finish-edit
:after #'genehack/restore-all-the-icons)
(advice-add 'wdired-abort-changes
:after #'genehack/restore-all-the-icons)
These tiny little bits of code do the trick: right before wdired
is
activated, all-the-icons
is turned off; right after wdired
is
exited — via either route — all-the-icons
is turned back on. Yet
another problem solved with a little advice.
Speaking of advice, an emacs pro-tip for the folks that read this far:
make your lives easier, and your wdired
use more convenient, by
setting up a better keybinding for entering the mode. Personally, I
use E
(for “Edit”), which happens to be unbound in dired
by
default. This code will set up that binding for you:
(defun genehack/bind-key-for-wdired ()
"Add a keybinding for wdired in 'dired-mode'."
(local-set-key (kbd "E") 'wdired-change-to-wdired-mode))
(add-hook 'dired-mode-hook 'genehack/bind-key-for-wdired)
All the code in this post is available in my
emacs config repo,
specifically in the
etc/builtins.el
file.
What are you using advice for?