From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Danny Freeman via "Bug reports for GNU Emacs, the Swiss army knife of text editors" Newsgroups: gmane.emacs.bugs Subject: bug#59149: Feature Request: Report progress of long requests in Eglot Date: Wed, 23 Nov 2022 09:12:12 -0500 Message-ID: <87tu2pohub.fsf@dfreeman.email> References: <87educqkar.fsf@dfreeman.email> <86cz9jmg9r.fsf@stephe-leake.org> <87k03qvmla.fsf@dfreeman.email> <86fseckwvb.fsf@stephe-leake.org> Reply-To: Danny Freeman Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="17070"; mail-complaints-to="usenet@ciao.gmane.io" Cc: 59149@debbugs.gnu.org To: Stephen Leake Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Wed Nov 23 15:40:33 2022 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1oxqvQ-0004GL-2o for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 23 Nov 2022 15:40:32 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oxquy-0002m9-TQ; Wed, 23 Nov 2022 09:40:04 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oxqux-0002ly-NN for bug-gnu-emacs@gnu.org; Wed, 23 Nov 2022 09:40:03 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1oxqux-0002DS-Dn for bug-gnu-emacs@gnu.org; Wed, 23 Nov 2022 09:40:03 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1oxqux-0003KV-5o for bug-gnu-emacs@gnu.org; Wed, 23 Nov 2022 09:40:03 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Danny Freeman Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Wed, 23 Nov 2022 14:40:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 59149 X-GNU-PR-Package: emacs X-Debbugs-Original-Cc: 59149@debbugs.gnu.org, bug-gnu-emacs@gnu.org Original-Received: via spool by 59149-submit@debbugs.gnu.org id=B59149.166921438712749 (code B ref 59149); Wed, 23 Nov 2022 14:40:03 +0000 Original-Received: (at 59149) by debbugs.gnu.org; 23 Nov 2022 14:39:47 +0000 Original-Received: from localhost ([127.0.0.1]:54270 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oxqug-0003JZ-Nx for submit@debbugs.gnu.org; Wed, 23 Nov 2022 09:39:47 -0500 Original-Received: from out2.migadu.com ([188.165.223.204]:62947) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oxque-0003JQ-8u for 59149@debbugs.gnu.org; Wed, 23 Nov 2022 09:39:45 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dfreeman.email; s=key1; t=1669214382; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=4IafvPNRpVK05lQ898+nI+om/qkHPIvzJuBWBQc0kcU=; b=MSdEDZ9QiftQccekNCbyy/ZgG7wuN+ib4lECE07XQP0orgVeYG/IyJ5rNAxsuZOmVuqbSO FqfnihdXp7bJ/o/4lA+AW4xhiBpLh4f9TFL7r7rUsY2VvZhpvmndzPNGhk18YWTa+Yguob xW98vdX3gyLK+qqbCKO+NCx5R2SCvvA= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. In-reply-to: <86fseckwvb.fsf@stephe-leake.org> X-Migadu-Flow: FLOW_OUT X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:248749 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Stephen Leake writes: > Note that this is checking the _server_ capabilities; since LSP does not > define "projess" as a capability, no server will ever advertise that > it is supported. > > So we can't use eglot-ignores-server-capabilities; we could maybe > introduce eglot-ignored-client-capabilities. > > But we already have a mechanism for that; eglot-stay-out-of. > > So a user can do: > > (add-to-list 'eglot-stay-out-of 'progress) > > And in eglot-handle-notification ($/progress) we check for that: > > (unless (eglot--stay-out-of-p 'progress) Yeah, I think this will be the way to go as well. > There's probably a way to fold that check into the cl-defmethod > dispatching parameters; I did not look into that. If there is I do not know how to do it either. I think a boolean check in the body of the function is fine (and probably more clear). > I just ran into the progress reporter in eglot--apply-text-edits; I'd > like to turn that off, but leave other progress-reporters on. >=20 > So we need something more fine-grained: >=20 > (setq eglot-progress-reporter-disable '(apply-text-edits)) I believe that is a different progress reporter, unrelated to the one I would like to introduce. It is not a progress report that come from the lsp server, so I don't think it would be good to conflate them. If the user wants to ignore specific progress indicators from the lsp servers then they could implement an empty version of eglot-handle-notification that matches their progress token. ``` (cl-defmethod eglot-handle-notification (_server (_method (eql $/progress)) &key (token (eql "THE PROGRESS TOKEN"= )) _value)) ``` That would be more targeted. I also suspect it would be a pretty rare occurrence. I personally think the best way to decide on adding more configuration variables to eglot is to release this into the wild and see what kind of feedback we receive. Responding to Jo=C3=A3o from a couple of threads ago: > Thanks Danny, this doesn't look bad at all, though I have yet to > understand how it works. I can half-see why progress status should be a > server property, but it would be better if you provided an automated > test, or maybe just a step-by-step sequence diagram (which can be in > plain text and language) that clarifies this. An animated gif that > might also work. I think I can explain it with a sample of my server logs: The first thing that happens is the lsp server sends a progress BEGIN message, which indicates to the client that it is starting some long running process. ``` [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "begin" :title "clojure-lsp" :percentage 0))) ``` Following that is a series of REPORT type progress messages. These indicate to the client that the work is ongoing. They might have a `percentage` key somewhere between 0 and 100 to show how the work is progressing. ``` [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "report" :title "Finding kondo config" :percentage= 5))) [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "report" :title "Finding cache" :percentage 10))) [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "report" :title "Copying kondo configs" :percentag= e 15))) [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "report" :title "Resolving config paths" :percenta= ge 15))) [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "report" :title "Analyzing project files" :percent= age 20))) ... this goes on for a while [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "report" :title "Analyzing project files" :percent= age 99))) ``` Finally, the server sends an END type progress message, indicating that the work is complete, and the client can stop showing the progress message. ``` [server-notification] Wed Nov 23 08:55:22 2022: (:jsonrpc "2.0" :method "$/progress" :params (:token "clojure-lsp" :value (:kind "end" :title "Project analyzed" :percentage 100))) ``` The result of this patch is a series of messages that display in the minibuffer: ``` [eglot:website] clojure-lsp:=20=20 [eglot:website] clojure-lsp: 5% Finding kondo config [eglot:website] clojure-lsp: 10% Finding cache [eglot:website] clojure-lsp: 15% Copying kondo configs [eglot:website] clojure-lsp: 20% Analyzing project files [eglot:website] clojure-lsp: 26% Analyzing project files [eglot:website] clojure-lsp: 33% Analyzing project files [eglot:website] clojure-lsp: 39% Analyzing project files [eglot:website] clojure-lsp: 46% Analyzing project files [eglot:website] clojure-lsp: 52% Analyzing project files [eglot:website] clojure-lsp: 59% Analyzing project files [eglot:website] clojure-lsp: 66% Analyzing project files [eglot:website] clojure-lsp: 72% Analyzing project files [eglot:website] clojure-lsp: 79% Analyzing project files [eglot:website] clojure-lsp: 85% Analyzing project files [eglot:website] clojure-lsp: 92% Analyzing project files [eglot:website] clojure-lsp: 99% Analyzing project files [eglot:website] clojure-lsp: done ``` Does that help? Attached is an updated patch that uses `eglot-stay-out-of` --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Eglot-Display-progress-notifications-in-minibuffer-a.patch Content-Description: progress reporter patch >From 54bc6b70c8a6da8853db1bcc45ca51796b98b6c2 Mon Sep 17 00:00:00 2001 From: dannyfreeman Date: Wed, 9 Nov 2022 08:46:45 -0500 Subject: [PATCH] Eglot: Display progress notifications in minibuffer as they arrive The LSP spec describes methods for reporting progress on long running jobs to the client: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#progress https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workDoneProgress This change reports those notifications in the minibuffer as they come in. It shows a percent indicator (if the server provides theme), or a spinner. This change should open the door for writing a "cancel long running request" command, which are identified by these progress notifications. See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_workDoneProgress_cancel --- lisp/progmodes/eglot.el | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index e057b12e0e..3dc71b148c 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -821,6 +821,9 @@ eglot-lsp-server (project :documentation "Project associated with server." :accessor eglot--project) + (progress-reporter-alist + :documentation "Alist of (PROGRESS-TOKEN . PROGRESS-REPORTER)." + :accessor eglot--progress-reporter-alist) (inhibit-autoreconnect :initform t :documentation "Generalized boolean inhibiting auto-reconnection if true." @@ -2037,6 +2040,43 @@ eglot-handle-notification (_server (_method (eql telemetry/event)) &rest _any) "Handle notification telemetry/event.") ;; noop, use events buffer +(defun eglot--progress-report-message (title message) + "Format a $/progress report message, given a TITLE and/or MESSAGE string." + (cond + ((and title message) (format "%s %s" title message)) + (title title) + (message message))) + +(defun eglot--progress-reporter (server token) + "Get a prgress-reporter identified by the progress TOKEN from the SERVER ." + (cdr (assoc token (eglot--progress-reporter-alist server)))) + +(defun eglot--progress-reporter-delete (server token) + "Delete progress-reporters identified by the progress TOKEN from the SERVER." + (setf (eglot--progress-reporter-alist server) + (assoc-delete-all token (eglot--progress-reporter-alist server)))) + +(cl-defmethod eglot-handle-notification + (server (_method (eql $/progress)) &key token value) + "Handle a $/progress notification identified by TOKEN from the SERVER." + (unless (eglot--stay-out-of-p 'progress) + (cl-destructuring-bind (&key kind title percentage message) value + (pcase kind + ("begin" (let* ((prefix (format (concat "[eglot] %s %s:" (when percentage " ")) + (eglot-project-nickname server) token)) + (pr (if percentage + (make-progress-reporter prefix 0 100 percentage 1 0) + (make-progress-reporter prefix nil nil nil 1 0)))) + (eglot--progress-reporter-delete server token) + (setf (eglot--progress-reporter-alist server) + (cons (cons token pr) (eglot--progress-reporter-alist server))) + (progress-reporter-update pr percentage (eglot--progress-report-message title message)))) + ("report" (when-let ((pr (eglot--progress-reporter server token))) + (progress-reporter-update pr percentage (eglot--progress-report-message title message)))) + ("end" (when-let ((pr (eglot--progress-reporter server token))) + (progress-reporter-done pr) + (eglot--progress-reporter-delete server token))))))) + (cl-defmethod eglot-handle-notification (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode' -- 2.38.1 --=-=-= Content-Type: text/plain -- Danny Freeman --=-=-=--