Configuring Emacs Eglot for Optimal Performance

Eglot ships with Emacs as a built-in, lightweight LSP client, and its default configuration is sufficient for most projects. However, working with large codebases can cause noticeable latency. When Eglot becomes sluggish, the problem may involve work performed by Eglot and Emacs as well as the language server itself. JSON-RPC event logging, filesystem watching, diagnostics, completion, and other editor activity can all contribute to latency.

This article covers practical changes for keeping Eglot responsive when working with large projects.

Auto-shutting down idle Eglot servers

This reduces resource usage when switching between multiple projects. The eglot-autoshutdown variable shuts down the LSP server after the last managed buffer has been killed.

(setq eglot-autoshutdown t)

This is useful when many separate language servers would otherwise remain running after their buffers have been closed.

(If you find yourself routinely leaving unused buffers open, the buffer-terminator package can automate this cleanup. It quietly monitors your buffer activity and terminates idle file buffers, ensuring eglot-autoshutdown can trigger reliably without requiring you to manually execute kill-buffer.)

Preventing Eglot from blocking the Emacs UI on connection

Opening a project can cause Emacs to block while waiting for the LSP server to initialize. Setting eglot-sync-connect to 0 prevents Eglot from blocking on the initial connection, allowing the connection to continue in the background:

(setq eglot-sync-connect 0)

This prevents the connection attempt from unnecessarily delaying interactive editing.

Reducing LSP event logging

Eglot maintains an events buffer containing JSON-RPC activity. Large event logs consume memory and add string allocation costs, especially when retaining complete JSON payloads is not needed during normal editing.

Configuring eglot-events-buffer-config (or eglot-events-buffer-size on Emacs 29 and earlier) reduces the volume of retained data:

