80

Is there a difference between setting things for a mode using eval-after-load and using the mode hook?

I've seen some code where define-key is used inside a major mode hook, and some other code where define-key is used in eval-after-load form.


Update:

For better understanding, here is an example of using eval-after-load and mode hooks with org-mode. The code can run before (load "org") or (require 'org) or (package-initialize).

;; The following two lines of code set some org-mode options.
;; Usually, these can be outside (eval-after-load ...) and work.
;; In cases that doesn't work, try using setq-default or set-variable
;; and putting them in (eval-after-load ...), if the
;; doc for the variables don't say what to do.
;; Or use Customize interface.
(setq org-hide-leading-stars t)
(setq org-return-follows-link t)

;; "org" because C-h f org-mode RET says that org-mode is defined in org.el
(eval-after-load "org"
  '(progn
     ;; Establishing your own keybindings for org-mode.
     ;; Variable org-mode-map is available only after org.el or org.elc is loaded.
     (define-key org-mode-map (kbd "<C-M-return>") 'org-insert-heading-respect-content)
     (define-key org-mode-map (kbd "<M-right>") nil) ; erasing a keybinding.
     (define-key org-mode-map (kbd "<M-left>") nil) ; erasing a keybinding.

     (defun my-org-mode-hook ()
       ;; The following two lines of code is run from the mode hook.
       ;; These are for buffer-specific things.
       ;; In this setup, you want to enable flyspell-mode
       ;; and run org-reveal for every org buffer.
       (flyspell-mode 1)
       (org-reveal))
     (add-hook 'org-mode-hook 'my-org-mode-hook)))
Yoo
  • 17,526
  • 6
  • 41
  • 47
  • 7
    +1 for *"org" because C-h f org-mode RET says that org-mode is defined in org.el*. I was struggling getting `eval-after-load` to actually evaluate for `nxml-mode`, and this tip worked! – Zach Young Oct 12 '14 at 19:40

1 Answers1

112

Code wrapped in eval-after-load will be executed only once, so it is typically used to perform one-time setup such as setting default global values and behaviour. An example might be setting up a default keymap for a particular mode. In eval-after-load code, there's no notion of the "current buffer".

Mode hooks execute once for every buffer in which the mode is enabled, so they're used for per-buffer configuration. Mode hooks are therefore run later than eval-after-load code; this lets them take actions based upon such information as whether other modes are enabled in the current buffer.

N.N.
  • 8,336
  • 12
  • 54
  • 94
sanityinc
  • 15,002
  • 2
  • 49
  • 43
  • 1
    On a side note (correct me if I'm wrong): emacs-lisp-mode and lisp-mode seem to get loaded before custom eval-after-load scripts get to be executed. So in that case one might indeed need to use a mode hook instead. – balu Jan 10 '14 at 02:35
  • 1
    Yes: the `eval-after-load` block is always `eval`'d *after* the related library is `load`ed. But note that the code will always be executed *before* any functions in the related library are called. So if you `(eval-after-load 'lisp-mode ...)`, then the `...` code in this block will be run before the `lisp-mode` function in `lisp-mode.el` is called. – sanityinc Jan 10 '14 at 11:17
  • Sorry for the wording. Of course, the libraries are loaded before eval-**after**-load is executed. But as for emacs-lisp-mode and lisp-mode they don't seem be executed at all: I tried to modify their keymaps which worked fine in a mode hook but not in an eval-after-load block (nothing would change). With python-mode and other modes, however, everything works fine. So my guess was that the emacs-lisp and lisp modes are exceptions and are somehow builtin, in the sense that they are loaded before your Emacs config is even parsed. – balu Jan 10 '14 at 16:55
  • Odd. I have [code which does exactly that](https://github.com/purcell/emacs.d/blob/master/lisp/init-lisp.el#L26), and it works fine. There's nothing special about those modes. – sanityinc Jan 11 '14 at 12:04
  • 1
    What does `after-load` do exactly? Is there a difference to `eval-after-load`? – balu Jan 11 '14 at 20:59
  • 6
    It's just a local macro wrapper for `eval-after-load`, to avoid the need to quote the form passed to `eval-after-load`. ie. instead of `(eval-after-load 'foo '(progn (foo) (bar)))` I can write `(after-load 'foo (foo) (bar))`. – sanityinc Jan 12 '14 at 11:30
  • 2
    @balu: `emacs-lisp-mode` and `lisp-mode` are dumped with emacs and never loaded. – sds Oct 31 '14 at 16:00
  • @sds: Thanks, that confirms my suspicion. – balu Nov 05 '14 at 18:02
  • But the doc of `eval-after-load` says "If a matching file is loaded again, FORM will be evaluated again." Doesn't that mean the body of `eval-after-load` is evaluated every time such a file is opened? – Joseph Tesfaye Nov 04 '20 at 09:28
  • @IvanHuang I think that's correct, but generally files get loaded only once, since `require` avoids `load`-ing a file that has already `provide`-d the desired feature, so in practice I don't think this is often an important consideration. – sanityinc Nov 05 '20 at 21:57
  • @sanityinc Does that mean say if I have a opened an org file and when I open another org file it won't be evaluated again? – Joseph Tesfaye Nov 06 '20 at 11:06
  • @IvanHuang Yes. Opening another org file causes org-mode functions to run again, but org itself will only be loaded once. – sanityinc Nov 08 '20 at 20:47
  • Why is `(eval-after-load "dired" (define-key ...` necessary instead of just the `(define-key ...` in an init file? – user129393192 Jul 08 '23 at 17:50