From 74a009dd96a643b12a63a29fba3d40a74e7d82a2 Mon Sep 17 00:00:00 2001 From: dannyfreeman Date: Fri, 9 Dec 2022 12:49:26 +0000 Subject: [PATCH] Eglot: Handle LSP progress with Emacs progress reporters (bug#59149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Távora * 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 | 7 +++++++ lisp/progmodes/eglot.el | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi index b76f8bdfd71..2aff038b9ab 100644 --- a/doc/misc/eglot.texi +++ b/doc/misc/eglot.texi @@ -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 diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 06541d47d82..a53f62fc565 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -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'