Add chapter on advanced server configuration to Eglot manual
* doc/misc/eglot.texi (Top): Add section "Advanced server configuration" (Setting Up LSP Servers): Rework. (Advanced server configuration): New chapter.
This commit is contained in:
parent
2e7460c231
commit
50a3559c5a
1 changed files with 247 additions and 99 deletions
|
@ -98,6 +98,7 @@ This manual documents how to configure, use, and customize Eglot.
|
||||||
* Eglot and LSP Servers:: How to work with language servers.
|
* Eglot and LSP Servers:: How to work with language servers.
|
||||||
* Using Eglot:: Important Eglot commands and variables.
|
* Using Eglot:: Important Eglot commands and variables.
|
||||||
* Customizing Eglot:: Eglot customization and advanced features.
|
* Customizing Eglot:: Eglot customization and advanced features.
|
||||||
|
* Advanced server configuration:: Fine-tune a specific language server
|
||||||
* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
|
* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
|
||||||
* GNU Free Documentation License:: The license for this manual.
|
* GNU Free Documentation License:: The license for this manual.
|
||||||
* Index::
|
* Index::
|
||||||
|
@ -226,11 +227,10 @@ This says to invoke @var{program} with zero or more arguments
|
||||||
standard input and standard output streams.
|
standard input and standard output streams.
|
||||||
|
|
||||||
@item (@var{program} @var{args}@dots{} :initializationOptions @var{options}@dots{})
|
@item (@var{program} @var{args}@dots{} :initializationOptions @var{options}@dots{})
|
||||||
Like above, but with @var{options} specifying the options to be
|
@var{program} is invoked with @var{args} but @var{options} specifies
|
||||||
used for constructing the @samp{initializationOptions} JSON object for
|
how to construct the @samp{:initializationOptions} JSON object to pass
|
||||||
the server. @var{options} can also be a function of one argument, in
|
the server on during the LSP handshake (@pxref{Advanced server
|
||||||
which case it will be called with the server instance as the argument,
|
configuration}).
|
||||||
and should return the JSON object to use for initialization.
|
|
||||||
|
|
||||||
@item (@var{host} @var{port} @var{args}@dots{})
|
@item (@var{host} @var{port} @var{args}@dots{})
|
||||||
Here @var{host} is a string and @var{port} is a positive integer
|
Here @var{host} is a string and @var{port} is a positive integer
|
||||||
|
@ -970,83 +970,170 @@ mechanism.
|
||||||
Set this variable to true if you'd like progress notifications coming
|
Set this variable to true if you'd like progress notifications coming
|
||||||
from the LSP server to be handled as Emacs's progress reporting
|
from the LSP server to be handled as Emacs's progress reporting
|
||||||
facilities.
|
facilities.
|
||||||
|
|
||||||
@vindex eglot-workspace-configuration
|
|
||||||
@cindex server workspace configuration
|
|
||||||
@item eglot-workspace-configuration
|
|
||||||
This variable is meant to be set in the @file{.dir-locals.el} file, to
|
|
||||||
provide per-project settings, as described below in more detail.
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
Some language servers need to know project-specific settings, which
|
@node Advanced server configuration
|
||||||
the LSP calls @dfn{workspace configuration}. Eglot allows such fine
|
@chapter Advanced server configuration
|
||||||
tuning of per-project settings via the variable
|
|
||||||
@code{eglot-workspace-configuration}. Eglot sends the settings in
|
|
||||||
this variable to each server, and each server applies the portion of the
|
|
||||||
settings relevant to it and ignores the rest. These settings are
|
|
||||||
communicated to the server initially (upon establishing the
|
|
||||||
connection) or when the settings are changed, or in response to a
|
|
||||||
configuration request from the server.
|
|
||||||
|
|
||||||
In many cases, servers can be configured globally using a
|
Though many language servers work well out-of-the-box, most allow
|
||||||
configuration file in the user's home directory or in the project
|
fine-grained control of their operation via specific configuration
|
||||||
directory, which the language server reads. For example, the
|
options that vary from server to server. A small number of servers
|
||||||
@command{pylsp} server for Python reads the file
|
require such special configuration to work acceptably, or even to work
|
||||||
@file{~/.config/pycodestyle} and the @command{clangd} server reads the
|
at all.
|
||||||
file @file{.clangd} anywhere in the current project's directory tree.
|
|
||||||
If possible, we recommend using those configuration files that are
|
|
||||||
independent of Eglot and Emacs; they have the advantage that they will
|
|
||||||
work with other LSP clients as well.
|
|
||||||
|
|
||||||
If you do need to provide Emacs-specific configuration for a language
|
After having setup a server executable program in
|
||||||
server, we recommend defining the appropriate value in the
|
@code{eglot-server-programs} (@pxref{Setting Up LSP Servers}) and
|
||||||
@file{.dir-locals.el} file in the project's directory. The value of
|
ensuring Eglot can invoke it, you may want to take advantage of some
|
||||||
this variable should be a property list of the following format:
|
of these options. You should first distinguish two main kinds of
|
||||||
|
server configuration:
|
||||||
|
|
||||||
|
@itemize @bullet
|
||||||
|
@item
|
||||||
|
User-specific, applying to all projects the server is used for;
|
||||||
|
|
||||||
|
@item
|
||||||
|
Project-specific, applying to a specific project.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
When you have decided which kind you need, the following sections
|
||||||
|
teach how Eglot's user variables can be used to achieve it:
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* User-specific configuration::
|
||||||
|
* Project-specific configuration::
|
||||||
|
* JSONRPC objects in Elisp::
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
It's important to note that not all servers allow both kinds of
|
||||||
|
configuration, nor is it guaranteed that user options can be copied
|
||||||
|
over project options, and vice-versa. When in doubt, consult your
|
||||||
|
language server's documentation.
|
||||||
|
|
||||||
|
It's also worth noting that some language servers can read these
|
||||||
|
settings from configuration files in the user's @code{HOME} directory
|
||||||
|
or in a project's directory. For example, the @command{pylsp} Python
|
||||||
|
server reads the file @file{~/.config/pycodestyle} for user
|
||||||
|
configuration. The @command{clangd} C/C++ server reads both
|
||||||
|
@file{~/.config/clangd/config.yaml} for user configuration and
|
||||||
|
@file{.clangd} for project configuration. It may be advantageous to
|
||||||
|
use these mechanisms instead of Eglot's, as the latter have the
|
||||||
|
advantage of working with other LSP clients.
|
||||||
|
|
||||||
|
@node User-specific configuration
|
||||||
|
@section User-specific configuration
|
||||||
|
@cindex initializationOptions
|
||||||
|
@cindex command-line arguments
|
||||||
|
|
||||||
|
This kind of configuration applies to all projects the server is used
|
||||||
|
for. Here, there are again two main ways to do this inside Eglot.
|
||||||
|
|
||||||
|
A common way is to pass command-line options to the server invocation
|
||||||
|
via @code{eglot-server-programs}. Let's say we want to configure
|
||||||
|
where the @command{clangd} server reads its
|
||||||
|
@code{compile_commands.json} from. This can be done like so:
|
||||||
|
|
||||||
@lisp
|
@lisp
|
||||||
(:@var{server} @var{plist}@dots{})
|
(with-eval-after-load 'eglot
|
||||||
|
(add-to-list 'eglot-server-programs
|
||||||
|
`(c++-mode . ("clangd" "--compile-commands-dir=/tmp"))))
|
||||||
|
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
Here @code{:@var{server}} identifies a particular language server and
|
Another way is to have Eglot pass a JSON object to the server during
|
||||||
@var{plist} is the corresponding keyword-value property list of one or
|
the LSP handshake. This is done using the
|
||||||
more parameter settings for that server, serialized by Eglot as a JSON
|
@code{:initializationOptions} syntax of @code{eglot-server-programs}:
|
||||||
object. @var{plist} may be arbitrarily complex, generally containing
|
|
||||||
other keyword-value property sublists corresponding to JSON subobjects.
|
@lisp
|
||||||
The JSON values @code{true}, @code{false}, @code{null} and @code{@{@}}
|
(with-eval-after-load 'eglot
|
||||||
are represented by the Lisp values @code{t}, @code{:json-false},
|
(add-to-list 'eglot-server-programs
|
||||||
@code{nil}, and @code{eglot-@{@}}, respectively.
|
`(c++-mode . ("clangd" :initializationOptions
|
||||||
|
(:compilationDatabasePath "/tmp")))))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
The argument @code{(:compilationDatabasePath "/tmp")} is Emacs's
|
||||||
|
representation in plist format of a simple JSON object
|
||||||
|
@code{@{"compilationDatabasePath": "/tmp"@}}. To learn how to
|
||||||
|
represent more deeply nested options in this format, @xref{JSONRPC
|
||||||
|
objects in Elisp}.
|
||||||
|
|
||||||
|
In this case, the two examples achieve exactly the same, but notice
|
||||||
|
how the option's name has changed between them.
|
||||||
|
|
||||||
|
@node Project-specific configuration
|
||||||
|
@section Project-specific configuration
|
||||||
|
@vindex eglot-workspace-configuration
|
||||||
|
@cindex workspace configuration
|
||||||
|
|
||||||
|
To set project-specific settings, which the LSP specification calls
|
||||||
|
@dfn{workspace configuration}, the variable
|
||||||
|
@code{eglot-workspace-configuration} may be used.
|
||||||
|
|
||||||
|
This variable is a directory-local variable (@pxref{Directory
|
||||||
|
Variables, , Per-directory Local Variables, emacs, 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 globally or in a major-mode hook.
|
||||||
|
|
||||||
|
The most common way to set @code{eglot-workspace-configuration } is
|
||||||
|
using a @file{.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
|
||||||
|
@code{dir-locals-set-class-variables} function. (@pxref{Directory
|
||||||
|
Local Variables,,, elisp, GNU Emacs Lisp Reference Manual}).
|
||||||
|
|
||||||
|
However you choose to set it, the variable's value is a plist
|
||||||
|
(@pxref{Property Lists,,, elisp, GNU Emacs Lisp Reference Manual}) with
|
||||||
|
the following format:
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(@var{:server1} @var{plist1} @var{:server2} @var{plist2} @dots{})
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
Here, @var{:server1} and @var{:server2} are keywords whose names
|
||||||
|
identify the LSP language servers to target. Consult server
|
||||||
|
documentation to find out what name to use. @var{plist1} and
|
||||||
|
@var{plist2} are plists of options, possibly nesting other plists.
|
||||||
|
|
||||||
@findex eglot-show-workspace-configuration
|
@findex eglot-show-workspace-configuration
|
||||||
When experimenting with workspace settings, you can use the command
|
When experimenting with workspace settings, you can use the command
|
||||||
@kbd{M-x eglot-show-workspace-configuration} to inspect and debug the
|
@kbd{M-x eglot-show-workspace-configuration} to inspect and debug the
|
||||||
JSON value to be sent to the server. This helper command works even
|
value of this variable in its final JSON form, ready to be sent to the
|
||||||
before actually connecting to the server.
|
server (@pxref{JSONRPC objects in Elisp}). This helper command works
|
||||||
|
even before actually connecting to the server.
|
||||||
|
|
||||||
Here's an example of defining the workspace-configuration settings for
|
These variable's value doesn't take effect immediately. That happens
|
||||||
a project that uses two different language servers, one for Python,
|
upon establishing the connection, in response to an explicit query
|
||||||
the other one for Go (presumably, the project is written in a
|
from the server, or when issuing the command @kbd{M-x
|
||||||
combination of these two languages). The server for Python in this
|
eglot-signal-didChangeConfiguration} which notifies the server during
|
||||||
case is @command{pylsp}, the server for Go is @command{gopls}. The
|
an ongoing Eglot session.
|
||||||
value of @code{eglot-workspace-configuration} in this case should be:
|
|
||||||
|
|
||||||
@lisp
|
@subsection Examples
|
||||||
((python-mode
|
|
||||||
. ((eglot-workspace-configuration
|
|
||||||
. (:pylsp (:plugins (:jedi_completion (:include_params t
|
|
||||||
:fuzzy t)
|
|
||||||
:pylint (:enabled :json-false)))))))
|
|
||||||
(go-mode
|
|
||||||
. ((eglot-workspace-configuration
|
|
||||||
. (:gopls (:usePlaceholders t))))))
|
|
||||||
@end lisp
|
|
||||||
|
|
||||||
@noindent
|
For some users, setting @code{eglot-workspace-configuration} is a
|
||||||
This should go into the @file{.dir-locals.el} file in the project's
|
somewhat daunting task. One of the reasons is having to manage the
|
||||||
root directory. It sets up the value of
|
general Elisp syntax of per-mode directory-local variables, which uses
|
||||||
@code{eglot-workspace-configuration} separately for each major mode.
|
alists (@pxref{Association Lists,,, elisp, GNU Emacs Lisp Reference
|
||||||
|
Manual}), and the specific syntax of Eglot's variable, which uses
|
||||||
|
plists. Some examples are useful.
|
||||||
|
|
||||||
Alternatively, the same configuration could be defined as follows:
|
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 @command{pylsp} and @command{gopls} LSP 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:
|
||||||
|
|
||||||
|
@example
|
||||||
|
pylsp.plugins.jedi_completion.include_params: true
|
||||||
|
pylsp.plugins.jedi_completion.fuzzy: true
|
||||||
|
pylsp.pylint.enabled: false
|
||||||
|
gopls.usePlaceholders: true
|
||||||
|
@end example
|
||||||
|
|
||||||
|
To apply this to Eglot, and assuming you chose the
|
||||||
|
@file{.dir-locals.el} file method, the contents of that file could be:
|
||||||
|
|
||||||
@lisp
|
@lisp
|
||||||
((nil
|
((nil
|
||||||
|
@ -1054,55 +1141,116 @@ Alternatively, the same configuration could be defined as follows:
|
||||||
. (:pylsp (:plugins (:jedi_completion (:include_params t
|
. (:pylsp (:plugins (:jedi_completion (:include_params t
|
||||||
:fuzzy t)
|
:fuzzy t)
|
||||||
:pylint (:enabled :json-false)))
|
:pylint (:enabled :json-false)))
|
||||||
:gopls (:usePlaceholders t))))))
|
:gopls (:usePlaceholders t)))))
|
||||||
|
(python-mode . ((indent-tabs-mode . nil)))
|
||||||
|
(go-mode . ((indent-tabs-mode . t))))
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
This is an equivalent setup which sets the value for all the
|
@noindent
|
||||||
major-modes inside the project; each server will use only the section
|
This sets the value of @code{eglot-workspace-configuration} in all the
|
||||||
of the parameters intended for that server, and ignore the rest.
|
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
|
||||||
|
@code{eglot-workspace-configuration}.
|
||||||
|
|
||||||
As yet another alternative, you can set the value of
|
This following form may also be used:
|
||||||
@code{eglot-workspace-configuration} programmatically, via the
|
|
||||||
@code{dir-locals-set-class-variables} function, @pxref{Directory Local
|
@lisp
|
||||||
Variables,,, elisp, GNU Emacs Lisp Reference Manual}.
|
((python-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))))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
This sets up the value of @code{eglot-workspace-configuration}
|
||||||
|
separately depending on the major mode of each of that project's
|
||||||
|
buffers. @code{python-mode} buffers will have the variable set to
|
||||||
|
@code{(:pylsp (:plugins ...))}. @code{go-mode} buffers will have the
|
||||||
|
variable set to @code{(:gopls (:usePlaceholders t))}.
|
||||||
|
|
||||||
|
Some servers will issue workspace configuration for specific files
|
||||||
|
inside your project. For example, if you know @code{gopls} is asking
|
||||||
|
about specific files in the @code{src/imported} subdirectory and you
|
||||||
|
want to set a different option for @code{gopls.usePlaceholders} , you
|
||||||
|
may use something like:
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
((python-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))))))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
Finally, if one needs to determine the workspace configuration based
|
Finally, if one needs to determine the workspace configuration based
|
||||||
on some dynamic context, @code{eglot-workspace-configuration} can be
|
on some dynamic context, @code{eglot-workspace-configuration} can be
|
||||||
set to a function. The function is called with the
|
set to a function. The function is called with the
|
||||||
@code{eglot-lsp-server} instance of the connected server (if any) and
|
@code{eglot-lsp-server} instance of the connected server (if any) and
|
||||||
with @code{default-directory} set to the root of the project. The
|
with @code{default-directory} set to the root of the project. The
|
||||||
function should return a value of the form described above.
|
function should return a plist suitable for use as the variable's
|
||||||
|
value.
|
||||||
|
|
||||||
Some servers need special hand-holding to operate correctly. If your
|
@node JSONRPC objects in Elisp
|
||||||
server has some quirks or non-conformity, it's possible to extend
|
@section JSONRPC objects in Elisp
|
||||||
Eglot via Elisp to adapt to it, by defining a suitable
|
|
||||||
@code{eglot-initialization-options} method via @code{cl-defmethod}
|
|
||||||
(@pxref{Generic Functions,,, elisp, GNU Emacs Lisp Reference Manual}).
|
|
||||||
|
|
||||||
Here's an example:
|
Emacs's preferred way of representing JSON is via Lisp lists. In
|
||||||
|
Eglot, the syntax of this list is the simplest possible (the one with
|
||||||
|
fewer parenthesis), a plist (@pxref{Property Lists,,, elisp, GNU Emacs
|
||||||
|
Lisp Reference Manual}).
|
||||||
|
|
||||||
|
The plist may be arbitrarily complex, and generally containing other
|
||||||
|
keyword-value property sub-plists corresponding to JSON sub-objects.
|
||||||
|
|
||||||
|
For representing the JSON leaf values @code{true}, @code{false},
|
||||||
|
@code{null} and @code{@{@}}, you can use the Lisp values @code{t},
|
||||||
|
@code{:json-false}, @code{nil}, and @code{eglot-@{@}}, respectively.
|
||||||
|
|
||||||
|
For example, this plist:
|
||||||
|
|
||||||
@lisp
|
@lisp
|
||||||
(require 'eglot)
|
(:pylsp (:plugins (:jedi_completion (:include_params t
|
||||||
|
:fuzzy t)
|
||||||
(add-to-list 'eglot-server-programs
|
:pylint (:enabled :json-false)))
|
||||||
'((c++-mode c-mode) . (eglot-cquery "cquery")))
|
:gopls (:usePlaceholders t))
|
||||||
|
|
||||||
(defclass eglot-cquery (eglot-lsp-server) ()
|
|
||||||
:documentation "A custom class for cquery's C/C++ langserver.")
|
|
||||||
|
|
||||||
(cl-defmethod eglot-initialization-options ((server eglot-cquery))
|
|
||||||
"Passes through required cquery initialization options"
|
|
||||||
(let* ((root (car (project-roots (eglot--project server))))
|
|
||||||
(cache (expand-file-name ".cquery_cached_index/" root)))
|
|
||||||
(list :cacheDirectory (file-name-as-directory cache)
|
|
||||||
:progressReportFrequencyMs -1)))
|
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
@noindent
|
Is serialized by Eglot to the following JSON text:
|
||||||
See the doc string of @code{eglot-initialization-options} for more
|
|
||||||
details.
|
@example
|
||||||
@c FIXME: The doc string of eglot-initialization-options should be
|
@{
|
||||||
@c enhanced and extended.
|
"pylsp": @{
|
||||||
|
"plugins": @{
|
||||||
|
"jedi_completion": @{
|
||||||
|
"include_params": true,
|
||||||
|
"fuzzy": true
|
||||||
|
@},
|
||||||
|
"pylint": @{
|
||||||
|
"enabled": false
|
||||||
|
@}
|
||||||
|
@}
|
||||||
|
@},
|
||||||
|
"gopls": @{
|
||||||
|
"usePlaceholders":true
|
||||||
|
@},
|
||||||
|
@}
|
||||||
|
@end example
|
||||||
|
|
||||||
@node Troubleshooting Eglot
|
@node Troubleshooting Eglot
|
||||||
@chapter Troubleshooting Eglot
|
@chapter Troubleshooting Eglot
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue