3

Let's say I want to customize a keybind for a mode that hasn't been loaded yet like so:

(define-key eshell-mode-map (kbd "C-M-i") 'eshell-previous-input)

But the problem is if I haven't yet loaded eshell I get this error:
Debugger entered--Lisp error: (void-variable eshell-mode-map)

How would I make the keymap visible without opting for the mode-hook eshell-mode-hook?
Are there multiple ways of achieving this?
If so, what is the standard way of doing so (loading files to make variables/functions/keymaps visible)?

Also, what would the implications be if I decided to load the file manually as opposed to using a mode-hook?

Drew
  • 75,699
  • 9
  • 109
  • 225
John DeBord
  • 550
  • 3
  • 13

2 Answers2

5

@phils gave you what is almost always the correct answer, that is, it is the right answer for most major and minor modes; but in the case of eshell you need to do something else:

(add-hook 'eshell-mode-hook
  (lambda ()
    (define-key eshell-mode-map (kbd "C-M-i") 'eshell-previous-input)))

The reason is that, unlike most modes, eshell creates its keymap very late, in fact, it does it every time you activate the mode. In the file esh-mode.el, inside the (define-derived-mode eshell-mode ...) definition, you'll find:

;; FIXME: What the hell!?
(setq-local eshell-mode-map (make-sparse-keymap))
(use-local-map eshell-mode-map)
Omar
  • 4,732
  • 1
  • 17
  • 32
  • 1
    That's certainly unusual. It goes to show that there are always special cases! Good answer. I would just recommend using a named function instead of a lambda for the hook, to make it easier to [modify](https://emacs.stackexchange.com/a/18886/454) and [inspect](https://stackoverflow.com/a/27264339/324105). – phils Sep 19 '18 at 13:49
3

You don't :) At least, not for this use-case.

Instead you defer your configuration until such time as the library in question has been loaded:

(with-eval-after-load "esh-mode"
  (define-key eshell-mode-map (kbd "C-M-i") 'eshell-previous-input))

Note that C-hv eshell-mode-map provided me with the "esh-mode" library name.


If you did want to "make all variables and functions of a mode visible without activating the mode" then you would require the library. e.g.:

(require 'eshell)

Also, what would the implications be if I decided to load the file manually as opposed to using a mode-hook?

If you load the file, then you incur the cost of doing so. If that's happening at init time, then you've slowed down your start-up time slightly. If it's possible that you won't even use eshell in a given session, then this probably isn't what you want to do. (Or if you invariably run emacs as a server which starts when your system boots, then maybe you do want to pre-load things to make it that much more responsive later on, should you happen use that feature -- your call.)

If you use a mode hook then you are once again deferring the evaluation -- but rather than evaluating the code once, you are (re)evaluating it every time any buffer enters that mode. In this particular use-case we only need it to happen once, so with-eval-after-load is more appropriate.

phils
  • 48,657
  • 3
  • 76
  • 115
  • 4
    You gave what is generally the right answer (i.e., would be correct for most modes), but `eshell` does things the hard way. :) For some reason, `eshell` actually creates its keymap in the `eshell-mode` command and sets it with `setq-local`. (Search for `;; FIXME: What the hell!?` in `esh-mode.el`.) This means that to redefine `eshell-mode-map` keys you need to do it everytime the mode is activated, in `eshell-mode-hook`, for example. – Omar Sep 19 '18 at 12:39