Emacs 23 has the ability to set local variables based on a file’s location. This supersedes earlier hacks such as project-local-variables.el. It’s very handy if you do work for multiple clients, or if you want to tinker with personal projects in addition to your main employer’s work. I have templates for new files which insert the company name and my email address, and with this mechanism, they’re always correct.
The basic idea is that you define classes, which are collections of variables (per major mode, or for all modes) which should be applied to different files. Then you bind these classes to directories; when you open a file in or under that directory, the variables are applied to it. For example:
(dir-locals-set-class-variables
'digg
'((nil . ((user-company . "Digg, Inc.")
(user-mail-address . "my-digg-address")))))
(dir-locals-set-directory-class (expand-file-name "~/Projects/Digg") 'digg))
And any file I open (or create) under ~/Projects/Digg has these variables defined.
Unfortunately, it doesn’t work if you’re editing a file via Tramp, and I do the vast majority of my editing this way. I submitted a patch which toggles this behavior; In the mean time, I’m using this ugly hack to make it work:
(defadvice hack-dir-local-variables
(around hack-remote-file-p first activate)
"Hack (hack-dir-local-variables) to make it work with remote files."
(require 'cl)
(flet ((file-remote-p (file &optional identification connected) nil))
ad-do-it))
The other problem I ran into is that you can only use one class per file. I really need to have multiple classes apply, like this:
(dir-locals-set-class-variables
'digg
'((nil . ((user-company . "Digg, Inc.")
(user-mail-address . "my-digg-address")))))
(dir-locals-set-class-variables
'project-a
'((php-mode . ((phpunit-program . (expand-file-name "~/Projects/Digg/project-a/bin/phpunit"))))))
(dir-locals-set-directory-class (expand-file-name "~/Projects/Digg") 'digg))
(dir-locals-set-directory-class (expand-file-name "~/Projects/Digg/project-a")
'project-a))
This only uses the most specific class, instead of all applicable classes. I worked around this by creating a compound class:
(dir-locals-set-class-variables
'digg-project-a (cons (car (dir-locals-get-class-variables 'digg))
(dir-locals-get-class-variables 'project-a)))
(dir-locals-set-directory-class (expand-file-name "~/Projects/Digg/project-a")
'digg-project-a))
This solution works for me, but it’s extremely naïve. It won’t work if you define classes with variables for the same modes. If I get the time, I might look into fixing this properly.
Discussion