;; Disable event logging completely (Emacs >= 30)
(setq eglot-events-buffer-config '(:size 0 :format short))

;; For Emacs <= 29
;; (setq eglot-events-buffer-size 0)

Setting :size 0 disables the events buffer entirely.

In addition, disabling the JSON-RPC event hook also avoids serializing JSON payloads:

(setq jsonrpc-event-hook nil)

The tradeoff is that disabling the JSON-RPC event hook (jsonrpc-event-hook) removes useful debugging information.

Reducing file watchers

Allowing a language server to monitor large portions of a repository can consume OS file descriptors, increase memory usage, and add startup delay. To reduce these resource costs, set a ceiling on active watches in Eglot:

(setq eglot-max-file-watches 3000)

In addition, where supported by the language server, configure it to ignore large generated directories such as node_modules, .git, or build output folders. Because these exclusion rules are specific to the language server, they must be passed through the eglot-workspace-configuration variable. (The standard method is to define these rules per-project using a .dir-locals.el file at the root of your repository.)

Disabling progress reporting

Whenever a server sends notifications (e.g., during indexing or builds), this triggers continuous mode-line redisplays. The following suppress mode-line progress reports:

;; Suppress mode-line progress animations
(setq eglot-report-progress nil)

Disabling automatic code action probing

By default, Eglot asynchronously polls the language server for available code actions whenever the cursor idles. This is responsible for displaying visual indicators, such as a lightbulb, in the editor's fringe or margin.

Disabling these automatic indications reduces continuous process communication and background activity during editing:

;; Disable automatic code action indicators to reduce background polling
(setq eglot-code-action-indications nil)

Note: Disabling this feature only removes the automatic UI indicators. You retain the ability to manually request and execute code actions at any time by invoking M-x eglot-code-actions.

Disabling UI noise, formatting, and unneeded LSP server capabilities

LSP servers can provide optional capabilities that are not required for every workflow. Disabling features that are not needed can reduce processing and visual clutter, although the actual performance benefit depends on the language server and project.

Delegating code formatting to dedicated tools such as Apheleia can separate formatting from the language server and provide asynchronous formatting workflows. The tradeoff is additional formatter configuration and the loss of the language server's built-in formatting:

(add-to-list 'eglot-ignored-server-capabilities :documentFormattingProvider)
(add-to-list 'eglot-ignored-server-capabilities :documentRangeFormattingProvider)
(add-to-list 'eglot-ignored-server-capabilities :documentOnTypeFormattingProvider)

Inlay hints insert automatically determined types and parameter names directly into the buffer. Types describe what kind of value an expression or variable represents, such as string, int, or User, while parameter names identify the arguments expected by a function or method. Disabling them removes the associated visual annotations and any work required to maintain them:

(add-to-list 'eglot-ignored-server-capabilities :inlayHintProvider)

When the cursor rests on a symbol, the LSP server can highlight other occurrences of that symbol. Disabling this feature removes those transient highlights and their associated updates:

(add-to-list 'eglot-ignored-server-capabilities :documentHighlightProvider)

Code lenses provide contextual annotations associated with source code. Depending on the language server, they can display information such as the number of references to a function or class, available test actions, implementation counts, or other metadata supplied by the language server. These annotations are typically displayed directly alongside the relevant source code and may require the language server to analyze the buffer and periodically update the displayed information. Disabling code lenses removes these annotations and eliminates the associated server/client processing required to calculate, transmit, and display them:

(add-to-list 'eglot-ignored-server-capabilities :codeLensProvider)

The LSP server can provide document links representing URLs or other navigable locations embedded in source files. For example, a language server can identify a URL, documentation reference, import target, or other location and expose it as a clickable link in the editor. Disabling this capability prevents Eglot from requesting and displaying these links, removing the associated link detection and presentation from the LSP integration:

(add-to-list 'eglot-ignored-server-capabilities :documentLinkProvider)

Some servers render small color swatches next to hex codes or RGB values in your code. Disabling this has the benefit of saving rendering time by preventing the editor from calculating and drawing these visual elements:

(add-to-list 'eglot-ignored-server-capabilities :colorProvider)

If you rely on Emacs' built-in folding (hs-minor-mode), Tree-sitter, or outline-indent for code folding, having the language server compute and transmit folding ranges is redundant. For large files, calculating these ranges can be expensive for the server. Disabling this capability saves computation and bandwidth:

(add-to-list 'eglot-ignored-server-capabilities :foldingRangeProvider)

These settings depend on the workload and are not necessary for every setup. Each disabled capability removes functionality as well as its associated processing.

Garbage collection, native compilation, and read process output max

  • The Emacs garbage collector can cause pauses during heavy workloads. LSP activity can increase allocation through diagnostics, completion, JSON processing, and other editor integrations. GC tuning can therefore help in some workloads, but the effect is workload-dependent. Increasing gc-cons-threshold permanently can reduce garbage collection frequency:
    (setq gc-cons-threshold (* 100 1024 1024))
    (Alternatively, several users rely on the gcmh package, which raises the GC threshold during active editing and forces a collection when Emacs becomes idle.)
  • Enable Native Compilation: Ensure that Emacs is built with native compilation support enabled. Native compilation can improve the execution speed of Emacs Lisp code, including code used by Eglot and other packages. (Recommendation: Use the compile-angel package to ensure that all packages are natively compiled.)
  • read-process-output-max: read-process-output-max controls the maximum amount of data Emacs reads from a subprocess in a single operation. Since Eglot communicates with language servers through subprocesses, increasing this value can improve performance when a server sends large bursts of JSON-RPC data, such as during initialization, workspace indexing, or large file updates, by reducing the number of read operations required to consume the output. Raising it can help workloads that regularly receive large bursts of process output:
    (setq read-process-output-max (* 4 1024 1024))
    (This raises the limit to 4 MiB. It does not reserve 4 MiB of memory for each process or increase the amount of data a server can send; it only allows Emacs to consume more output per read operation.)

Freeing up the main Emacs thread with tree-sitter

Emacs executes Lisp, handles asynchronous process output, and updates the UI on a single main thread. In traditional major modes, typing triggers complex regular expression evaluations for syntax highlighting (font-locking). This CPU-bound work competes directly with Eglot, which relies on the exact same thread to deserialize incoming JSON-RPC payloads and render diagnostics. If the main thread is busy computing regexes, Eglot is forced to wait in the queue, resulting in input lag even if the external language server responds instantly.

Major modes built on Tree-sitter (the *-ts-mode variants, such as c-ts-mode, python-ts-mode, and rust-ts-mode) offload syntax parsing to a fast C library. If a stable *-ts-mode exists for your programming language, enabling it can improve Eglot's responsiveness.

Leave a Reply

Your email address will not be published. Required fields are marked *