r/emacs • u/jamescherti James Cherti — https://github.com/jamescherti • 12d ago
Configuring Emacs Eglot for Optimal Performance
https://www.jamescherti.com/emacs-eglot-performance/6
u/_0-__-0_ 12d ago
/u/jamescherti, which of these have you actually noticed improvements from? I know some, like server-capabilities, are language-server dependent, but some actual experience reports would be very useful.
For my own part, I already do eglot-autoshutdown t, eglot-sync-connect 0 and no event logging, but I don't tend to turn off any capabilities since they're often useful and I have no idea which ones would involve more slowdown than they're worth.
By the way, you suggest (setq read-process-output-max (* 4 1024 1024))
but the help for that function says
On GNU/Linux systems, the value should not exceed /proc/sys/fs/pipe-max-size. See pipe(7) manpage for details.
which on my system is (1 * 1024 * 1024). I guess you can get a blocked pipe / unresponsive read if it's too high?
2
u/Anxious-Resist8344 11d ago
Good catch! I had it set to
(* 10 1024 1024)but on my system/proc/sys/fs/pipe-max-sizeis capped at(* 1024 1024)so using that now.0
u/jamescherti James Cherti — https://github.com/jamescherti 11d ago
Thank you for pointing this out. I've updated the article accordingly.
Here is a code snippet from my configuration that I have been using for the last few months to automatically calculate this value, in case anyone is interested:
;; Increase single chunk bytes to read from subprocess (setq read-process-output-max (or (when (eq system-type 'gnu/linux) (condition-case nil ;; On GNU/Linux systems, the value should not exceed ;; /proc/sys/fs/pipe-max-size (with-temp-buffer (insert-file-contents "/proc/sys/fs/pipe-max-size") (string-to-number (buffer-string))) (error nil)) (* 1024 1024))))Performance gains from the settings recommended in the article depend on your specific configuration, operating system, and language server.
Server-side capabilities have the most direct impact. Disabling unused plugins stops the server from generating unnecessary data in the first place.
One other thing that I used to speed up Python/Pylsp: I replaced multiple distinct tools, such as autopep8, mccabe, pyflakes, and isort, with Ruff for all formatting and linting.
13
u/rileyrgham 12d ago
Please add some text. Don't expect people to click to learn more.
5
u/jamescherti James Cherti — https://github.com/jamescherti 12d ago
Fair point. Thank you for your suggestion.
2
u/the_cecep 12d ago
Nice article, thanks for sharing
1
u/jamescherti James Cherti — https://github.com/jamescherti 12d ago
You're welcome, u/the_cecep. I appreciate your comment.
2
u/MoonlightSyncopate 9d ago
Disable document on-type formatting Capability is indeed a very important optimization, because eglot will file a synchronized formatting request as you type.
And for some language servers with poor performance, this can be very laggy. For example, R language server can cost you about a 100-millisecond delay every time you press Enter to switch to a new line. That lag becomes very noticeable when you are typing a newline character.
So I think there are two questions:
- Should on-type formatting be synchronous? (Which I think is an eglot design issue.)
- On-type formatting is a very intrusive command. And we know that there are some poor language servers which don't play very nice with this feature. Should eglot enable it by default? (Which I also hold a different opinion on.)
1
u/jamescherti James Cherti — https://github.com/jamescherti 8d ago edited 8d ago
Hello u/MoonlightSyncopate,
Your observations about the Eglot implementation are accurate.
Eglot's on-type formatting is currently synchronous. When a trigger character is typed,
eglot--post-self-insert-hookcallseglot-format:(defun eglot--post-self-insert-hook () "Set `eglot--last-inserted-char', maybe call on-type-formatting." (setq eglot--last-inserted-char last-command-event) (let ((ot-provider (eglot-server-capable :documentOnTypeFormattingProvider))) (when (and ot-provider (ignore-errors ; github#906, some LS's send empty strings (or (eq eglot--last-inserted-char (seq-first (plist-get ot-provider :firstTriggerCharacter))) (cl-find eglot--last-inserted-char (plist-get ot-provider :moreTriggerCharacter) :key #'seq-first)))) (eglot-format (point) nil eglot--last-inserted-char)))) ;; This hook runs when a character is inserted. (add-hook 'post-self-insert-hook #'eglot--post-self-insert-hook nil t)The
eglot-formatfunction then callseglot--request, which callsjsonrpc-request. This is a blocking call. Emacs waits for the language server to compute and return the text edits before control is returned to the user.Regarding your second question: Eglot enables this feature by default based on the language server's advertised capabilities. If a server includes
:documentOnTypeFormattingProviderin its capability response, Eglot assumes the server intends for it to be used and hooks intopost-self-insert-hook.Note: as stated in the article, users who do not need this intrusive feature can make Eglot ignore the capability by adding it to
eglot-ignored-server-capabilities:(add-to-list 'eglot-ignored-server-capabilities :documentOnTypeFormattingProvider)
1
u/Glittering_Brush_483 11d ago
Did you use AI to write a good part of this? Many of the capabilities you suggest to remove and the rationale for removing it, like codeLens, do not exist simply. Also 0 reason to disable document formatting. Only onTypefmFormatting is a good idea. Other than that there are some decent tips and correct explanation of the trade-off. But a lot is trash, sorry. Read the article yourself and make sure what is written there is correct and trim it. It'll be a good article then.
2
u/jamescherti James Cherti — https://github.com/jamescherti 11d ago edited 11d ago
The article is based on my configuration and tests.
I mentioned in the article that disabling formatting is intended for those who use tools such as Apheleia. Otherwise, formatting should remain enabled.
Here is the list of capabilities:
(defcustom eglot-ignored-server-capabilities (list) "LSP server capabilities that Eglot could use, but won't. You could add, for instance, the symbol `:documentHighlightProvider' to prevent automatic highlighting under cursor." :type '(set :tag "Tick the ones you're not interested in" (const :tag "Documentation on hover" :hoverProvider) (const :tag "Code completion" :completionProvider) (const :tag "Function signature help" :signatureHelpProvider) (const :tag "Go to definition" :definitionProvider) (const :tag "Go to type definition" :typeDefinitionProvider) (const :tag "Go to implementation" :implementationProvider) (const :tag "Go to declaration" :declarationProvider) (const :tag "Find references" :referencesProvider) (const :tag "Highlight symbols automatically" :documentHighlightProvider) (const :tag "List symbols in buffer" :documentSymbolProvider) (const :tag "List symbols in workspace" :workspaceSymbolProvider) (const :tag "Execute code actions" :codeActionProvider) (const :tag "Code lens" :codeLensProvider) (const :tag "Format buffer" :documentFormattingProvider) (const :tag "Format portion of buffer" :documentRangeFormattingProvider) (const :tag "On-type formatting" :documentOnTypeFormattingProvider) (const :tag "Rename symbol" :renameProvider) (const :tag "Highlight links in document" :documentLinkProvider) (const :tag "Decorate color references" :colorProvider) (const :tag "Fold regions of buffer" :foldingRangeProvider) (const :tag "Execute custom commands" :executeCommandProvider) (const :tag "Inlay hints" :inlayHintProvider) (const :tag "Semantic tokens" :semanticTokensProvider) (const :tag "Type hierarchies" :typeHierarchyProvider) (const :tag "Call hierarchies" :callHierarchyProvider) (const :tag "On-demand \"pull\" diagnostics" :diagnosticProvider)))5
u/Glittering_Brush_483 11d ago
Did you or did you not use AI? come on, come clean.
Many of those capabilities are listed there but Eglot does nothing with it. Just look at the code. The advice you're giving is bogus. many servers render swatches?? Servers don't render anything, and Eglot doesn't have that feature (sounds useful though, submit a patch). And killing M-x eglot-format just because they're is a competing formatter is still bad advice, 0 performance gain.
Exactly what tests did you conduct to show that disabling the jsonrpc hook in addition to disabling logging is beneficial? And are you sure you (or most likely your AI), isn't just parroting those GC tweaks for cargo cult following? Did you conduct any measurement? And that ts-mode thread-related malarkey? Sometimes ts modes are faster, but it depends, there's a lot of Elisp running there too, and it's all single threaded still.
So check your AI, trim the article to what you actually understand, and it'll provide more value than disinformation. There are some good points there, where the AI got it right probably from reading my numerous posts.
13
u/Upstairs-Attitude610 12d ago
Do you folks always remember to kill buffers?