summaryrefslogtreecommitdiff
path: root/doc/misc
diff options
context:
space:
mode:
authorJoão Távora <joaotavora@gmail.com>2023-03-09 01:20:11 +0000
committerJoão Távora <joaotavora@gmail.com>2023-03-09 11:14:44 +0000
commit50a3559c5a7106b99d254e1a0acb047b1fb858a2 (patch)
tree103e783faca5fa300013a4e92af77b33ce7282b6 /doc/misc
parent2e7460c231532cd9f79e4c394d8004cbf75c2605 (diff)
downloademacs-50a3559c5a7106b99d254e1a0acb047b1fb858a2.tar.gz
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.
Diffstat (limited to 'doc/misc')
-rw-r--r--doc/misc/eglot.texi336
1 files changed, 242 insertions, 94 deletions
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index eed9744b9f0..474800fb270 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -98,6 +98,7 @@ This manual documents how to configure, use, and customize Eglot.
* Eglot and LSP Servers:: How to work with language servers.
* Using Eglot:: Important Eglot commands and variables.
* Customizing Eglot:: Eglot customization and advanced features.
+* Advanced server configuration:: Fine-tune a specific language server
* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
* GNU Free Documentation License:: The license for this manual.
* Index::
@@ -226,11 +227,10 @@ This says to invoke @var{program} with zero or more arguments
standard input and standard output streams.
@item (@var{program} @var{args}@dots{} :initializationOptions @var{options}@dots{})
-Like above, but with @var{options} specifying the options to be
-used for constructing the @samp{initializationOptions} JSON object for
-the server. @var{options} can also be a function of one argument, in
-which case it will be called with the server instance as the argument,
-and should return the JSON object to use for initialization.
+@var{program} is invoked with @var{args} but @var{options} specifies
+how to construct the @samp{:initializationOptions} JSON object to pass
+the server on during the LSP handshake (@pxref{Advanced server
+configuration}).
@item (@var{host} @var{port} @var{args}@dots{})
Here @var{host} is a string and @var{port} is a positive integer
@@ -970,139 +970,287 @@ mechanism.
Set this variable to true if you'd like progress notifications coming
from the LSP server to be handled as Emacs's progress reporting
facilities.
+@end table
+
+@node Advanced server configuration
+@chapter Advanced server configuration
+
+Though many language servers work well out-of-the-box, most allow
+fine-grained control of their operation via specific configuration
+options that vary from server to server. A small number of servers
+require such special configuration to work acceptably, or even to work
+at all.
+
+After having setup a server executable program in
+@code{eglot-server-programs} (@pxref{Setting Up LSP Servers}) and
+ensuring Eglot can invoke it, you may want to take advantage of some
+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
+(with-eval-after-load 'eglot
+ (add-to-list 'eglot-server-programs
+ `(c++-mode . ("clangd" "--compile-commands-dir=/tmp"))))
+
+@end lisp
+
+@noindent
+Another way is to have Eglot pass a JSON object to the server during
+the LSP handshake. This is done using the
+@code{:initializationOptions} syntax of @code{eglot-server-programs}:
+
+@lisp
+(with-eval-after-load 'eglot
+ (add-to-list 'eglot-server-programs
+ `(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 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
+@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.
-Some language servers need to know project-specific settings, which
-the LSP calls @dfn{workspace configuration}. Eglot allows such fine
-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
-configuration file in the user's home directory or in the project
-directory, which the language server reads. For example, the
-@command{pylsp} server for Python reads the file
-@file{~/.config/pycodestyle} and the @command{clangd} server reads the
-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
-server, we recommend defining the appropriate value in the
-@file{.dir-locals.el} file in the project's directory. The value of
-this variable should be a property list of the following format:
+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{server} @var{plist}@dots{})
+ (@var{:server1} @var{plist1} @var{:server2} @var{plist2} @dots{})
@end lisp
@noindent
-Here @code{:@var{server}} identifies a particular language server and
-@var{plist} is the corresponding keyword-value property list of one or
-more parameter settings for that server, serialized by Eglot as a JSON
-object. @var{plist} may be arbitrarily complex, generally containing
-other keyword-value property sublists corresponding to JSON subobjects.
-The JSON values @code{true}, @code{false}, @code{null} and @code{@{@}}
-are represented by the Lisp values @code{t}, @code{:json-false},
-@code{nil}, and @code{eglot-@{@}}, respectively.
+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
When experimenting with workspace settings, you can use the command
@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
-before actually connecting to the server.
+value of this variable in its final JSON form, ready to be sent to the
+server (@pxref{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 @kbd{M-x
+eglot-signal-didChangeConfiguration} which notifies the server during
+an ongoing Eglot session.
+
+@subsection Examples
+
+For some users, setting @code{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 (@pxref{Association Lists,,, elisp, 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 @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:
-Here's an example of defining the workspace-configuration settings for
-a project that uses two different language servers, one for Python,
-the other one for Go (presumably, the project is written in a
-combination of these two languages). The server for Python in this
-case is @command{pylsp}, the server for Go is @command{gopls}. The
-value of @code{eglot-workspace-configuration} in this case should be:
+@lisp
+((nil
+ . ((eglot-workspace-configuration
+ . (:pylsp (:plugins (:jedi_completion (:include_params t
+ :fuzzy t)
+ :pylint (:enabled :json-false)))
+ :gopls (:usePlaceholders t)))))
+ (python-mode . ((indent-tabs-mode . nil)))
+ (go-mode . ((indent-tabs-mode . t))))
+@end lisp
+
+@noindent
+This sets the value of @code{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
+@code{eglot-workspace-configuration}.
+
+This following form may also be used:
@lisp
((python-mode
. ((eglot-workspace-configuration
. (:pylsp (:plugins (:jedi_completion (:include_params t
:fuzzy t)
- :pylint (:enabled :json-false)))))))
+ :pylint (:enabled :json-false)))))
+ (indent-tabs-mode . nil)))
(go-mode
. ((eglot-workspace-configuration
- . (:gopls (:usePlaceholders t))))))
+ . (:gopls (:usePlaceholders t)))
+ (indent-tabs-mode . t))))
@end lisp
@noindent
-This should go into the @file{.dir-locals.el} file in the project's
-root directory. It sets up the value of
-@code{eglot-workspace-configuration} separately for each major mode.
-
-Alternatively, the same configuration could be defined as follows:
+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
-((nil
+((python-mode
. ((eglot-workspace-configuration
. (:pylsp (:plugins (:jedi_completion (:include_params t
:fuzzy t)
- :pylint (:enabled :json-false)))
- :gopls (:usePlaceholders 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
-This is an equivalent setup which sets the value for all the
-major-modes inside the project; each server will use only the section
-of the parameters intended for that server, and ignore the rest.
-
-As yet another alternative, you can set the value of
-@code{eglot-workspace-configuration} programmatically, via the
-@code{dir-locals-set-class-variables} function, @pxref{Directory Local
-Variables,,, elisp, GNU Emacs Lisp Reference Manual}.
-
Finally, if one needs to determine the workspace configuration based
on some dynamic context, @code{eglot-workspace-configuration} can be
set to a function. The function is called with the
@code{eglot-lsp-server} instance of the connected server (if any) and
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
-server has some quirks or non-conformity, it's possible to extend
-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}).
+@node JSONRPC objects in Elisp
+@section JSONRPC objects in Elisp
-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}).
-@lisp
-(require 'eglot)
+The plist may be arbitrarily complex, and generally containing other
+keyword-value property sub-plists corresponding to JSON sub-objects.
-(add-to-list 'eglot-server-programs
- '((c++-mode c-mode) . (eglot-cquery "cquery")))
+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.
-(defclass eglot-cquery (eglot-lsp-server) ()
- :documentation "A custom class for cquery's C/C++ langserver.")
+For example, this plist:
-(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)))
+@lisp
+(:pylsp (:plugins (:jedi_completion (:include_params t
+ :fuzzy t)
+ :pylint (:enabled :json-false)))
+ :gopls (:usePlaceholders t))
@end lisp
-@noindent
-See the doc string of @code{eglot-initialization-options} for more
-details.
-@c FIXME: The doc string of eglot-initialization-options should be
-@c enhanced and extended.
+Is serialized by Eglot to the following JSON text:
+
+@example
+@{
+ "pylsp": @{
+ "plugins": @{
+ "jedi_completion": @{
+ "include_params": true,
+ "fuzzy": true
+ @},
+ "pylint": @{
+ "enabled": false
+ @}
+ @}
+ @},
+ "gopls": @{
+ "usePlaceholders":true
+ @},
+@}
+@end example
@node Troubleshooting Eglot
@chapter Troubleshooting Eglot