eval-after-load vs. mode hook
Asked Answered
D

1

83

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)))
Durra answered 29/4, 2010 at 9:20 Comment(1)
+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!Glassblowing
L
114

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.

Lining answered 29/4, 2010 at 9:30 Comment(12)
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.Baronetcy
Yes: the eval-after-load block is always eval'd after the related library is loaded. 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.Lining
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.Baronetcy
Odd. I have code which does exactly that, and it works fine. There's nothing special about those modes.Lining
What does after-load do exactly? Is there a difference to eval-after-load?Baronetcy
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)).Lining
@balu: emacs-lisp-mode and lisp-mode are dumped with emacs and never loaded.Bulrush
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?Neuralgia
@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.Lining
@Lining 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?Neuralgia
@IvanHuang Yes. Opening another org file causes org-mode functions to run again, but org itself will only be loaded once.Lining
Why is (eval-after-load "dired" (define-key ... necessary instead of just the (define-key ... in an init file?Sissie

© 2022 - 2024 — McMap. All rights reserved.