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

João Távora <[email protected]> Thu, 30 Jul 2026 00:25:45 +0100
Newsgroups gmane.emacs.bugs
Message-ID <CALDnm53xuFbmkweFnV+HyE58kiSrGbLX9jmeBBrpVLP0_j0PsQ@mail.gmail.com>
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?

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))))))))))
>