To set project-specific settings, which the LSP specification calls
workspace configuration, the variable
eglot-workspace-configuration
may be used.
This variable is a directory-local variable (see Per-directory Local Variables in The GNU Emacs Manual). It’s important to recognize that this variable really only makes sense when set directory-locally. It usually does not make sense to set it file-locally or in a major-mode hook.
The most common way to set eglot-workspace-configuration
is
using a .dir-locals.el file in the root of your project. If
you can’t do that, you may also set it from Elisp code via the
dir-locals-set-class-variables
function. (see Directory
Local Variables in GNU Emacs Lisp Reference Manual).
However you choose to set it, the variable’s value is a plist (see Property Lists in GNU Emacs Lisp Reference Manual) with the following format:
(:server1 plist1 :server2 plist2 …)
Here, :server1 and :server2 are keywords whose names identify the LSP language servers to target. Consult server documentation to find out what name to use. plist1 and plist2 are plists of options, possibly nesting other plists.
When experimenting with workspace settings, you can use the command M-x eglot-show-workspace-configuration to inspect and debug the value of this variable in its final JSON form, ready to be sent to the server (see JSONRPC objects in Elisp). This helper command works even before actually connecting to the server.
These variable’s value doesn’t take effect immediately. That happens upon establishing the connection, in response to an explicit query from the server, or when issuing the command M-x eglot-signal-didChangeConfiguration which notifies the server during an ongoing Eglot session.
For some users, setting eglot-workspace-configuration
is a
somewhat daunting task. One of the reasons is having to manage the
general Elisp syntax of per-mode directory-local variables, which uses
alists (see Association Lists in GNU Emacs Lisp Reference
Manual), and the specific syntax of Eglot’s variable, which uses
plists. Some examples are useful.
Let’s say you want to configure two language servers to be used in a
project written in a combination of the Python and Go languages. You
want to use the pylsp
and gopls
languages
servers. In the documentation of the servers in question (or in some
other editor’s configuration file, or in some blog article), you find
the following configuration options in informal dotted-notation
syntax:
pylsp.plugins.jedi_completion.include_params: true pylsp.plugins.jedi_completion.fuzzy: true pylsp.pylint.enabled: false gopls.usePlaceholders: true
To apply this to Eglot, and assuming you chose the .dir-locals.el file method, the contents of that file could be:
((nil . ((eglot-workspace-configuration . (:pylsp (:plugins (:jedi_completion (:include_params t :fuzzy t) :pylint (:enabled :json-false))) :gopls (:usePlaceholders t))))) (python-base-mode . ((indent-tabs-mode . nil))) (go-mode . ((indent-tabs-mode . t))))
This sets the value of eglot-workspace-configuration
in all the
buffers inside the project; each server will use only the section of
the parameters intended for that server, and ignore the rest. Note
how alists are used for associating Emacs mode names with alists
associating variable names with variable values. Then notice how
plists are used inside the value of
eglot-workspace-configuration
.
This following form may also be used:
((python-base-mode . ((eglot-workspace-configuration . (:pylsp (:plugins (:jedi_completion (:include_params t :fuzzy t) :pylint (:enabled :json-false))))) (indent-tabs-mode . nil))) (go-mode . ((eglot-workspace-configuration . (:gopls (:usePlaceholders t))) (indent-tabs-mode . t))))
This sets up the value of eglot-workspace-configuration
separately depending on the major mode of each of that project’s
buffers. python-base-mode
buffers will have the variable set to
(:pylsp (:plugins ...))
. go-mode
buffers will have the
variable set to (:gopls (:usePlaceholders t))
.
Some servers will issue workspace configuration for specific files
inside your project. For example, if you know gopls
is asking
about specific files in the src/imported
subdirectory and you
want to set a different option for gopls.usePlaceholders
, you
may use something like:
((python-base-mode . ((eglot-workspace-configuration . (:pylsp (:plugins (:jedi_completion (:include_params t :fuzzy t) :pylint (:enabled :json-false))))) (indent-tabs-mode nil))) (go-mode . ((eglot-workspace-configuration . (:gopls (:usePlaceholders t))) (indent-tabs-mode t))) ("src/imported" . ((eglot-workspace-configuration . (:gopls (:usePlaceholders nil))))))
Finally, if one needs to determine the workspace configuration based
on some dynamic context, eglot-workspace-configuration
can be
set to a function. The function is called with the
eglot-lsp-server
instance of the connected server (if any) and
with default-directory
set to the root of the project. The
function should return a plist suitable for use as the variable’s
value.