Eglot: prevent textDocument/diagnostic from being sent before didOpen

Set eglot--docver to -1 in LSP documents not yet 'didOpen'ed,
then add a check for this in the jsonrpc-connection-ready-p
predicate.

We do this because the call to eglot-flymake-backend may come in
so fast that textDocument/diagnostic actually makes it into the
jsonrpc queue before the didOpen.  Much like, say, completions
before didChange, some servers don't like that, understandibly.

So use the existing "deferred" mechanism checks to make sure, as
usual, that requests targetting a specific LSP document come
after the didOpen/didChange informing the server of the actual
state of the buffer.

I _could_ have used nil instead of -1, and it would probably be
cleaner.  But -1 is safer, we never know if a version comparison
won't slip outside the didOpen period.  Might change my mind
about this.

* lisp/progmodes/eglot.el (eglot--docver): Init to -1.
(eglot--managed-mode): Set eglot--docver to -1 when unmanaging.
(jsonrpc-connection-ready-p): Check eglot--docver non-negative.
(eglot--signal-textDocument/didClose): Set eglot--docver to -1.
This commit is contained in:
João Távora 2026-01-05 17:41:01 +00:00
parent 3ffbc5a709
commit d55a455ec2

View file

@ -1309,7 +1309,7 @@ If optional MARKERS, make markers instead."
(cl-defmethod initialize-instance :before ((_server eglot-lsp-server) &optional args)
(cl-remf args :initializationOptions))
(defvar-local eglot--docver 0
(defvar-local eglot--docver -1
"LSP document version. Bumped on `eglot--after-change'.")
(defvar eglot--servers-by-project (make-hash-table :test #'equal)
@ -2286,6 +2286,7 @@ LSP Document version reported for DIAGNOSTICS (comparable to
(eldoc-mode 1))
(cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server))))
(t
(setq eglot--docver -1)
(eglot-inlay-hints-mode -1)
(eglot-semantic-tokens-mode -1)
(eglot--delete-overlays 'eglot--overlay)
@ -2952,7 +2953,7 @@ buffer."
(cl-defmethod jsonrpc-connection-ready-p ((_server eglot-lsp-server) _what)
"Tell if SERVER is ready for WHAT in current buffer."
(and (cl-call-next-method) (not eglot--recent-changes)))
(and (cl-call-next-method) (not (cl-minusp eglot--docver)) (not eglot--recent-changes)))
(defvar-local eglot--change-idle-timer nil "Idle timer for didChange signals.")
@ -3158,6 +3159,7 @@ When called interactively, use the currently active server"
(defun eglot--signal-textDocument/didClose ()
"Send textDocument/didClose to server."
(setq eglot--docver -1)
(with-demoted-errors
"[eglot] error sending textDocument/didClose: %s"
(jsonrpc-notify