5.1 Project-specific configuration

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.

5.1.1 Examples

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.