2021-07-26 Binding a command to every key bound to another command

For today, I have an extremely specific Emacs Lisp tip – this is definitely not something you would use every day, but when you need it, it’s there.

Assume that you are writing a mode (call it cool-mode), which should support some general Emacs command (call it do-something) – only the workings of this mode are so specific that this command should be basically written from scratch instead of using Emacs’ own do-something. You could (of course) advise do-something, but it seems cleaner to define cool-do-something instead. How do you bind it to the same keys it is normally bound to in Emacs? For bonus points, the user might have its Emacs customized (with global-set-key, for example) to bind do-something to a key of their choice.

It turns out that (surprise!) Emacs has support for exactly this use case. The function substitute-key-definition, called with four arguments, olddef, newdef, keymap and oldmap, finds all the keys in oldmap which are bound to olddef and binds these precise keys in keymap to newdef. So the only thing you need to say (after defining your mode’s keymap) is something like

(substitute-key-definition
 'do-something
 'cool-do-something
 cool-mode-map
 global-map)

Clever, isn’t it? And if that code is called after the user’s customizations, every key customized by the user (in global-map, of course) to call do-something will call cool-do-something in cool-mode. Great!

One caveat: the fourth parameter to the substitute-key-definition function is optional, but if it’s absent, the meaning of the command is different (though still related). Head on to the manual to learn what it does then (although it’s not hard to guess, really).

And, if you happen to be wondering why I needed this, here’s why: I was writing a mode which I wanted to have undo capabilities, but it was so atypical (basically it involved changing the current buffer to read-only and using some custom commands to edit the contents of another buffer, however strange it may sound!) that the usual undo command was useless there.

And, if you happen to be wondering what strange mode I was coding… Well, I will talk about it very soon on this blog, but if you really want to know now, there is a certain book about Elisp you might want to check out;-). (Note that as of today, this particular command isn’t yet used there – it is a part of a section-in-progress.)

CategoryEnglish, CategoryBlog, CategoryEmacs