mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 17:24:23 +00:00
Eglot: document LSP server multiplexer support
This documents how to use LSP multiplexer programs like Rassumfrassum to connect multiple language servers to a single buffer. * doc/misc/eglot.texi (Top): Add "Multi-server support" menu entry. (Multi-server support): New chapter. (Using Rassumfrassum, Design rationale): New sections documenting how to use the Rassumfrassum multiplexer program with Eglot, with practical examples for C++, Python, and multi-language files. (Performance): Mention Rassumfrassum as solution for JSONRPC traffic performance issues. (Reporting bugs): Add guidance for troubleshooting multiplexer-related bugs. Improve project description guidance. Fix various typos. * lisp/progmodes/eglot.el (eglot-server-programs): Add a couple of rass entries. * etc/EGLOT-NEWS: Announce support for LSP server multiplexers via Rassumfrassum.
This commit is contained in:
parent
cc5ebad841
commit
6921244718
3 changed files with 190 additions and 14 deletions
|
|
@ -99,6 +99,7 @@ read this manual from within Emacs, type @kbd{M-x eglot-manual
|
|||
* Using Eglot:: Important Eglot commands and variables.
|
||||
* Customizing Eglot:: Eglot customization and advanced features.
|
||||
* Advanced server configuration:: Fine-tune a specific language server
|
||||
* Multi-server support:: Use more than one server in a buffer
|
||||
* Extending Eglot:: Writing Eglot extensions in Elisp
|
||||
* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
|
||||
* GNU Free Documentation License:: The license for this manual.
|
||||
|
|
@ -1511,6 +1512,152 @@ is serialized by Eglot to the following JSON text:
|
|||
@}
|
||||
@end example
|
||||
|
||||
@node Multi-server support
|
||||
@chapter Multi-server support
|
||||
@cindex multiple servers per buffer
|
||||
@cindex LSP server multiplexer
|
||||
@cindex per-buffer multiple servers
|
||||
|
||||
One of the most frequently requested features for Eglot in close to a
|
||||
decade of existence is the ability to use more than one LSP server in a
|
||||
single buffer. This is distinct from using multiple servers in a
|
||||
project, where each server manages a disjoint set of files written in
|
||||
different languages.
|
||||
|
||||
The latter case---multiple servers for different files---is
|
||||
intrinsically supported by Eglot. For example, in a web project with
|
||||
JavaScript, CSS, and Python files, Eglot can seamlessly manage separate
|
||||
language servers for each file type within the same project
|
||||
(@pxref{Starting Eglot}). Each buffer communicates with its appropriate
|
||||
server, and this works out-of-the-box.
|
||||
|
||||
However, there are several scenarios where multiple servers per buffer
|
||||
are useful:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Combining a spell-checking language server like @command{codebook-lsp}
|
||||
with language-specific servers for C++, Go, or Python files. The
|
||||
spell-checker provides diagnostics for comments and strings, while the
|
||||
language server handles syntax and semantics.
|
||||
|
||||
@item
|
||||
One might want multiple servers to cover different aspects of the same
|
||||
language. For Python, you might combine @command{ty} for type checking
|
||||
with @command{ruff} for linting and formatting. For JavaScript, you
|
||||
might use @command{typescript-language-server} for language features
|
||||
together with @command{eslint} for linting.
|
||||
|
||||
@item
|
||||
When working on multi-language files like Vue @file{.vue} files, which
|
||||
contain JavaScript, CSS, and HTML embedded in a single file, multiple
|
||||
servers can manage the different areas of the buffer.
|
||||
@end itemize
|
||||
|
||||
These use cases are not directly supported by Eglot's architecture,
|
||||
however, you can use a language-agnostic @dfn{LSP server multiplexer}
|
||||
that sits between Eglot and the actual language servers. Eglot still
|
||||
communicates with a single LSP server process in each buffer, but that
|
||||
process mediates communication to multiple language-specific servers,
|
||||
meaning that for practical purposes, it's @emph{as if} Eglot was
|
||||
connected to them directly.
|
||||
|
||||
This approach is more powerful and user-friendly than current
|
||||
workarounds that combine one LSP server in a buffer with additional
|
||||
non-LSP mechanisms such as extra Flymake backends (@pxref{Top,,,
|
||||
Flymake, GNU Flymake manual}) for the same buffer.
|
||||
|
||||
@menu
|
||||
* Using Rassumfrassum:: Setup the @code{rass} LSP multiplexer
|
||||
* Design rationale:: Benefits and drawbacks of LSP multiplexers
|
||||
@end menu
|
||||
|
||||
@node Using Rassumfrassum
|
||||
@section Using Rassumfrassum
|
||||
|
||||
@uref{https://github.com/joaotavora/rassumfrassum, Rassumfrassum} is an
|
||||
LSP server multiplexer program that fits the bill. Like most language
|
||||
servers, it must be installed separately since it is not bundled with
|
||||
Emacs (at time of writing). The installation is similar to installing
|
||||
any other language server, and usually amounts to making sure the
|
||||
program executable is somewhere in @code{PATH} or @code{exec-path}.
|
||||
|
||||
The Rassumfrassum program, invoked via the @command{rass} command, works
|
||||
by spawning multiple LSP server subprocesses and aggregating their
|
||||
capabilities, requests, and responses into a single unified LSP
|
||||
interface. From Eglot's perspective, it appears to be communicating with
|
||||
a single server.
|
||||
|
||||
To use Rassumfrassum with Eglot, you can start it interactively with a
|
||||
prefix argument to @code{eglot} and specify the @command{rass} command
|
||||
followed by the actual servers you want to use, separated by @code{--}:
|
||||
|
||||
@example
|
||||
C-u M-x eglot RET rass -- clangd -- codebook-lsp serve RET
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
This starts @command{clangd} for C++ language support and
|
||||
@command{codebook-lsp} for spell-checking in the same buffer.
|
||||
|
||||
For Python, you might use:
|
||||
|
||||
@example
|
||||
C-u M-x eglot RET rass -- ty server -- ruff server RET
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
or simply @kbd{C-u M-x eglot RET rass python}, using the ``preset''
|
||||
feature. This combines @command{ty} for type checking with
|
||||
@command{ruff} for linting and formatting.
|
||||
|
||||
These configurations can be integrated into the
|
||||
@code{eglot-server-programs} variable (@pxref{Setting Up LSP Servers})
|
||||
for automatic use:
|
||||
|
||||
@lisp
|
||||
(with-eval-after-load 'eglot
|
||||
(add-to-list 'eglot-server-programs
|
||||
'(c-ts-base-mode . ("rass" "--" "clangd" "--"
|
||||
"codebook-lsp" "serve")))
|
||||
(add-to-list 'eglot-server-programs
|
||||
'(python-mode . ("rass" "--" "ty" "server" "--"
|
||||
"ruff" "server"))))
|
||||
@end lisp
|
||||
|
||||
@node Design rationale
|
||||
@section Design rationale
|
||||
|
||||
Using an LSP server multiplexer like @command{rass} relieves Eglot from
|
||||
knowing about the specific characteristics of individual servers and the
|
||||
complexity of managing multiple simultaneous server connections per
|
||||
buffer. This helps preserve the essential features that distinguish
|
||||
Eglot's code base from other LSP offers for Emacs: simple, performant
|
||||
and mindful of the core tenet of LSP, which is for a client to be
|
||||
language-agnostic.
|
||||
|
||||
This approach has an additional benefit: because the multiplexer
|
||||
mediates all communication between Eglot and the servers, it can take
|
||||
advantage of different optimization opportunities. For instance, at the
|
||||
system level it may be multi-threaded to process different JSONRPC
|
||||
streams in with true parallelism, something which is currently
|
||||
impossible to do in plain Elisp. At the LSP-level it can merge server
|
||||
responses intelligently, truncate unnecessarily large objects, and cache
|
||||
significant amounts of information in efficient ways. In many cases,
|
||||
this can reduce the amount of JSONRPC traffic exchanged with Emacs to
|
||||
levels well below what would occur if a client connected to multiple
|
||||
servers separately. Some of these optimizations may apply even when a
|
||||
program like @command{rass} is mediating communication to a single
|
||||
server.
|
||||
|
||||
The multiplexer approach is not without drawbacks. Since LSP is a
|
||||
relatively large protocol with a decade of existence and many backward
|
||||
compatibility concerns, combining the responses of servers using completely
|
||||
different mechanisms of the protocol to respond to the same request
|
||||
sometimes leads to complexity in covering the corner cases. However,
|
||||
offloading this complexity to a completely separate layer has proven
|
||||
very effective in practice.
|
||||
|
||||
@node Extending Eglot
|
||||
@chapter Extending Eglot
|
||||
|
||||
|
|
@ -1687,9 +1834,13 @@ slowly, try to customize the variable @code{eglot-events-buffer-config}
|
|||
0. This will disable recording any events and may speed things up.
|
||||
|
||||
In other situations, the cause of poor performance lies in the language
|
||||
server itself. Servers use aggressive caching and other techniques to
|
||||
improve their performance. Often, this can be tweaked by changing the
|
||||
server configuration (@pxref{Advanced server configuration}).
|
||||
server itself. Some servers use aggressive caching and other techniques
|
||||
to improve their performance. Often, this can be tweaked by changing
|
||||
the server configuration (@pxref{Advanced server configuration}).
|
||||
|
||||
Another aspect that may cause performance degradation is the amount of
|
||||
JSONRPC information exchanged with Emacs. Using an LSP program like
|
||||
@ref{Using Rassumfrassum,Rassumfrassum} may alleviate such problems.
|
||||
|
||||
@node Getting the latest version
|
||||
@section Getting the latest version
|
||||
|
|
@ -1751,10 +1902,17 @@ may be using. If possible, try to replicate the problem with the
|
|||
C/C@t{++} or Python servers, as these are very easy to install.
|
||||
|
||||
@item
|
||||
Describe how to setup a @emph{minimal} project directory where Eglot
|
||||
If using an LSP multiplexer server like @ref{Using Rassumfrassum,
|
||||
Rassumfrassum}, first verify if the program replicates by using one of
|
||||
the multiplexed servers directly. If it doesn't the problem lies in the
|
||||
LSP multiplexer program and should be reported there.
|
||||
|
||||
@item
|
||||
Include a description of a @emph{minimal} project directory where Eglot
|
||||
should be started for the problem to happen. Describe each file's name
|
||||
and its contents. Alternatively, you can supply the address of a public
|
||||
Git repository.
|
||||
and its contents, or---sometimes better--- zip that project directory
|
||||
completely and attach it. Alternatively, you can supply the address of
|
||||
a public Git repository.
|
||||
|
||||
@item
|
||||
Include versions of the software used. The Emacs version can be
|
||||
|
|
@ -1767,12 +1925,13 @@ first check if the problem isn't already fixed in the latest version
|
|||
It's also essential to include the version of ELPA packages that are
|
||||
explicitly or implicitly loaded. The optional but popular Company or
|
||||
Markdown packages are distributed as GNU ELPA packages, not to mention
|
||||
Eglot itself in some situations. Some major modes (Go, Rust, etc.) are
|
||||
provided by ELPA packages. It's sometimes easy to miss these, since
|
||||
they are usually implicitly loaded when visiting a file in that
|
||||
language.
|
||||
Eglot itself in some situations. Prefer reproducing the problem with
|
||||
built-in Treesit major modes like @code{go-ts-mode} or
|
||||
@code{rust-ts-mode} since the non-ts modes for such languages are
|
||||
usually provided by ELPA packages, and it's often easy to miss them.
|
||||
|
||||
ELPA packages usually live in @code{~/.emacs.d/elpa} (or what is in
|
||||
If you can't reproduce your bug without ELPA packages, you may find the
|
||||
ones you're using in @code{~/.emacs.d/elpa} (or what is in
|
||||
@code{package-user-dir}). Including a listing of files in that
|
||||
directory is a way to tell the maintainers about ELPA package versions.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,20 @@ https://github.com/joaotavora/eglot/issues/1234.
|
|||
|
||||
* Changes to upcoming Eglot
|
||||
|
||||
** Support for LSP server multiplexers via Rassumfrassum
|
||||
|
||||
Eglot can now leverage LSP server multiplexer programs like Rassumfrassum
|
||||
(invoked via the 'rass' command) to use multiple language servers in a
|
||||
single buffer. This enables combining spell-checkers with language
|
||||
servers, using multiple servers for the same language (e.g., 'ty' for
|
||||
type checking and 'ruff' for linting in Python), or handling
|
||||
multi-language files like Vue.
|
||||
|
||||
Some invocations of 'rass' are offered as alternatives in the built-in
|
||||
'eglot-server-programs' variable. The manual (readable with 'M-x
|
||||
eglot-manual') contains a comprehensive discussion of how to set up and
|
||||
use multiplexers in the new "Multi-server support" chapter.
|
||||
|
||||
** Support for pull diagnostics (github#1559, github#1290)
|
||||
|
||||
For servers supporting the 'diagnosticProvider' capability, Eglot
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ automatically)."
|
|||
(defvar eglot-server-programs
|
||||
;; FIXME: Maybe this info should be distributed into the major modes
|
||||
;; themselves where they could set a buffer-local `eglot-server-program'
|
||||
;; instead of keeping this database centralized.
|
||||
;; which would allow deprecating this database.
|
||||
;; FIXME: With `derived-mode-add-parents' in Emacs≥30, some of
|
||||
;; those entries can be simplified, but we keep them for when
|
||||
;; `eglot.el' is installed via GNU ELPA in an older Emacs.
|
||||
|
|
@ -248,7 +248,8 @@ automatically)."
|
|||
(vimrc-mode . ("vim-language-server" "--stdio"))
|
||||
((python-mode python-ts-mode)
|
||||
. ,(eglot-alternatives
|
||||
'("pylsp" "pyls" ("basedpyright-langserver" "--stdio")
|
||||
'(("rass" "python")
|
||||
"pylsp" "pyls" ("basedpyright-langserver" "--stdio")
|
||||
("pyright-langserver" "--stdio")
|
||||
("pyrefly" "lsp")
|
||||
("ty" "server")
|
||||
|
|
@ -262,7 +263,9 @@ automatically)."
|
|||
(tsx-ts-mode :language-id "typescriptreact")
|
||||
(typescript-ts-mode :language-id "typescript")
|
||||
(typescript-mode :language-id "typescript"))
|
||||
. ("typescript-language-server" "--stdio"))
|
||||
. ,(eglot-alternatives
|
||||
'(("rass ts")
|
||||
("typescript-language-server" "--stdio"))))
|
||||
((bash-ts-mode sh-mode) . ("bash-language-server" "start"))
|
||||
((php-mode phps-mode php-ts-mode)
|
||||
. ,(eglot-alternatives
|
||||
|
|
|
|||
Loading…
Reference in a new issue