30

After a few years customizing my .emacs file, I find I used two different kinds of constructs to setup major-mode-specific key bindings:

1. using a hook and local-set-key. For example:

(defun my/bindkey-recompile ()
  "Bind <F5> to `recompile'."
  (local-set-key (kbd "<f5>") 'recompile))
(add-hook 'c-mode-common-hook 'my/bindkey-recompile)

I would say this construct makes it easy to use the same key bindings for different major-modes by adding the same function to all relevant major-mode hooks (in other words, the "which keybindings do I want" is clearly separated from the "for which modes do I want them"). However, I'm not comfortable with the fact that such customizations are done at the buffer level, whereas I would think they belong to the major mode.

2. using define-key (often combined with eval-after-load to delay evaluation until the relevant keymap is loaded). For example:

(eval-after-load "cc-mode"
  '(progn
     (define-key c-mode-map   (kbd "C-c o") 'ff-find-other-file)
     (define-key c++-mode-map (kbd "C-c o") 'ff-find-other-file)))

By contrast, this construct customizes the major-mode itself, but is less flexible: if I want to use the same key bindings for another mode, I will have to find the correct file and keymap names for this mode, and almost duplicate the eval-after-load expression (although this could probably be automated with a function/macro).

Question: although both construct types work well and produce the result I want, they are technically very different, setting-up the key bindings in different keymaps at different times. So my question is: among these two constructs, is there a "preferred/better" way to do things? (Or maybe the "best" construct is a third one which I'm not aware of?)

By "preferred/better", I mean such things as:

  1. less prone to break with new emacs versions
  2. less prone to disturb/be disturbed by active minor-modes
  3. more idiomatic / readable / shareable with others
François Févotte
  • 19,520
  • 4
  • 51
  • 74
  • I discovered the same thing many years later, when I tried to make a major mode that coped with the minibuffer window, that has its own key table and where was compulsory the `local--`. – alinsoar Oct 05 '21 at 13:19

2 Answers2

24

I believe the two approaches you describe are less different than you think.

Notice that local-set-key does in fact evaluate (define-key map key command) where map is the value of (current-local-map), which is typically set by the major mode.

So although they could be doing different things; most of the time the only real difference will be that the hook function with the local-set-key call will be setting that same key repeatedly/redundantly, whereas the other approach sets it only once.

You can demonstrate this to yourself by using local-set-key in a mode hook, removing that hook function after it has been used, and then creating a new buffer in that same major mode, and testing the binding.

less prone to break with new emacs versions

I guess you could argue that the name of a keymap might change in future and therefore not needing to know the name is an advantage, but you could equally say the name of the mode hook might change. I don't think either is enough of a concern to worry about.

One thing to note is that local-set-key will work even if the major mode did not establish a current-local-map, which I guess makes it slightly more robust as far as generalised approaches go.

less prone to disturb/be disturbed by active minor-modes

There's no difference. All minor mode keymaps take precedence over all major mode keymaps, and neither approach is going to have any effect on the order of minor-mode-map-alist (which determines the precedence of minor mode keymaps).

more idiomatic / readable / shareable with others

They're both entirely readable to my mind, so I can't distinguish them in this aspect either.

I say just use whichever approach seems best to you in each context. I do think it's good to have a standard approach for most things for the sake of consistency in your code, but I doubt it matters which one you choose. There's obviously a saving of a few CPU cycles to be had by not evaluating the same code unnecessarily, but that should matter so very little as to be of no concern whatsoever.

I think the most obvious case for one over the other is the one you already mentioned -- if you want to apply the same binding to multiple modes using a common hook (but not to all modes -- for that I thoroughly recommend creating a custom minor mode), then a local-set-key within that hook is definitely the way to go.

Community
  • 1
  • 1
phils
  • 71,335
  • 11
  • 153
  • 198
  • 4
    +1 for the mention that the current local keymap is actually the major-mode-map. Now that I re-read the documentation of `current-local-map` it's pretty clear, but for some reason I had always thought that the local keymap was specific to a buffer and did not impact other buffers using the same major-mode. – François Févotte Mar 22 '12 at 10:11
2

I have lots of custom keyboard commands and I couldn't bother with various ways to set them in Emacs and all these keymaps overriding each other, so I just installed John Wiegley's bind-key as per my relevant answer.

(require 'bind-key)
(bind-key "C-l" 'goto-line)
Community
  • 1
  • 1
Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166