This gist is for anyone who's trying to use emacs + eglot + monorepo (with Clojure or any other language).
When you open a file in a buffer, eglot needs to determine the scope or folder to run the language server in.
By default, the folder eglot will pick as the assumed project root is the repo root (the ancestory directory containing .git
).
But in a monorepo, that's rarely what you want.
In a large repo, analyzing all the *.clj
files with clojure-lsp could take a minute or longer.
As a limiting case, imagine Google with its gargantuan monorepo. Analyzing all the source files would
take an indefinite period of time.
So we need to scope eglot (and clojure-lsp) to a subfolder of the monorepo (e.g. /projects/foo
).
Eglot is built on top of project.el, a sort
of built-in replacement for projectile,
and uses its (project-current)
function to determine the project root.
(You can run M-x eval-expression
(project-current)
to see which folder it picks.)
Sadly project.el doesn't have an easy, built-in way to define what your repo root is (a strange omission for a project management tool.)
However, you can customize the behavior with elisp:
(require 'cl-extra)
(setq project-sentinels '("bb.edn" "deps.edn" "package.json" ".monorepo-project"))
(defun find-enclosing-project (dir)
(locate-dominating-file
dir
(lambda (file)
(and (file-directory-p file)
(cl-some (lambda (sentinel)
(file-exists-p (expand-file-name sentinel file)))
project-sentinels)))))
(add-hook 'project-find-functions
#'(lambda (d)
(let ((dir (find-enclosing-project d)))
(if dir (cons 'vc dir) nil))))
What this does is to instruct project.el (and thus, eglot) to pick the folder with a "dominating file" (in Emacs lingo a sentinel
file found in the a file's parent directory, or its parent directory, or its parent directory etc) called deps.edn
(or package.json
etc) as the project root.
In Clojure, looking for deps.edn
as a sentinel is not a bad assumption. Make sure to adapt the project-sentinels
list
to your own project.
h/t https://michael.stapelberg.ch/posts/2021-04-02-emacs-project-override/
Thanks for posting this. This, and the linked post from Michael Stapelberg, were quite helpful though not exactly what was after - I wanted project.el to continue operating at the monorepo level (so I could find project files across all sub-projects) but have eglot use sub-directories when starting lsp servers.
It looks like recent versions of eglot have a variable,
eglot-lsp-context
, which is only set when it is looking for the project root. That means you can inspect the variable and only "change" the project root when eglot is starting an lsp server.What I ended up with: