Overriding project.el project root in Emacs
I've recently been experimenting with replacing
Projectile with the built-in project.el
, and
so far it has impressed me. Not only are all of Projectile's useful features available, but for
me
project.el
runs significantly faster in large repositories. If you're not familiar, both of these packages
provide functions to search and operate on files and directories in the same project. If you're
using Git, a "project" is probably synonymous with a repository.
Unfortunately, project detection is not always as easy as looking for a
.git/
nearby, and sometimes Emacs gets it wrong. Projectile solves this by also
looking for a .projectile
file, which overrides detection and says "this is the
project root". This happens to be one of the features missing in project.el
.
Update
Since the writing of this post, Emacs 29 has been released and introduces a variable which solves this same issue in a cleaner way! Unfortunately it doesn't appear to work for me. Still it may be worth a shot for you: set project-vc-extra-root-markers
to a list of file names or glob patterns which mark a project's root in addition to the default ".git", ".hg" and other common markers.
So, the cleaner equivalent to the rest of the post, if it works, is a simple
(setq project-vc-extra-root-markers '(".project.el" ".projectile" ))
If, however, you're like me and can't seem to get this to do anything, the original post still works:
Original post
Luckily, we can provide our own function to project.el
which looks for a file like
this in the current and parent directories. Even better, the excellent Emacs community has
already jumped on this, and a splendid solution was
provided by Michael Stapelberg.
Alas, Michael couldn't have forseen that Emacs would change the project root data format in
Emacs 29, so the provided function only works in earlier versions. However, adding in forward
compatibility isn't much trouble. And while we're at it, we can also provide support for anyone
else moving from Projectile like I am, by allowing .projectile
to serve as a
project root marker alongside Michael's .project.el
.
(defun project-root-override (dir)
"Find DIR's project root by searching for a '.project.el' file.
If this file exists, it marks the project root. For convenient compatibility
with Projectile, '.projectile' is also considered a project root marker.
https://blog.jmthornton.net/p/emacs-project-override"
(let ((root (or (locate-dominating-file dir ".project.el")
(locate-dominating-file dir ".projectile")))
(backend (ignore-errors (vc-responsible-backend dir))))
(when root (if (version<= emacs-version "28")
(cons 'vc root)
(list 'vc backend root)))))
;; Note that we cannot use :hook here because `project-find-functions' doesn't
;; end in "-hook", and we can't use this in :init because it won't be defined
;; yet.
(use-package project
:config
(add-hook 'project-find-functions #'project-root-override))
Now we can use touch .project.el
in any directory, and project.el
with
recognize it as the project root!
By the way, the snippet above makes use of use-package which provides fantastic package configuration and loading ability. John Wiegley is currently working on adding it into Emacs itself, so it shouldn't be long before this code snippet is fully native!
One note, in an ideal world, I'd prefer the root marker to be just .project
instead
of .project.el
, but this is already widely used by other tools like Eclipse and I'd
rather not cause conflicts. If you'd like to use this in your own Emacs, obviously you can
change the function to check for anything you want.