One of the best things about Emacs is its extensibility. The bulk of its editing functions are written in the same language as the editor itself, Emacs Lisp, which you, the user, can program inside the editor itself.
Experienced Emacs users invariably find packages on the Web, maybe from EmacsWiki or github, and stuff them into their .emacs
file, loading them with (require)
. For a small number of small packages, this can often work well, but eventually you're going to either stumble across a large package like Org Mode, or for the polyglot programmer, load the major modes for half a dozen or more languages.
All those (load)
and (require)
calls will slow down the time taken to load your .emacs
file considerably. If you want to see how long it takes, M-x emacs-init-time
will give you the answer. Personally, I consider load times in excess of one or two seconds to be unacceptable for development-class hardware.
Loading What You Need, When You Need It
So how do you speed up loading of your init file, but at the same time keep everything that makes Emacs work for you? Simple: autoload
and eval-after-load
.
autoload
tells Emacs that a function is defined in a specific file, but doesn't actually load that file until it's explicitly asked for, e.g:
(autoload 'ruby-mode "ruby-mode")
This tells Emacs that the function ruby-mode
is in the file ruby-mode.el (or ruby-mode.elc, if appropriate). When the function is invoked, the path elements in load-path
are checked in order to find ruby-mode.el, which is then loaded on-demand.
Automatically Using the Right Mode for a File
So, how does ruby-mode
get called? Well, it's either explicitly (M-x ruby-mode
), or by specifying that it should be invoked when you load an appropriate file, like this:
(add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))
This says that Emacs should enter ruby-mode
whenever a file with the ".rb" extension is loaded. When it is, ruby-mode
is started, and the autoload
ensures that it is loaded from the appropriate file if required.
Customising Just-Loaded Code
But what happens if you want to do more than just load the major mode when .rb files are loaded? Maybe you also want to load yasnippet
, ace-jump
or other modules?
You've got two choices: a mode hook, or eval-after-load
. The former is cleaner, but the latter is more flexible, and available in the event that there's no mode hook variable for your language (e.g. scala-mode-hook
isn't available until after scala-mode
is loaded).
eval-after-load
takes an Emacs Lisp form as an unevaluated list and only evaluates it once the specified file has been loaded, e.g.:
(eval-after-load "ruby-mode" '(setup-autocompletion))
The time-saver here comes from the fact that the quoted form is evaluated after load, so it can potentially result in quite a lot of work getting done when it's loaded, and you'll only feel the effects when you enter that specific mode.
In this case, the same effect can be achieved by adding a function toruby-mode-hook
:
(add-to-list 'ruby-mode-hook #'setup-autocompletion)
Here, I'm assuming that (setup-autocompletion)
has already been defined and, as in my case, is referred to by multiple programming modes (nb: that function is one I've written myself; it's not part of Emacs, so you won't have it unless you've written one with the same name!). The form can be atribrarily complex, but it must be a single form. Multiple forms can be wrapped in a (progn)
to compose them. Here's an example for cperl-mode
that's more involved, setting some defaults, defining some Perl-specific functions, and doing that (setup-autocompletion)
again.
(eval-after-load "cperl-mode" '(progn (setq cperl-merge-trailing-else nil cperl-continued-statement-offset 0 cperl-extra-newline-before-brace t) (defun installed-perl-version () (interactive) (let ((perl (executable-find "perl"))) (if perl (shell-command-to-string (concatenate 'string perl " -e '($v = $]) =~ s/(?<!\\.)(?=(\\d{3})+$)/./g; print $v;'"))))) (defun use-installed-perl-version () (interactive) (let ((perl-version (installed-perl-version))) (if perl-version (save-excursion (beginning-of-buffer) (let ((case-fold-search nil)) (re-search-forward "^use [a-z]" (point-max) t) (beginning-of-line) (open-line 1) (insert (concatenate 'string "use v" perl-version ";")))) (message "Couldn't determine perl version")))) (setup-autocompletion)))
eval-after-load
isn't limited to programming modes. Here's an example of how you can auto-load your credentials when loading GNUS:
(eval-after-load "gnus" '(setq nntp-authinfo-file "~/.authinfo" gnus-nntp-server *my-nntp-server*))
Summary
So, there you have it:
auto-mode-alist
allows you to enter specific modes based on file extensionautoload
allows you to not not load that mode until it's actually required by opening a file of that typeeval-after-load
and/or(add-to-list {mode}-mode-hook)
can be used for mode customisation or setup, again only when the mode actually loads
As much as possible, ensure that your .emacs file makes minimal use of non-deferred (require)
or (load)
calls, although (require 'cl)
is an exception. It's also fair to load code/minor modes that you use all the time, since there's no point in deferring them just to load them the first time you do anything!
No comments:
Post a Comment