Emacs 24 includes many improvements over 23, but there is one particular addition that makes me run around and go crazy with joy: a built-in package management system, ELPA (Emacs 24 is still in development, Bozhidar Batsov has a good guide on how to get it set up). I switched over to Emacs almost a year ago, searching for something that would give me an IDE with the following attributes:

  • Functionality (context-based completion, on the fly syntax checking)
  • Customization (key bindings, easily extensible)
  • Portability (minimal setup on new environments)

There are a lot of nice extensions that do well for the first two. However, Portability was always tricky. To get some of the more power coding features in Emacs, one needed to install large packages, and there was no way to move these around short of zipping the whole thing up or finding and installing all these packages again.

ELPA completes the trifecta I have been looking for. It was now easy to have a list of packages to install. I have a GitHub repository to contain all of my .emacs setup, so I can just clone a repository with every new environment. To make the setup completely automatic, I needed a method to automatically install packages that did not exist. After a little research, I was able to figure it out:

;; Packages to install first (check if emacs is 24 or higher)
(if (>= emacs-major-version 24)
    (progn
        ;; Add a larger package list
        (setq package-archives '(
            ("ELPA" . "http://tromey.com/elpa/")
            ("gnu" . "http://elpa.gnu.org/packages/")
            ("marmalade" . "http://marmalade-repo.org/packages/"))
        )
        (package-refresh-contents)
        ;; Install flymake mode if it doesn't exist, then configure       (when (not (require 'flymake nil t))
            (package-install 'flymake))
            (global-set-key (kbd "C-; C-f") 'flymake-mode)
            ;; flymake-cursor
            (when (not (require 'flymake-cursor nil t))
                (package-install 'flymake-cursor)
            )
            ;; Install rainbow mode if it doesn't exist, then  configure
            (when (not (require 'rainbow-mode nil t))
                (package-install 'rainbow-mode)
            )
            (defun all-css-modes()
                (css-mode)
                (rainbow-mode)
            )
            (add-to-list 'auto-mode-alist '("\.css quot; . all-css-modes)
))

NOTE!!! This must be run after ALL OTHER INITIALIZATIONS are run! You can do this by placing it within a hook:

(add-hook 'after-init-hook '(lambda () (load "~/.emacs.loadpackages"))) ;; anything within the lambda will run after everything has initialized.

As you can see, I’ve put the above logic into a file called “.emacs.loadpackages”. This is so I can remove it at easy if I want a more bare environment.

I’d like to talk about this a little bit in detail. The first line ensures that emacs is version 24 or higher:

(if (>= emacs-major-version 24) PACKAGE_STUFF_HERE)

I then add more repositories to the package manager, gnu and Marmalade (the base package is a bit limited, in my opinion)

(setq package-archives '(
    ("ELPA" . "http://tromey.com/elpa/")
    ("gnu" . "http://elpa.gnu.org/packages/")
    ("marmalade" . "http://marmalade-repo.org/packages/")
))

This requires a refresh:

(package-refresh-contents)

And then onto the logic to see if a package exists! You can use require to see if a package exists, nullifying the error message it usually return by adding the true statement at the end. For example, this will return true when the package fly-make cursor is not installed:

(not (require 'flymake-cursor nil t))

You can then add this to a complete clause:

(when (not (require 'flymake-cursor nil t)) (package-install 'flymake-cursor))

And you’re done!

Issues #

There a couple of things I’m still working on regarding this setup. Although I haven’t gotten any environment breaking errors so far, there’s not a lot of error checking, so I’m sure it can break if things are not completely right. In addition, this does not work very well for portable programmers, as Emacs will try to initialize ELPA, resulting in an exception due to not being able to contact the server.

Please leave comments and suggestions!