bug#81538: 32.0.50; Eglot: Non-local exit during window/showDocument handling

Eli Zaretskii <[email protected]> Sun, 02 Aug 2026 21:18:03 +0300
Newsgroups gmane.emacs.bugs
Message-ID <[email protected]>
> Date: Sun, 2 Aug 2026 14:05:56 -0400
> From: Troy Brown via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <[email protected]>
> 
> The Ada Language Server has a command for showing the "other file".
> This is similar to `ff-find-other-file` for navigating between an Ada
> body (similar to C source) file and an Ada specification (similar to C
> header) file.  It has advantages over `ff-find-other-file` as it can
> automatically map between files with non-standard naming conventions,
> so it is preferred over `ff-find-other-file`.  I trigger this command
> using `eglot-execute-command` which sends a
> `workspace/executeCommand`.  When the Language Server command is sent,
> the server responds with a `window/showDocument` request back to Emacs
> to open the corresponding file.  Eglot receives this request and
> starts the process of opening the corresponding file (via
> `find-file-noselect`), and sends a response back to the server.
> 
> As was noted in Bug#60088, when a string sent to `process-send-string`
> (via `jsonrpc-connection-send`) is large enough, it can cause
> reception of output from the server (likely due to the output buffer
> becoming full, causing output from the server to be processed by
> `jsonrpc-connection-receive` [via `jsonrpc--process-filter`]).
> 
> When the eglot package is loaded, it registers
> `eglot--maybe-activate-editing-mode` in the
> `after-change-major-mode-hook`.  As part of the `find-file-noselect`
> machinery, these hooks are executed and
> `eglot--maybe-activate-editing-mode` is run.  This function executes
> `eglot--signal-textDocument/didOpen` to indicate to the server the new
> document being opened (by sending `textDocument/didOpen` via
> `jsonrpc-connection-send` and ultimtely via `process-send-string`).
> The `textDocument/didOpen` ends up sending the entire buffer contents
> of the new file to the server as part of it's payload.  If this file
> is large enough, it can cause the scenario above where the output
> buffer becomes full and it starts processing output from the language
> server.
> 
> When Emacs starts processing the output from the server, it ends up
> processing the `workspace/executeCommand` response.  This bubbles down
> through `jsonrpc--process-filter`, `jsonrpc-connection-receive` and
> `jsonrpc--continue` where the "success function" is invoked.  This
> function is the lambda function setup in `jsonrpc-request` which
> performs a transfer of control via the "throw" in that function.  This
> causes a non-local exit and an unwinding all the way back to the
> `jsonrpc-request` from the original `workspace/executeCommand`
> command.  This non-local exit prevents the `textDocument/didOpen` from
> completing and prevents the `find-file-noselect` from finishing,
> leaving the buffer incompletedly initialized (e.g., `font-lock` is not
> enabled, etc.) and doesn't end up popping the buffer, so it stays
> hidden.
> 
> While I've documented the method by which this problem arises for
> myself, I think this scenario can arise in more generalized scenarios
> where outstanding replies might be received and cause in-progress
> transmissions to be aborted prematurely.  I think this might be solved
> by delaying processing of the process output while a send is currently
> in progress.
> 
> I have enabled numerous function traces to track this down and provide
> a visible representation of what appears to be happening.  The full
> eglot event and trace function logs can be found in the attachments.
> The following illustrates the interesting part of the eglot event log
> to illustrate the client/server communication described above.
> 
> ```text
> [jsonrpc] e[10:12:28.254] --> workspace/executeCommand[13]
> {"jsonrpc":"2.0","id":13,"method":"workspace/executeCommand","params":{"command":"als-other-file","arguments":[{"uri":"file:///home/troy/junk/crate/gtkada_24.0.0_80c56171/src/gtkada-mdi.adb"}]}}
> [jsonrpc] e[10:12:28.255]   <-- window/showDocument[4]
> {"jsonrpc":"2.0","id":4,"method":"window/showDocument","params":{"uri":"file:///home/troy/junk/crate/gtkada_24.0.0_80c56171/src/gtkada-mdi.ads","takeFocus":true}}
> [jsonrpc] e[10:12:28.256]     --> window/showDocument[4]
> {"jsonrpc":"2.0","id":4,"result":{"success":true}}
> [jsonrpc] e[10:12:28.313]   <-- workspace/executeCommand[13]
> {"jsonrpc":"2.0","id":13,"result":null}
> ```

Adding João.