aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2026-01-09 11:09:21 +0000
committerJoão Távora2026-01-11 03:42:01 +0000
commit6921244718522b27461b06cca7b29e187861f46f (patch)
treea61fdc9a38f0be0c5882814c99ff95f2b4530065
parentcc5ebad8410a0523509ac0e8d23eb96ab43bef0a (diff)
downloademacs-6921244718522b27461b06cca7b29e187861f46f.tar.gz
emacs-6921244718522b27461b06cca7b29e187861f46f.zip
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.
-rw-r--r--doc/misc/eglot.texi181
-rw-r--r--etc/EGLOT-NEWS14
-rw-r--r--lisp/progmodes/eglot.el9
3 files changed, 190 insertions, 14 deletions
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index 532416f17ad..579c568f264 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -99,6 +99,7 @@ read this manual from within Emacs, type @kbd{M-x eglot-manual
99* Using Eglot:: Important Eglot commands and variables. 99* Using Eglot:: Important Eglot commands and variables.
100* Customizing Eglot:: Eglot customization and advanced features. 100* Customizing Eglot:: Eglot customization and advanced features.
101* Advanced server configuration:: Fine-tune a specific language server 101* Advanced server configuration:: Fine-tune a specific language server
102* Multi-server support:: Use more than one server in a buffer
102* Extending Eglot:: Writing Eglot extensions in Elisp 103* Extending Eglot:: Writing Eglot extensions in Elisp
103* Troubleshooting Eglot:: Troubleshooting and reporting bugs. 104* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
104* GNU Free Documentation License:: The license for this manual. 105* GNU Free Documentation License:: The license for this manual.
@@ -1511,6 +1512,152 @@ is serialized by Eglot to the following JSON text:
1511@} 1512@}
1512@end example 1513@end example
1513 1514
1515@node Multi-server support
1516@chapter Multi-server support
1517@cindex multiple servers per buffer
1518@cindex LSP server multiplexer
1519@cindex per-buffer multiple servers
1520
1521One of the most frequently requested features for Eglot in close to a
1522decade of existence is the ability to use more than one LSP server in a
1523single buffer. This is distinct from using multiple servers in a
1524project, where each server manages a disjoint set of files written in
1525different languages.
1526
1527The latter case---multiple servers for different files---is
1528intrinsically supported by Eglot. For example, in a web project with
1529JavaScript, CSS, and Python files, Eglot can seamlessly manage separate
1530language servers for each file type within the same project
1531(@pxref{Starting Eglot}). Each buffer communicates with its appropriate
1532server, and this works out-of-the-box.
1533
1534However, there are several scenarios where multiple servers per buffer
1535are useful:
1536
1537@itemize @bullet
1538@item
1539Combining a spell-checking language server like @command{codebook-lsp}
1540with language-specific servers for C++, Go, or Python files. The
1541spell-checker provides diagnostics for comments and strings, while the
1542language server handles syntax and semantics.
1543
1544@item
1545One might want multiple servers to cover different aspects of the same
1546language. For Python, you might combine @command{ty} for type checking
1547with @command{ruff} for linting and formatting. For JavaScript, you
1548might use @command{typescript-language-server} for language features
1549together with @command{eslint} for linting.
1550
1551@item
1552When working on multi-language files like Vue @file{.vue} files, which
1553contain JavaScript, CSS, and HTML embedded in a single file, multiple
1554servers can manage the different areas of the buffer.
1555@end itemize
1556
1557These use cases are not directly supported by Eglot's architecture,
1558however, you can use a language-agnostic @dfn{LSP server multiplexer}
1559that sits between Eglot and the actual language servers. Eglot still
1560communicates with a single LSP server process in each buffer, but that
1561process mediates communication to multiple language-specific servers,
1562meaning that for practical purposes, it's @emph{as if} Eglot was
1563connected to them directly.
1564
1565This approach is more powerful and user-friendly than current
1566workarounds that combine one LSP server in a buffer with additional
1567non-LSP mechanisms such as extra Flymake backends (@pxref{Top,,,
1568Flymake, GNU Flymake manual}) for the same buffer.
1569
1570@menu
1571* Using Rassumfrassum:: Setup the @code{rass} LSP multiplexer
1572* Design rationale:: Benefits and drawbacks of LSP multiplexers
1573@end menu
1574
1575@node Using Rassumfrassum
1576@section Using Rassumfrassum
1577
1578@uref{https://github.com/joaotavora/rassumfrassum, Rassumfrassum} is an
1579LSP server multiplexer program that fits the bill. Like most language
1580servers, it must be installed separately since it is not bundled with
1581Emacs (at time of writing). The installation is similar to installing
1582any other language server, and usually amounts to making sure the
1583program executable is somewhere in @code{PATH} or @code{exec-path}.
1584
1585The Rassumfrassum program, invoked via the @command{rass} command, works
1586by spawning multiple LSP server subprocesses and aggregating their
1587capabilities, requests, and responses into a single unified LSP
1588interface. From Eglot's perspective, it appears to be communicating with
1589a single server.
1590
1591To use Rassumfrassum with Eglot, you can start it interactively with a
1592prefix argument to @code{eglot} and specify the @command{rass} command
1593followed by the actual servers you want to use, separated by @code{--}:
1594
1595@example
1596C-u M-x eglot RET rass -- clangd -- codebook-lsp serve RET
1597@end example
1598
1599@noindent
1600This starts @command{clangd} for C++ language support and
1601@command{codebook-lsp} for spell-checking in the same buffer.
1602
1603For Python, you might use:
1604
1605@example
1606C-u M-x eglot RET rass -- ty server -- ruff server RET
1607@end example
1608
1609@noindent
1610or simply @kbd{C-u M-x eglot RET rass python}, using the ``preset''
1611feature. This combines @command{ty} for type checking with
1612@command{ruff} for linting and formatting.
1613
1614These configurations can be integrated into the
1615@code{eglot-server-programs} variable (@pxref{Setting Up LSP Servers})
1616for automatic use:
1617
1618@lisp
1619(with-eval-after-load 'eglot
1620 (add-to-list 'eglot-server-programs
1621 '(c-ts-base-mode . ("rass" "--" "clangd" "--"
1622 "codebook-lsp" "serve")))
1623 (add-to-list 'eglot-server-programs
1624 '(python-mode . ("rass" "--" "ty" "server" "--"
1625 "ruff" "server"))))
1626@end lisp
1627
1628@node Design rationale
1629@section Design rationale
1630
1631Using an LSP server multiplexer like @command{rass} relieves Eglot from
1632knowing about the specific characteristics of individual servers and the
1633complexity of managing multiple simultaneous server connections per
1634buffer. This helps preserve the essential features that distinguish
1635Eglot's code base from other LSP offers for Emacs: simple, performant
1636and mindful of the core tenet of LSP, which is for a client to be
1637language-agnostic.
1638
1639This approach has an additional benefit: because the multiplexer
1640mediates all communication between Eglot and the servers, it can take
1641advantage of different optimization opportunities. For instance, at the
1642system level it may be multi-threaded to process different JSONRPC
1643streams in with true parallelism, something which is currently
1644impossible to do in plain Elisp. At the LSP-level it can merge server
1645responses intelligently, truncate unnecessarily large objects, and cache
1646significant amounts of information in efficient ways. In many cases,
1647this can reduce the amount of JSONRPC traffic exchanged with Emacs to
1648levels well below what would occur if a client connected to multiple
1649servers separately. Some of these optimizations may apply even when a
1650program like @command{rass} is mediating communication to a single
1651server.
1652
1653The multiplexer approach is not without drawbacks. Since LSP is a
1654relatively large protocol with a decade of existence and many backward
1655compatibility concerns, combining the responses of servers using completely
1656different mechanisms of the protocol to respond to the same request
1657sometimes leads to complexity in covering the corner cases. However,
1658offloading this complexity to a completely separate layer has proven
1659very effective in practice.
1660
1514@node Extending Eglot 1661@node Extending Eglot
1515@chapter Extending Eglot 1662@chapter Extending Eglot
1516 1663
@@ -1687,9 +1834,13 @@ slowly, try to customize the variable @code{eglot-events-buffer-config}
16870. This will disable recording any events and may speed things up. 18340. This will disable recording any events and may speed things up.
1688 1835
1689In other situations, the cause of poor performance lies in the language 1836In other situations, the cause of poor performance lies in the language
1690server itself. Servers use aggressive caching and other techniques to 1837server itself. Some servers use aggressive caching and other techniques
1691improve their performance. Often, this can be tweaked by changing the 1838to improve their performance. Often, this can be tweaked by changing
1692server configuration (@pxref{Advanced server configuration}). 1839the server configuration (@pxref{Advanced server configuration}).
1840
1841Another aspect that may cause performance degradation is the amount of
1842JSONRPC information exchanged with Emacs. Using an LSP program like
1843@ref{Using Rassumfrassum,Rassumfrassum} may alleviate such problems.
1693 1844
1694@node Getting the latest version 1845@node Getting the latest version
1695@section Getting the latest version 1846@section Getting the latest version
@@ -1751,10 +1902,17 @@ may be using. If possible, try to replicate the problem with the
1751C/C@t{++} or Python servers, as these are very easy to install. 1902C/C@t{++} or Python servers, as these are very easy to install.
1752 1903
1753@item 1904@item
1754Describe how to setup a @emph{minimal} project directory where Eglot 1905If using an LSP multiplexer server like @ref{Using Rassumfrassum,
1906Rassumfrassum}, first verify if the program replicates by using one of
1907the multiplexed servers directly. If it doesn't the problem lies in the
1908LSP multiplexer program and should be reported there.
1909
1910@item
1911Include a description of a @emph{minimal} project directory where Eglot
1755should be started for the problem to happen. Describe each file's name 1912should be started for the problem to happen. Describe each file's name
1756and its contents. Alternatively, you can supply the address of a public 1913and its contents, or---sometimes better--- zip that project directory
1757Git repository. 1914completely and attach it. Alternatively, you can supply the address of
1915a public Git repository.
1758 1916
1759@item 1917@item
1760Include versions of the software used. The Emacs version can be 1918Include 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
1767It's also essential to include the version of ELPA packages that are 1925It's also essential to include the version of ELPA packages that are
1768explicitly or implicitly loaded. The optional but popular Company or 1926explicitly or implicitly loaded. The optional but popular Company or
1769Markdown packages are distributed as GNU ELPA packages, not to mention 1927Markdown packages are distributed as GNU ELPA packages, not to mention
1770Eglot itself in some situations. Some major modes (Go, Rust, etc.) are 1928Eglot itself in some situations. Prefer reproducing the problem with
1771provided by ELPA packages. It's sometimes easy to miss these, since 1929built-in Treesit major modes like @code{go-ts-mode} or
1772they are usually implicitly loaded when visiting a file in that 1930@code{rust-ts-mode} since the non-ts modes for such languages are
1773language. 1931usually provided by ELPA packages, and it's often easy to miss them.
1774 1932
1775ELPA packages usually live in @code{~/.emacs.d/elpa} (or what is in 1933If you can't reproduce your bug without ELPA packages, you may find the
1934ones you're using in @code{~/.emacs.d/elpa} (or what is in
1776@code{package-user-dir}). Including a listing of files in that 1935@code{package-user-dir}). Including a listing of files in that
1777directory is a way to tell the maintainers about ELPA package versions. 1936directory is a way to tell the maintainers about ELPA package versions.
1778 1937
diff --git a/etc/EGLOT-NEWS b/etc/EGLOT-NEWS
index 9c7786f09b9..8735e966ee9 100644
--- a/etc/EGLOT-NEWS
+++ b/etc/EGLOT-NEWS
@@ -20,6 +20,20 @@ https://github.com/joaotavora/eglot/issues/1234.
20 20
21* Changes to upcoming Eglot 21* Changes to upcoming Eglot
22 22
23** Support for LSP server multiplexers via Rassumfrassum
24
25Eglot can now leverage LSP server multiplexer programs like Rassumfrassum
26(invoked via the 'rass' command) to use multiple language servers in a
27single buffer. This enables combining spell-checkers with language
28servers, using multiple servers for the same language (e.g., 'ty' for
29type checking and 'ruff' for linting in Python), or handling
30multi-language files like Vue.
31
32Some invocations of 'rass' are offered as alternatives in the built-in
33'eglot-server-programs' variable. The manual (readable with 'M-x
34eglot-manual') contains a comprehensive discussion of how to set up and
35use multiplexers in the new "Multi-server support" chapter.
36
23** Support for pull diagnostics (github#1559, github#1290) 37** Support for pull diagnostics (github#1559, github#1290)
24 38
25For servers supporting the 'diagnosticProvider' capability, Eglot 39For servers supporting the 'diagnosticProvider' capability, Eglot
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 8be3a459b95..b6c4d5b7a89 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -238,7 +238,7 @@ automatically)."
238(defvar eglot-server-programs 238(defvar eglot-server-programs
239 ;; FIXME: Maybe this info should be distributed into the major modes 239 ;; FIXME: Maybe this info should be distributed into the major modes
240 ;; themselves where they could set a buffer-local `eglot-server-program' 240 ;; themselves where they could set a buffer-local `eglot-server-program'
241 ;; instead of keeping this database centralized. 241 ;; which would allow deprecating this database.
242 ;; FIXME: With `derived-mode-add-parents' in Emacs≥30, some of 242 ;; FIXME: With `derived-mode-add-parents' in Emacs≥30, some of
243 ;; those entries can be simplified, but we keep them for when 243 ;; those entries can be simplified, but we keep them for when
244 ;; `eglot.el' is installed via GNU ELPA in an older Emacs. 244 ;; `eglot.el' is installed via GNU ELPA in an older Emacs.
@@ -248,7 +248,8 @@ automatically)."
248 (vimrc-mode . ("vim-language-server" "--stdio")) 248 (vimrc-mode . ("vim-language-server" "--stdio"))
249 ((python-mode python-ts-mode) 249 ((python-mode python-ts-mode)
250 . ,(eglot-alternatives 250 . ,(eglot-alternatives
251 '("pylsp" "pyls" ("basedpyright-langserver" "--stdio") 251 '(("rass" "python")
252 "pylsp" "pyls" ("basedpyright-langserver" "--stdio")
252 ("pyright-langserver" "--stdio") 253 ("pyright-langserver" "--stdio")
253 ("pyrefly" "lsp") 254 ("pyrefly" "lsp")
254 ("ty" "server") 255 ("ty" "server")
@@ -262,7 +263,9 @@ automatically)."
262 (tsx-ts-mode :language-id "typescriptreact") 263 (tsx-ts-mode :language-id "typescriptreact")
263 (typescript-ts-mode :language-id "typescript") 264 (typescript-ts-mode :language-id "typescript")
264 (typescript-mode :language-id "typescript")) 265 (typescript-mode :language-id "typescript"))
265 . ("typescript-language-server" "--stdio")) 266 . ,(eglot-alternatives
267 '(("rass ts")
268 ("typescript-language-server" "--stdio"))))
266 ((bash-ts-mode sh-mode) . ("bash-language-server" "start")) 269 ((bash-ts-mode sh-mode) . ("bash-language-server" "start"))
267 ((php-mode phps-mode php-ts-mode) 270 ((php-mode phps-mode php-ts-mode)
268 . ,(eglot-alternatives 271 . ,(eglot-alternatives