bug#81514: 32.0.50; Eglot messages progress reporter shows errors

Stéphane Marks <[email protected]> Thu, 30 Jul 2026 01:54:04 +0200
Newsgroups gmane.emacs.bugs
Message-ID <CAN+1Hbo4getfT4FF0g3t55e9jennDgok8+Et58yfHNY8X9OR0g@mail.gmail.com>
On Wed, Jul 29, 2026 at 7:25 PM João Távora <[email protected]> wrote:

> Haven't read it, but see lots of + and only one - which makes me frown.
> What was so wrong in Eglot's use of this facility, which worked fine afaict
> until your changes?
>

Two issues with it.  First is that the LSP progress messages are
percentages growing from 0-100 and the pulsing reporter was incorrect for
that use.  It shows visibly when one uses the Emacs 31 system-taskbar
progress reporters that display graphical bars representing % completion.
Second is that those stateful progress bars need to be cleared when the
progress reporter is considered "done."  So we changed from a pulsing
reporter to a "numeric" reporter and correctly called
progress-reporter-done at the end.  We amended the Tramp progress reporter
similarly recently.


> João
>
> João Távora
>
> On Wed, Jul 29, 2026, 21:28 Stéphane Marks <[email protected]> wrote:
>
>> 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))))))))))
>>
>