13

I want to add a specific key binding to latex-mode:

(define-key latex-mode-map (kbd "<f6>") 'my-latex-defun)

At the same time I want to set it only when the specific mode is loaded. So I would like to know if there is a preference (best practice) between using the appropriate hook (latex-mode-hook) to accomplish that or using the clause (with-eval-after-load "tex-mode" ... to wrap the key binding definition. I tested both and they work fine, however I wonder which one is the right way to do it.

3
  • 2
    with-eval-after-load is loaded once, so a keymap-change goes there. Hooks load every time the mode is run, e.g. on loading a new file associated with the mode, or however the hook is defined. You would load e.g. flyspell-mode or hl-line-mode in a hook. In my init.el I use with-eval-after-load approximately 160 times and add-hook 110 times. A lazy init is a good init (IMO).
    – rasmus
    Commented Oct 26, 2014 at 23:32
  • @rasmus sounds like an answer if you have the time :) The distinction of how many times the code is run is the main one to consider, in my opinion. (It's also the only one I can think of…) Commented Oct 26, 2014 at 23:44
  • For why it is a bad idea to change key bindings in mode hooks see emacs.stackexchange.com/questions/990/…
    – tarsius
    Commented Oct 27, 2014 at 0:38

2 Answers 2

19

A lazy init is a good init.

(IMO)

When to use with-eval-after-load

with-eval-after-load is loaded once when a certain feature or file is first loaded, so a keymap-change clearly goes inside one of these. Not the least because the keymap may not be known at init-time [try something like (define-key message-mode-map (kbd "C-c f") 'Footnote-add-footnote) in emacs -q]. A hook is not a nice solution here since it binds the function to a key every time the hook is started. As tarsius notes, you can read more about why not to local-set-key in hooks.

Finally, note that with-eval-after-load of GNU Emacs 24.4 is a 2 line wrapper around eval-after-load where body need not be quoted.

When to use hooks

Hooks are list of functions loaded every time a criterion is met, e.g. a certain mode is started. A common use of hooks is to load minor-modes, such as flyspell-mode or hl-line-mode. E.g. (add-hook 'org-agenda-mode-hook 'hl-line-mode). As Rémi points out, add-hooks is smart and will do the right thing even if the hook-variable has yet to been loaded. Nonetheless, I have many clauses like the following, which may be negligible for speed-gains, but does gives a sense of organization and dependency-structure:

(with-eval-after-load 'org-agenda
  (add-hook 'org-agenda-mode-hook 'hl-line-mode))

Why add to hook after org-agenda? As always, C-h v org-agenda-mode-hook C-j delivers. The hook is defined in org-agenda.el as shown in *help*.

Loading extra features with-eval-after-load

with-eval-after-load is also important for loading extra features. You'd probably want something like (with-eval-after-load 'org (require 'org-inlinetask)) to load org inlinetasks. To see why (find-library "org-inlinetask"). Since org-inlinetasks.el directly (require 'org), all of the nice autoload that your friendly Emacs maintainers took great care to provide will be "ignored" and all of org.el(c) will be loaded.

But what if your (personal) defun is required several places? If you are really picky, you could put the defuns in another file in your load-path and add autoload cookies, or you can tell Emacs where to find the function with the autoload function. Then something like this would work:

(autoload 'org-cdlatex-mode "org" "cdlatex mode from org.")
(with-eval-after-load "latex"
  (add-hook 'TeX-mode-hook 'org-cdlatex-mode))

Or you could just require org as it in turn would pull cdlatex

And, honestly, personal defun ain't gonna matter much for init time most of the time. The 1150 lines of personal defuns (84 defuns) in my init.el adds 0.02s over vanilla Emacs.

Measuring initialization time

An easy way to approximate intitialization time is

time emacs --eval "(kill-emacs)"

(benchmark against emacs -q.

But to get a more detailed approximation of where the initialization bottlenecks are check out Joe Schafer's esup.

Finally, loading time does not disappear magically. You are only pushing it forward (to the extend that you use all features in each session).

3
  • 1
    You don't need to put add-hook in a with-eval-after-load, because add-hook will bind the variable if it's not already there.
    – Rémi
    Commented Oct 27, 2014 at 7:11
  • You are right! Could there still be speed gains from it though? [I will try to update the answer later to reflect this]
    – rasmus
    Commented Oct 27, 2014 at 8:05
  • What about defuns that later I would like to bind to a hook or key binding. Should I define a defun outside the with-eval-after-load without loosing lazy initialization advantages? Commented Oct 27, 2014 at 20:03
5

In addition to your suggestions of eval-after-load and using a hook, let me suggest use-package.el. You can do what you need to with

(use-package latex
  :ensure auctex
  :bind ("<f6>" . my-latex-defun))

LaTeX won't be loaded until after you request it.

Just as a side note: careful use of use-package has cut my startup times down to under a second. (It used to take around four seconds.)

2
  • Thank you for your suggestion. I've also contemplated to use use-package but I really want to figure it out how to do it without additional packages. Commented Oct 26, 2014 at 21:19
  • 1
    @jrbalderrama note that use-package is set to ship with Emacs 25. Commented Feb 15, 2016 at 23:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.