LSP with Emacs 29
Posted: ; Updated: 16 Dec, 2022 emacs
Eglot, an Emacs package that integrates the language server protocol (LSP) into Emacs, was just merged into Emacs main. It joins tree-sitter and use-package as another reason to be excited about the Emacs 29 release.
This post aims to answer some common questions I observed in recent discussions around the Eglot merge. In particular, what is LSP and how do alternatives compare to Eglot?
What is LSP?
LSP was originally designed to provide IDE language features to Visual Studio Code, e.g., "Go to definition" or "Find references" popups. The project was later open sourced by Microsoft and now has a formal specification.
The protocol came out of the need to design an editor-specific compiler that prioritizes the features programmers care about when actively writing code, as opposed to compilers that work with code that is already written. While such a compiler can be built on an ad-hoc basis for a specific IDE, it is difficult to share that work with other editors that may be built using a different programming language. With LSP, the compiler is run in an independent process that communicates to the editor via messages, serialized as JSON.
Decoupling the language server implementation from the editor or extension implementation is a great move that has seen a broad level of adoption in the editor space, even outside of Visual Studio Code. And now with Emacs 29, we have support built into Emacs.
Why was Eglot merged instead of alternatives?
Two primary reasons come to mind:
- Free Software Foundation (FSF) licensing
- Idiomatic Emacs Lisp and package standards
The former reason refers to the GNU requirement that "legally significant" contributions (e.g. 15+ LOC) are accompanied by signed paperwork. If you've ever tried contributing to an Emacs package that lives in Emacs main you're probably already familiar with the process of submitting a request, signing the documents, and sending them in for approval.
Since the nature of this requirement extends to all contributors of an Emacs package, it can be difficult to gain approval to merge a new package into Emacs main without requiring copyright from the beginning. Otherwise, package maintainers will need to hunt down contributors to get them to sign the relevant paperwork (see use-package#282 as an example).
The secondary reason has to do with Eglot's implementation. Compared to alternatives, Eglot doesn't depend on any packages external to Emacs and integrates nicely with existing Emacs tools like Flymake, Xref, etc. These qualities make it easier to merge and ship with the standard Emacs distribution.
How do alternatives differ?
lsp-mode
The alternative I have the most experience with is lsp-mode, which I used for six or so months before switching to Eglot. Unlike Eglot, lsp-mode has no intention of being merged into Emacs main, allowing it some more flexibility in its design and implementation.
Compared to Eglot, lsp-mode is the more maximal package. For one, it supports the entire LSP specification. It also has a ton of extra features, including bespoke UI, non-ELPA package integrations like dap-mode and dash, and support for multiple language servers for a single file.
Whether you decide to pick lsp-mode over Eglot probably comes down to your reaction to lsp-ui. If you desire a fully-featured IDE experience, lsp-mode will serve you well. Otherwise, you may appreciate Eglot's simpler take.
No matter which LSP package you pick, it's worth reading through lsp-mode's performance documentation. It's beneficial no matter which LSP package you choose.
lsp-bridge
lsp-bridge aims to be "the fastest LSP client in Emacs". Contributor Matthew Zeng recently gave an introduction to the package at EmacsConf 2022, walking through it's implementation and goals.
In general, lsp-bridge aims to work around the single-threaded bottlenecks present in Emacs and Emacs Lisp. Since LSP works via JSON RPC, most of the performance issues are due to blocking IO and JSON parsing. lsp-bridge splits processing into two threads to better handle asynchronous communication between server and editor.
The quest for performance comes with a cost, however, in that lsp-bridge requires Python dependencies and ties its implementation to a specific completion framework bundled with the package.
tl;dr
Eglot is a rad LSP package that is now merged into Emacs main, staged for release with Emacs 29. It's well worth giving it a try, even if you already use lsp-mode. You may prefer its comparatively lightweight feel.
Eglot
- Minimalist implementation that "just works"
- Available out of the box with Emacs 29
- Development may slow down since it's now integrated in Emacs main
- No dependencies outside of Emacs
lsp-mode
- Fully supports LSP specification
- Optional UI extras with lsp-ui
- Debugging support with dap-mode
- Support for Flycheck, a Flymake alternative
- Can be a pain to set up properly
lsp-bridge
- Great performance at the cost of idiomatic Emacs conventions
- Coupled to a specific completion framework bundled with the package