bug#81514: 32.0.50; Eglot messages progress reporter shows errors
Stéphane Marks <[email protected]> Wed, 29 Jul 2026 22:28:04 +0200
| Newsgroups | gmane.emacs.bugs |
|---|---|
| Message-ID | <CAN+1HbrNi6wDy4FnGzC3zc0kVDFCqSydBrKXhqvR5RZ1MFpZGQ@mail.gmail.com> |
On Wed, Jul 29, 2026 at 3:59 PM João Távora <[email protected]> wrote: > Hi! > > If there are confirmed failings in Eglot's usage of progress reporters, > fixes are welcome. > > That said, please tread carefully: said fixes must not endanger (or worsen > in any way) the backward compatibility of eglot.el all the way to at least > Emacs 27, where these progress reporters didn't exist or were more > primitive. I'd also appreciate if said fixes are succinct in nature, and > not architecture-changing... > > Thanks! > João > > > > > > > > > > On Wed, Jul 29, 2026, 20:33 Stéphane Marks <[email protected]> wrote: > >> On Wed, Jul 29, 2026 at 2:47 PM Stéphane Marks <[email protected]> >> wrote: >> >>> On Wed, Jul 29, 2026 at 11:17 AM Jacek Świerk <[email protected]> >>> wrote: >>> >>>> I can reproduce it with clangd on the following C++ repository: >>>> https://github.com/rui314/mold. It doesn't happen every time but often >>>> enough: >>>> >>>> Debugger entered--Lisp error: (wrong-type-argument sequencep 100) >>>> ((20 . [1785337541.907988 nil nil "[eglot] mold >>>> backgroundIndexProgress: indexing..." 1 0.2 nil nil]) 20 100) >>>> progress-reporter-do-update((20 . [1785337541.907988 nil nil "[eglot] >>>> mold backgroundIndexProgress: indexing..." 1 0.2 nil nil]) 100 nil) >>>> eglot--reporter-update((20 . [1785337541.907988 nil nil "[eglot] mold >>>> backgroundIndexProgress: indexing..." 1 0.2 nil nil]) 100 nil) >>>> #f(compiled-function (arg1 arg2 arg3 arg4 &rest rest) #<bytecode >>>> 0xa1216d65848076a>)(#<eglot-lsp-server 1022b7360904> >>>> "backgroundIndexProgress" 100 nil) >>>> #f(compiled-function (arg1 arg2 &rest rest) "Handle $/progress >>>> notification identified by TOKEN from SERVER." #<bytecode >>>> -0xf68beacb9f43862>)(#<eglot-lsp-server 1022b7360904> $/progress :token >>>> "backgroundIndexProgress" :value (:kind "end")) >>>> apply(#f(compiled-function (arg1 arg2 &rest rest) "Handle $/progress >>>> notification identified by TOKEN from SERVER." #<bytecode >>>> -0xf68beacb9f43862>) #<eglot-lsp-server 1022b7360904> $/progress (:token >>>> "backgroundIndexProgress" :value (:kind "end"))) >>>> eglot-handle-notification(#<eglot-lsp-server 1022b7360904> $/progress >>>> :token "backgroundIndexProgress" :value (:kind "end")) >>>> apply(eglot-handle-notification #<eglot-lsp-server 1022b7360904> >>>> $/progress (:token "backgroundIndexProgress" :value (:kind "end"))) >>>> #f(compiled-function (server method params) #<bytecode >>>> 0x1f1b657efb0838aa>)(#<eglot-lsp-server 1022b7360904> $/progress (:token >>>> "backgroundIndexProgress" :value (:kind "end"))) >>>> jsonrpc-connection-receive(#<eglot-lsp-server 1022b7360904> (:jsonrpc >>>> "2.0" :method "$/progress" :params (:token "backgroundIndexProgress" :value >>>> (:kind "end")))) >>>> #f(compiled-function (conn msg) #<bytecode >>>> 0x4e6153735258fd3>)(#<eglot-lsp-server 1022b7360904> (:jsonrpc "2.0" >>>> :method "$/progress" :params (:token "backgroundIndexProgress" :value >>>> (:kind "end")))) >>>> apply(#f(compiled-function (conn msg) #<bytecode 0x4e6153735258fd3>) >>>> (#<eglot-lsp-server 1022b7360904> (:jsonrpc "2.0" :method "$/progress" >>>> :params (:token "backgroundIndexProgress" :value (:kind "end"))))) >>>> timer-event-handler([t 27242 5829 902968 nil #f(compiled-function >>>> (conn msg) #<bytecode 0x4e6153735258fd3>) (#<eglot-lsp-server 1022b7360904> >>>> (:jsonrpc "2.0" :method "$/progress" :params (:token >>>> "backgroundIndexProgress" :value (:kind "end")))) nil 894000 nil]) >>>> >>>> The LSP message that causes it looks like this from the >>>> (eglot-events-buffer): >>>> <-- $/progress >>>> {"jsonrpc":"2.0","method":"$/progress","params":{"token":"backgroundIndexProgress","value":{"kind":"end"}}} >>>> >>>> Based on my understanding of Eglot code, Eglot creates a non-numeric >>>> progress >>>> reporter because MIN-VALUE and MAX-VALUE arguments are not provided. >>>> However, it >>>> looks like Eglot updates the created progress reporter as it were >>>> numeric: >>>> (eglot--reporter-update pr pcnt msg) >>>> >>>> For the {"kind": "end"} notification, pcnt is set to 100 and msg is >>>> nil. After >>>> commit b6849229003, the pulsing reporter forwards (or update-text >>>> value) as >>>> UPDATE-TEXT. That evaluates to 100. Then progress-reporter-echo-area >>>> tries >>>> (concat (if update-text " " "") update-text) which triggers the shown >>>> error. >>>> >>> I was able to reproduce the issue without eglot and will take a look. >>> >>> Coincidentally, and independent of the issue, eglot seems to want a >>> numeric reporter but instead creates a non-numeric reporter. eglot-handle-notification >>> method seems to also be missing the call to progress-reporter-done that >>> would clear any stateful reporters it's created such as those in >>> system-taskbar for which a relevant change was made in Tramp recently. I >>> can take a stab at those. >>> >> >> Here's a patch for progress-reporter. I can look into eglot separately >> if agreed. >> > Something like this patch should work and it is backward compatible to Emacs 27. I added a comment about the LSP protocol value range for percentage and it says we can ignore values outside that range and/or integer type. Should we? diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f701a38ab8b..173a0b87f68 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -2870,9 +2870,17 @@ eglot-handle-notification (cl-flet ((fmt (&rest args) (mapconcat #'identity args " ")) (mkpr (title) (if (eq eglot-report-progress 'messages) + ;; https://github.com/microsoft/language-server-protocol/blob/b7f5132c95261c0898ae5124e7a91707abc48fcd/_specifications/specification-3-16.md?plain=1#L1455 + ;; + ;; The value range is [0, 100]. + ;; + ;; The value should be steadily rising. Clients are + ;; free to ignore values that are not following this + ;; rule. (make-progress-reporter (format "[eglot] %s %s: %s" - (eglot-project-nickname server) token title)) + (eglot-project-nickname server) token title) + 0 100) (list 'eglot--mode-line-reporter token title))) (upd (pcnt msg &optional (pr (gethash token (eglot--progress-reporters server)))) @@ -2880,7 +2888,14 @@ eglot-handle-notification ((eq (car pr) 'eglot--mode-line-reporter) (setcdr (cddr pr) (list msg pcnt)) (force-mode-line-update t)) - (pr (eglot--reporter-update pr pcnt msg))))) + (pr (eglot--reporter-update pr pcnt msg)))) + (done (&optional + (pr (gethash token (eglot--progress-reporters server)))) + (cond + ((eq (car pr) 'eglot--mode-line-reporter) + (setcdr (cddr pr) (list "" 100)) + (force-mode-line-update t)) + (pr (progress-reporter-done pr))))) (eglot--dbind ((WorkDoneProgress) kind title percentage message) value (pcase kind ("begin" @@ -2889,6 +2904,7 @@ eglot-handle-notification (eglot--progress-reporters server)))) ("report" (upd percentage message)) ("end" (upd (or percentage 100) message) + (done) (run-at-time 2 nil (lambda () (remhash token (eglot--progress-reporters server))))))))))