bug#81548: 31.0.91; eglot extremely slow with large source files

João Távora <[email protected]> Tue, 04 Aug 2026 23:12:26 +0100
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
TL;DR: The problem is clangd is extremely slow to calculate Semantic
Tokens for this very large file.  There's little Eglot can do about
this, except disabling semtok for this file, which is a suitable
workaround.  Clangd devs might be interested in profiling or explaining
(and confirming?) why clangd is irresponsive to other requests while
doing the relatively low-priority work of calculating semantic tokens.

>> From: Peter Wu <[email protected]>
>> Date: Mon, 3 Aug 2026 20:57:29 +0000

Hello Peter, thanks for reporting this.

First of all, a word on the reproduction recipe, which is excellent.  It
is is still longer than it needs to be.  Steps 7/8/9 can be use a
simpler way to create an utterly configless Emacs and launch eglot in a
file eglot'.  Avoid eglot-ensure and the opaque use-package macro.

7->9. /path/to/emacs -Q path/to/file.c -f eglot

>> 0. make sure you have clangd installed. I'm on macOS 26.6 and use the
>> system stock clangd: Apple clangd version 21.0.0 (clang-2100.1.1.101)
>> 1. git clone https://github.com/wolfSSL/wolfssl
>> 2. cd wolfssl
>> 3. ./autogen.sh (assuming you have the autotools installed)
>> 4. ./configure --enable-certgen \
>>             --enable-certreq \
>>             --enable-certext \
>>             --enable-crl \
>>             --enable-asn \
>>             --enable-opensslextra
>> 5. generate a compile_commands.json (I used: uvx compiledb make -j8)

Thank you for the ./configure trick, my compile_commands.json is now
fixed and I can open the file with just the "normal" error.  I didn't
need to make the whole project though . After 'configure' only this is
needed:

   bear -- make wolfcrypt/test/test.o

which is much faster (I suppose compiledb also works).  CMake doesn't
work for this project very well, but it does reproduce the specific bug.
So I reiterate the 5 liner here for future reference:

    clangd --version && emacs --version  # clangd 22.sth/emacs 32 for me
    git clone --depth=3D1 https://github.com/wolfSSL/wolfssl.git
    cd wolfssl
    cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=3Don
    path/to/emacs/master/emacs -Q wolfcrypt/test/test.c -f eglot
    ... and witness every synchronous request like find-definition hang
    for minutes.

Now, to the actual bug, it's the "semantic fontification", aka semtok,
which is a way for servers to say this bit here is a variable, this is a
function call, this is an operator, etc.

The reason you don't see this in Emacs 30.2 is that that version didn't
have semantic tokens.

Eglot turns semtok on by default in all buffers.  You can prevent that.
This is a workaround, if a bit heavy handed (becasue you lose semantic
tokens in all files)

  (add-hook 'eglot-managed-mode-hook
      (lambda ()
        (eglot-semantic-tokens-mode -1)))

The reason why this takes so long is:

  1. This file is almost 1M lines long
  2. It has lots of macros?

When it starts managing a file, one of the steps Eglot does is ask the
LSP server for tokens.  This file has tons of "tokens", and Eglot asks
for all of them upfront.  Although LSP has a mechanism for asking for
less tokens, known as textDocument/semanticTokens/range, it seems absent
from the 'clangd' server (and Eglot doesn't support it anymore, anyway,
I'd have to bring that code back which isn't simple).

Anyway, this "get tokens" is **asynchronous** and runs in the
background, which is why you can continue to scroll the buffer and do
other things like finding functions.

Anyway, clangd takes a very long time to calculate these tokens and gets
very busy. If you let it finish you can then probably continue working
normally.  If you do edits to the buffer, Eglot sends these to the LSP
server who has to recalculate tokens.  Oh no, you may despair, but in
that case Eglot asks only for a "delta" of tokens which is pretty fast.
Which is why after the first 45 seconds everything works smoothly for
you.

Anyway, if you do not decide to wait and at a certain point you decide
to find a definition that request is **synchronous**, which is why Emacs
will seem stuck if the request isn't answered.  Emacs/Eglot will wait
until the server answers.

Emacs's thread isn't doing any work, it's just down a pipe waiting for
input, but clangd's threads are very busy and for some reason the rest
of clangd doesn't remain responsive while this happens.  It lets all
other requests die.  Maybe clangd devs can be brought in.  I'm trying my
luck with Natan Ridge, aka HighCommander4, maybe he has insight.  You
may want to report the bug to https://github.com/clangd/clangd/issues.

The only thing I can't explain from Eglot's side is why the
xref-find-definitions sync request by Eglot doesn't time out after 10
seconds: that should be the default.  I.e. it shouldn't have an infinite
timeout, it should error after a while.

Jo=C3=A3o