]> git.eshelyaron.com Git - emacs.git/commitdiff
Eglot: Handle LSP progress with Emacs progress reporters (bug#59149)
authordannyfreeman <danny@dfreeman.email>
Fri, 9 Dec 2022 12:49:26 +0000 (12:49 +0000)
committerJoão Távora <joaotavora@gmail.com>
Fri, 9 Dec 2022 13:03:57 +0000 (13:03 +0000)
Co-authored-by: João Távora <joaotavora@gmail.com>
* lisp/progmodes/eglot.el (eglot-report-progress): New custom variable.
(eglot-lsp-server): New slot for tracking active progress reporters.
(eglot-handle-notification (eql $/progress)): New method.

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

* doc/misc/eglot.texi (Customizing Eglot): Describe new variable.

doc/misc/eglot.texi
lisp/progmodes/eglot.el

index b76f8bdfd7116eef366d226bdaacda52a53db034..2aff038b9ab0d6e31e7fac9f2155e0d932a9bc49 100644 (file)
@@ -955,6 +955,13 @@ Note that you can still configure the excluded Emacs features manually
 to use Eglot in your @code{eglot-managed-mode-hook} or via some other
 mechanism.
 
+@vindex eglot-report-progress
+@cindex progress
+@item eglot-report-progress
+Set this variable to true if you'd like progress notifications coming
+from the LSP server to be handled as Emacs's progress reporting
+facilities.
+
 @vindex eglot-workspace-configuration
 @cindex server workspace configuration
 @item eglot-workspace-configuration
index 06541d47d82f1e04dbc3dbd208e5bf214dd4277a..a53f62fc565bbc849655ea609d94b5c468e66b9a 100644 (file)
@@ -387,6 +387,11 @@ done by `eglot-reconnect'."
   "String displayed in mode line when Eglot is active."
   :type 'string)
 
+(defcustom eglot-report-progress t
+  "If non-nil, show progress of long running LSP server work"
+  :type 'boolean
+  :version "29.1")
+
 (defvar eglot-withhold-process-id nil
   "If non-nil, Eglot will not send the Emacs process id to the language server.
 This can be useful when using docker to run a language server.")
@@ -471,6 +476,7 @@ This can be useful when using docker to run a language server.")
       (TextDocumentEdit (:textDocument :edits) ())
       (TextEdit (:range :newText))
       (VersionedTextDocumentIdentifier (:uri :version) ())
+      (WorkDoneProgress (:kind) (:title :message :percentage :cancellable))
       (WorkspaceEdit () (:changes :documentChanges))
       (WorkspaceSymbol (:name :kind) (:containerName :location :data)))
     "Alist (INTERFACE-NAME . INTERFACE) of known external LSP interfaces.
@@ -832,6 +838,9 @@ treated as in `eglot--dbind'."
    (project
     :documentation "Project associated with server."
     :accessor eglot--project)
+   (progress-reporters
+    :initform (make-hash-table :test #'equal) :accessor eglot--progress-reporters
+    :documentation "Maps LSP progress tokens to progress reporters.")
    (inhibit-autoreconnect
     :initform t
     :documentation "Generalized boolean inhibiting auto-reconnection if true."
@@ -2050,6 +2059,27 @@ COMMAND is a symbol naming the command."
   (_server (_method (eql telemetry/event)) &rest _any)
   "Handle notification telemetry/event.") ;; noop, use events buffer
 
+(cl-defmethod eglot-handle-notification
+  (server (_method (eql $/progress)) &key token value)
+  "Handle $/progress notification identified by TOKEN from SERVER."
+  (when eglot-report-progress
+    (cl-flet ((fmt (&rest args) (mapconcat #'identity args " ")))
+      (eglot--dbind ((WorkDoneProgress) kind title percentage message) value
+        (pcase kind
+          ("begin"
+           (let* ((prefix (format (concat "[eglot] %s %s:" (when percentage " "))
+                                  (eglot-project-nickname server) token))
+                  (pr (puthash token
+                       (if percentage
+                           (make-progress-reporter prefix 0 100 percentage 1 0)
+                         (make-progress-reporter prefix nil nil nil 1 0))
+                       (eglot--progress-reporters server))))
+             (progress-reporter-update pr percentage (fmt title message))))
+          ("report"
+           (when-let ((pr (gethash token (eglot--progress-reporters server))))
+             (progress-reporter-update pr percentage (fmt title message))))
+          ("end" (remhash token (eglot--progress-reporters server))))))))
+
 (cl-defmethod eglot-handle-notification
   (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
            &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode'