Nicolas Martyanoff — Brain dump About

Replacing Projectile by Project

I have been using Projectile for years, and it served me well. While Projectile is really useful, it is a third party package made of more than 8000 lines of code. Since the built-in Project module has received lots of improvements since Emacs 28.1, I decided to give it a try and see if I could remove Projectile from my Emacs setup.

Key binding

The Project keymap is bound to C-x p by default, but I am used to C-c p. This is easy to change, for example with use-package:

(use-package project
  :bind-keymap
  (("C-c p" . project-prefix-map)))

Switching between projects

By default, the project switching command uses a prompt letting you select a command to run for the selected project. If you always use the same command, you can configure it, allowing to switch project without any prompt.

For example, to always display the project directory with Dired:

(setq project-switch-commands 'project-dired)

Search using Helm and AG

The Projectile feature I used the most is incremental project search using helm-projectile and helm-ag.

Adding this feature to Project is not that hard. Let us write the search function:

(defun g-project-search ()
  (interactive)
  (let ((project (project-current t)))
    (helm-do-ag (project-root project))))

And bind it to C-c p s (replacing project-shell, a command I do not use):

(use-package project
  :bind
  (:map project-prefix-map
        (("s" . 'g-project-search))))

One thing to be aware of: helm-ag does not take patterns returned by project-ignores into consideration when filtering results. It is not that much of a problem to me, since ag already supports .gitignore files

But it would be an improvement to use project-ignores and pass the pattern list to ag.

In the mean time, I can also remove the helm-projectile dependency.

Starting Eshell

While both Projectile and Project can run Eshell in the current project, I want the ability to start it the same way whether I am in a project or not. Therefore I have a small function looking for a current project and running Eshell the right way. Migrating to Project is easy:

(defun g-eshell ()
  "Start eshell at the root of the current project, or in the
current directory if the current buffer is not part of a
project."
  (interactive)
  (if (project-current)
      (project-eshell)
    (eshell)))

I bind it to C-x s (“s” being for “shell”):

(global-set-key (kbd "C-x s") 'g-eshell)

Conclusion

The result is quite satisfying. It may not do everything Projectile supports, but the features I use are all implemented. As a result, my Emacs setup is simpler and lighter, relying on one less external dependency.

Share the word!

Liked my article? Follow me on Twitter or on Mastodon to see what I'm up to.