15

Assume that I have downloaded a major mode called magical-mode, and it has its own magical keymap called magical-mode-map. This mode also provides a hook magical-mode-hook which is run each time magical-mode becomes the major mode of a buffer. Now I want to modify my init file to add a few custom key bindings to use in that mode.

It seems that there are (at least) two ways to set up custom key bindings for magical-mode. The one that I see most commonly is this:

(defun my-magical-keys ()
  (local-set-key (kbd "C-i") 'previous-line)
  (local-set-key (kbd "C-k") 'next-line)
  (local-set-key (kbd "C-j") 'backward-char)
  (local-set-key (kbd "C-l") 'forward-char))
(add-hook 'magical-mode-hook 'my-magical-keys)

But it is also possible to do it this way:

(define-key magical-mode-map (kbd "C-i") 'previous-line)
(define-key magical-mode-map (kbd "C-k") 'next-line)
(define-key magical-mode-map (kbd "C-j") 'backward-char)
(define-key magical-mode-map (kbd "C-l") 'forward-char)

The second method actually seems cleaner to me. Are there any advantages to doing it one way over the other?

Drew
  • 75,699
  • 9
  • 109
  • 225
nispio
  • 8,175
  • 2
  • 35
  • 73
  • I use the same keys for the basic movement commands. Be warned: this is an uphill battle and you might want to learn a little bit more about key bindings before you start doing this. – tarsius Oct 10 '14 at 00:58
  • @tarsius An uphill battle indeed. I have gone down that path before, but now I am back to good ol' `C-n` and `C-p`. The example is just dummy code. I wanted to come up with some very simple example modes and example bindings, precisely so that the bindings themselves would not distract from the actual purpose of the question. – nispio Oct 10 '14 at 13:49
  • the second way doesn't actually work without either `add-hook` or `require`. [See here](https://emacs.stackexchange.com/q/35318/18688) – xdavidliu May 22 '22 at 17:54

4 Answers4

17

The second approach is preferable as it modifies the mode's keymap just once.

If you do it using the mode's hook then that will be called every time that mode is enabled in some buffer. Doing so again usually won't actually have an effect because the keys are just again bound to what they are already bound to. Major mode keymaps are "local" to the major mode not the individual buffers which use that mode, so if you change a binding in one of these buffers using local-set-key then that affects all buffers with the same major mode.

local-set-key is primarily intended to be used as a command. Once you have determined that you would like to make some change persistent, use define-key with the mode keymap as the first argument.

If you use a hook to modify the keymap over and over again then that could conflict with the intended use of local-set-key. Say you used M-x local-set-key RET C-i fancy-previous-line RET because you want to tryout that variant of previous-line. If you now open a new buffer which uses the same major mode, then the hook would run again and override your temporary binding, in all buffers using that major mode, including the buffer in which you did previously use local-set-key.

tarsius
  • 25,298
  • 4
  • 69
  • 109
  • I like this answer but what if the mode is autoloaded? – remvee Oct 10 '14 at 15:30
  • 2
    You can delay loading any code until after some library has been loaded: `(eval-after-load 'magical '(progn (define-key magical-mode-map ...) ...))`. – tarsius Oct 10 '14 at 15:34
4

Using (define-key my-magical-mode-map …) is the normal way.

When you use a hook and local-set-key, the keys are added each time you enter My Magical mode in some buffer. This is weird because local-set-key affects all buffers that are in the same mode (more generally, all buffers using the same keymap). So if you've made any changes to the keymap, they will be overridden each time you enter My Magical mode in a buffer.

The second method may also be confusing if you customize the keymap in different places. The hooks are executed in the reverse order from the order they're added in, and until the first time they are executed, you won't see any trace of your customizations.

  • but if you just say `define-key` like you did, it doesn't actually work; you would still need a mode hook or `require` at beginning or else it will complain that `my-magical-mode-map` has symbol not found – xdavidliu May 22 '22 at 17:55
  • @xdavidliu No, you wouldn't put that in a mode hook: there's no point in running it each time you open a file in that mode. You'd typically put it in an [`eval-after-load`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks-for-Loading.html) form or similar. – Gilles 'SO- stop being evil' May 22 '22 at 18:26
2

You are apparently not asking about defining a major-mode keymap but about user code to add or change a few keybindings in an existing major-mode keymap. You say "custom", which suggests this, but we might as well make it clear.

To be sure, what you say you see most commonly for this is not what is used generally to define a major-mode keymap. It is not what you find in the Emacs source code, for instance. And it is not what is recommended in the Elisp manual (node Major Mode Conventions).

Just wanted to get that out of the way, to be clear for others: you generally do not want to use the mode hook to define the major-mode map.


To your question about user key customizations -

In any case it is not local-set-key that you should use in a mode hook. Just use define-key with the major-mode keymap, exactly as in your first example. @tarsius has already explained this well.

Other than that, the answer is: it makes little difference, in general, whether you bind the keys (using define-key with the mode map) once and for all or you use the hook to bind them each time you enter the mode.

But it can make a difference if bindings in the map change - for example, by your loading some other code that changes them. In that case, putting bindings on the major-mode hook ensures that when the mode is entered the bindings will be established. That is, it ensures that they will be made, but it does not ensure that nothing else changes them afterward (e.g. another function on the same hook, invoked afterward). Remember that you have little control over what gets run on a hook and when - unless, of course you are sure that only your own code messes with it.

That is the only difference in effect, that I can think of. For you to decide when you consider that difference to be an advantage of one or the other approach. FWIW, looking at my own code, I don't think I ever bind keys on a mode hook.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • Thanks. I have modified the question to hopefully clarify the fact that I am not *creating* a major mode, simply adding my own key bindings to and *existing* major mode. – nispio Oct 10 '14 at 13:55
0

Your naming is little confusing (I think you should remove my in your second part of the question).

Anyway assuming my-magical-keys is a user customizing function of magical-mode, I see one obvious advantage. It is easy to remove (by remove-hook) hook in a single go.

The second advantage is what functions are meant for. I mean they are reusable. you can hook them to other modes.

Edit:

As @tarsius has pointed out, removing hook won't restore original behavior and turning the function into a minor mode may be better.

kindahero
  • 528
  • 4
  • 11
  • I am customizing a hypothetical major mode called `my-magical-mode`. However, if the use of the `my-` prefix is confusing I can certainly edit the question. – nispio Oct 10 '14 at 00:39
  • Yes, that would be better, usually (atleast as I see in the wild) `my-` is added for user functions. – kindahero Oct 10 '14 at 00:42
  • 1
    Agreed. I just applied the `my-` so that nobody would think that I was asking how to configure a real mode called `magical-mode` (if it exists). – nispio Oct 10 '14 at 00:45
  • 1
    No, removing the hook won't restore the old bindings. At least not until Emacs is restarted, and then I don't see having to comment out only one line instead of four as such a big improvement. – tarsius Oct 10 '14 at 00:52
  • 2
    As for the second advantage you mention: here it would be preferable to create a minor mode which could then be enabled for various major modes and/or only some of the buffers using a particular major mode. – tarsius Oct 10 '14 at 00:53
  • @tarsius Thanks, You are right, I edited the answer to mention your point. – kindahero Oct 10 '14 at 01:02