June 25, 2013

Managing my Emacs Addons with El-Get

Filed under: blogging — jonEbird @ 8:39 am

I like to say that putting my dot-files into a repo was one of the best things I’ve ever done. It was one of those moments that you say to yourself, “Why didn’t I do this years ago?” The process encouraged me to clean up my Emacs configs but there was still one thing that was bugging me. Whenever I perform a fresh checkout of my repository, after a fresh OS install, I would have to keep installing the packages (M-x list-packages) that were referenced in my config that was not yet installed. The process was:

10 Start Emacs with --debug-init
20 Note missing package
30 Install missing packages
40 GOTO 10

It’s annoying and definitely not ideal. Enter El-Get. Before I get too far into this article, there are people that will tell me that I could have probably defined a list of the packages I needed installed and have a small elisp function to ensure they are installed. I know this but I’ve also kept my eye on the El-Get project and have admired the flexibility that it provides. I described it to another Emacs user as putting a layer of abstraction on installing additional packages. Not only does it support installing packages from the normal ELPA / packages.el sources, you can use a handful of other really handy sources for your installs. I specifically like the idea of sharing personal Emacs solutions with other co-workers via a simple git repository. Being able to install solutions directly off of the EmacsWiki is pretty darn cool too.

Let’s get started with the El-Get configuration. From their Basic Setup section, you merely have to add the following to your .emacs file:

(add-to-list 'load-path "~/.emacs.d/el-get/el-get")

(unless (require 'el-get nil 'noerror)
    (goto-char (point-max))

(add-to-list 'el-get-recipe-path "~/.emacs.d/el-get-user/recipes")
(el-get 'sync)

That is technically all you need in order to get things up and running. Of course, the whole point in setting this up is to move our packages to be managed by El-Get. The current list of packages I typically install has been:

  • ace-jump-mode
  • yaml-mode
  • etags-select
  • etags-table
  • lua-mode
  • markdown-mode
  • cups
  • magit

Let’s see what I need to do to get these managed by El-Get. Let’s try adding the “cups” package via El-Get. The El-Get equivalent to “M-x list-packages” is “M-x el-get-list-packages” and then it acts the same way. Page through the list of packages, hit ‘i’ to mark a package for installation and then a final ‘x’ to execute the installation. Ah, but where is the “cups” package? It’s not there.

I need to tell el-get that it can find the “cups” package available via the ELPA sources. The way I’m going to do this is by defining an optional el-get-sources variable. I’ll skip forward a few and also tell you I ended up doing the same for “magit” as well after the recipe from el-get failed. I would also encourage you to lookup the documentation for the el-get-sources variable as it is quite thorough and instructional on how el-get recipes work:

; Establish that some packages come from ELPA, etc.
 '(el-get                   ; self-hosting
   (:name cups
          :type elpa)
   (:name magit
          :type elpa
          :after (progn ()
                   (global-set-key (kbd "C-x C-z") 'magit-status)))

With that in place, you can put together the complete list of your desired packages to install between the pre-established El-Get recipes and the ELPA packages. From other people’s examples, it seems like the convention is to set a separate variable for your personal package list and then tell El-Get to install them:

(setq jsm-packages
       '(cups ace-jump-mode yaml-mode color-theme color-theme-solarized lua-mode
              etags-select etags-table markdown-mode cups cl-lib magit htmlize)

       (mapcar 'el-get-as-symbol (mapcar 'el-get-source-name el-get-sources))))

(el-get 'sync jsm-packages)

You can also write your own recipes. Let’s go through an example of what it would look like by creating a recipes for the “chess” package. The “chess” package is another one that is currently provided via my ELPA sources but not from a pre-defined el-get recipe. When I run “M-x el-get-install ENTER chess ENTER”, I notice that is not available. I am now going to run “M-x el-get-find-recipe-file ENTER chess ENTER” and since we already know a recipe does not exist, a new file will be created with the .rcp extension in our ~/.emacs.d/el-get-user/recipes/ directory. If this is the first recipe you are creating, then the directory probably does not exist and you’ll want to do a “M-x make-directory” before saving the file. Our recipe is going to look very simple:

(:name chess
       :description "Play chess in Emacs"
       :type elpa)

I would again direct you to the variable documentation for el-get-sources (C-h v el-get-sources) on the various options you can use. There are a lot of them and I have honestly not explored many of them.

With our new recipe in place, we can install the package via el-get with either “M-x el-get-install” or by browsing the package lists via “M-x el-get-list-packages”. After you have successfully installed the desired package, do not forget to commit your new recipe and to add it to the list of packages to be installed. Next time you re-build your workstation / laptop, you can do a quick clone of your repository and el-get will handle the rest for you.