From a4d7c5028d49cd5fd6d5599e91d2bcfb250851dc Mon Sep 17 00:00:00 2001 From: dannyfreeman Date: Wed, 9 Nov 2022 08:46:45 -0500 Subject: [PATCH 2/2] Eglot: Display progress notifications in minibuffer as they arrive * lisp/progmodes/eglot.el (eglot-report-progress): New custom variable. (eglot-lsp-server): New slot for tracking active progress reporters. (eglot-handle-notification): New impl for $/progress server responses. 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 | 50 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index e6e97fd8c8..e6673c37a1 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -386,6 +386,11 @@ eglot-menu-string "String displayed in mode line when Eglot is active." :type 'string) +(defcustom eglot-report-progress t + "If non-nil, show progress of long running server work in the minibuffer." + :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.") @@ -470,6 +475,7 @@ eglot--executable-find (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. @@ -831,6 +837,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." @@ -2049,6 +2058,47 @@ eglot-handle-notification (_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 a $/progress notification identified by TOKEN from the SERVER." + (when eglot-report-progress + (cl-flet ((eglot--pr-message (title message) + (cond + ((and title message) (format "%s %s" title message)) + (title title) + (message message))) + (eglot--get-pr (server token) + (cdr (assoc token (eglot--progress-reporter-alist server)))) + (eglot--delete-pr (server token) + (setf (eglot--progress-reporter-alist server) + (assoc-delete-all + token (eglot--progress-reporter-alist server))))) + (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 (if percentage + (make-progress-reporter + prefix 0 100 percentage 1 0) + (make-progress-reporter + prefix nil nil nil 1 0)))) + (eglot--delete-pr server token) + (setf (eglot--progress-reporter-alist server) + (cons (cons token pr) (eglot--progress-reporter-alist server))) + (progress-reporter-update + pr percentage (eglot--pr-message title message)))) + ("report" + (when-let ((pr (eglot--get-pr server token))) + (progress-reporter-update + pr percentage (eglot--pr-message title message)))) + ("end" + (when-let ((pr (eglot--get-pr server token))) + (progress-reporter-done pr) + (eglot--delete-pr 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