October 04, 2012

Managing Projects in Emacs

I use the word projects in it’s loosest possible sense. After evaluating what I needed from a project management workflow I’ve boiled it down to the following three points

  • changes the working directory of my editor
  • loads the correct ctags file and regenerate it if necessary
  • gets out of my way

The majority of the projects I work in are Ruby projects, mostly Rails but not always and I always keep my tags file in tmp/tags so there’s not a lot of variation in the process.

This is useful as it allowed me to put together a few quick functions in my .emacs.d to make life easier.

The entire process revolves around the function open-ruby-project which I wrote as follows

 (defun open-ruby-project (name base-path capistrano)
   "Open the project and reload the tags file if necessary"
   (setq app-path (expand-file-name
                   (concat base-path app-name (if capistrano "/current"))))
   (cd app-path)
   (ensure-presence-of-tmp)
   (unless (file-readable-p "tmp/TAGS")
       (regenerate-tags-file))
   (visit-tags-table "tmp/TAGS")
   (message (concat "Opened Project " app-name)))

Hopefully this is pretty easy to read. It takes a name and a base path for the application and jumps to that directory, loads the tags file (regenerating it if it’s not there) and lastly displays a message telling me that it’s finished. The capistrano variable can be passed in to append the '/current' directory, required for a project that’s been deployed using cap.n

The regenerate-tags-file function is pretty Ruby specific at the moment, as it’s unintelligently shelling out to etags, but it does the job :) For now.

 (defun regenerate-tags-file ()
   "Generates a TAGS file in tmp relative to the current working dir"
   (interactive)
   (save-window-excursion
     (shell-command "etags --Ruby-kinds=-Ff -o tmp/TAGS -R .")))

The call to save-window-excursion call was inserted to stop the shell-command output buffer appearing, as I found the output distracting.

This whole process I have then wrapped up into a couple of interactive calls depending on whether I want to open a work project or a personal one. The syntax of the (interactive) function and how to pass args is one of the things I have found most confusing and arcane about Emacs so far and pretty much deserves a blog post on it’s own.

 (defun open-personal-project (name)
   "Changes the working directory to a personal project and visits the tags table"
   (interactive "sopen project: ")
   (setq code-dir "~/code/personal/")
   (setq app-name name)
   (open-ruby-project app-name code-dir nil))

And that’s that! This works particularly well for me as I follow some conventions as to how I lay out my ~/code directory. Personal projects live in personal, Work stuff lives in work and checkouts of other people’s stuff lives in vendor.

I’m still new to Emacs so any constructive critisism or ways I can streamline these functions would be more than welcome.

As always my full Emacs config is available on Github