Using ELPA already will simplify your config file. You no longer have to change the
load-pathfor each package. All ELPA packages will be on the
Furthermore, some packages are specifically designed to give you a sane startup environment, notably the
starter-kitpackage. Just downloading the package from ELPA will get you its extra initialization.
However, it'd be best to not have to do any manual downloading for core packages that make up the customization we want. It'd be nice if we can set up our
~/.emacs.d/directory on a new machine from git, and just start emacs and have everything set up for us.
Right now, in my initialization file, I have the following code that will set up packages on a new machine:
(require 'package) (setq package-archives '(("ELPA" . "http://tromey.com/elpa/") ("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.milkbox.net/packages/") ("marmalade" . "http://marmalade-repo.org/packages/") ("technomancy" . "http://repo.technomancy.us/emacs/"))) ;; Package setup, taken from ;; https://github.com/zane/dotemacs/blob/master/zane-packages.el#L62 (setq ash-packages '(ace-jump-mode anaphora autopair bang color-theme-solarized cppcheck dart-mode dynamic-fonts expand-region fill-column-indicator flex-autopair flymake-cursor flyspell-lazy font-utils idle-highlight-mode ido-ubiquitous jabber js2-mode key-chord list-utils magit oauth2 paredit rainbow-delimiters rainbow-mode smex starter-kit undo-tree yasnippet )) (package-initialize) ;;; install missing packages (let ((not-installed (remove-if 'package-installed-p ash-packages))) (if not-installed (if (y-or-n-p (format "there are %d packages to be installed. install them? " (length not-installed))) (progn (package-refresh-contents) (dolist (package not-installed) (package-install package))))))First, this code loads the package code with a
requirestatement, then sets the list of ELPA archives I use. Then, it sets up a list of packages I'd like to install in the variable
ash-packages. After a necessary initialization, it looks at each of the packages, and for each one that isn't already installed, it installs it from ELPA. As the comment mentions, I took this whole thing from zane's dotemacs repository. As I mentioned in an earlier post, stealing other people's customizations is a time-honored emacs tradition.
Further down in the file, you will see that for some of these packages, some basic configuration happens. I try to group all configuration for a package together. It would be nice if we could specify the package for download and the configuration together, since then we could remove an unwanted package and all configuration at once. I don't think this is worth it, though, at least right now. Maybe I'll pursue this in a further blog post.
Finally, it's worth noting that I do all my customization in
eval-after-loadfunctions. The purpose of those functions is that the contents of them are loaded only when the package is required or autoloaded, which avoids issues with missing functions or variables. If the package is already loaded, then everything is evaluated immediately. It's also important note that these are written with the function to execute quoted (since we don't want to execute it right away).
For example, here's something that I use to setup the
(eval-after-load 'key-chord '(progn (key-chord-mode 1) (key-chord-define-global "jk" 'dabbrev-expand) (key-chord-define-global "l;" 'magit-status) (key-chord-define-global "`1" 'yas/expand) (key-chord-define-global "-=" (lambda () (interactive) (switch-to-buffer "*compilation*"))) (key-chord-define-global "xb" 'recentf-ido-find-file) (key-chord-define-global "xg" 'smex) (key-chord-define-global "XG" 'smex-major-mode-commands) (key-chord-define-global "fj" 'ash-clear)))
When a file that provides
key-chordis loaded, this will execute. If
key-chordis already loaded when this
eval-after-loadis evaluated, it will execute immediately.
Sometimes you want to have only load things when two packages are simultaneously loaded. Then you can nest one
eval-after-loadin another. I do that in that following elisp:
(eval-after-load 'multiple-cursors '(progn (global-set-key (kbd "C-c m m") 'mc/edit-lines) (global-set-key (kbd "C-c m a") 'mc/edit-beginnings-of-lines) (global-set-key (kbd "C-c m e") 'mc/edit-ends-of-lines) (global-set-key (kbd "C-c m r") 'mc/set-rectangular-region-anchor) (global-set-key (kbd "C-c m =") 'mc/mark-all-like-this) (global-set-key (kbd "C-c m n") 'mc/mark-next-like-this) (global-set-key (kbd "C-c m p") 'mc/mark-previous-like-this) (global-set-key (kbd "C-c m x") 'mc/mark-more-like-this-extended) (global-set-key (kbd "C-c m u") 'mc/mark-all-in-region) (eval-after-load 'key-chord '(progn (key-chord-define-global "zm" 'mc/edit-lines) (key-chord-define-global "za" 'mc/edit-lines) (key-chord-define-global "ze" 'mc/edit-lines) (key-chord-define-global "zr" 'set-rectangular-region-anchor) (key-chord-define-global "z=" 'mc/mark-all-like-this) (key-chord-define-global "i\\" 'mc/mark-all-like-this) (key-chord-define-global "zn" 'mc/mark-next-like-this) (key-chord-define-global "zp" 'mc/mark-previous-like-this) (key-chord-define-global "zx" 'mc/mark-more-like-this-extended) (key-chord-define-global "zu" 'mc/mark-all-in-region)))))
This is important! If we were to to put all the
key-chord-define-globalin the top-level
eval-after-load, then if
key-chordwere not downloaded from ELPA, or not loaded for some reason, this would break, throwing an error that
key-chord-define-globalis undefined. Doing things this way is much safer.
However, you can't do this all the time. If we were to take this statement:
(eval-after-load 'key-chord '(progn (key-chord-mode 1) (key-chord-define-global "l;" 'magit-status)))and turn it into this:
(eval-after-load 'key-chord '(progn (key-chord-mode 1) (eval-after-load 'magit '(key-chord-define-global "l;" 'magit-status))))
That would pose an issue. It would protect us against the case where the
magitpackage isn't installed, and therefore we shouldn't have a keychord defined. However, if it was installed, we wouldn't have a keybinding, and unless we run
(require 'magit), this keybinding wouldn't appear. We don't really want to run that, though, because an autoload is more efficient, and
magit-statusis an autoload. That means that the
magitpackage may be unloaded, but we still have the entry functions from the package. When the user invokes any of those commands, then
magitis loaded at that point.
Autoloads make our initialization files load much quicker. I think it's better to use them then to
requireeverything, so I don't have nesting
eval-after-loadstatements for anything autoloaded.
Here's the lessons we can take from this:
- Set up your ELPA such that you have a good set of
- Set things up so that you have a list of must-have ELPA packages, which are downloaded when they don't exist.
- Customize through
eval-after-load, and keep all your customization for a package in one place.
- When you are customizing things that only exist when two packages
are loaded at the same time, use nested
- The exception to the above rule is autoloads. There is no need
to nest an
eval-after-loadfor an autoloaded function. You can usually just assume that any major function in a package is autoloaded, but if you want to check, jump to the source and look for a comment above that says