From b87bc1152138a7ce798d484b37ebcae17da34a24 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Tue, 14 Jan 2025 15:58:57 +0000 Subject: [PATCH] Eglot: introduce eglot-advertise-cancellation Setting this variable to true causes Eglot to send special cancellation notification for certain stale client request. This may help some LSP servers avoid doing costly but ultimately useless work on behalf of the client, improving overall performance. Request cancellation is described in https://microsoft.github.io/language-server-protocol/ specifications/lsp/3.17/specification/#cancelRequest * lisp/jsonrpc.el (jsonrpc-request): Accept function as value for CANCEL-ON-INPUT. * lisp/progmodes/eglot.el (eglot--request): Rework. * doc/misc/eglot.texi (Customizing Eglot): Mention eglot-advertise-cancellation. (cherry picked from commit 7f0ef9655cdc28c3b8055e32c9e84ea57339b139) --- doc/misc/eglot.texi | 7 +++++++ etc/EGLOT-NEWS | 6 ++++++ lisp/jsonrpc.el | 12 ++++++++---- lisp/progmodes/eglot.el | 15 ++++++++++++++- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi index df24cf2a1dc..47649455cec 100644 --- a/doc/misc/eglot.texi +++ b/doc/misc/eglot.texi @@ -919,6 +919,13 @@ Set this variable to non-nil if you'd like progress notifications coming from the language server to be handled by Emacs's progress reporting facilities. If the value is the symbol @code{messages} the message buffer is used, else the progress is reported in the mode line. + +@cindex request cancellation +@item eglot-advertise-cancellation +Setting this variable to true causes Eglot to send special cancellation +notification for certain stale client request. This may help some LSP +servers avoid doing costly but ultimately useless work on behalf of the +client, improving overall performance. @end vtable @node Other Variables diff --git a/etc/EGLOT-NEWS b/etc/EGLOT-NEWS index f4621994b67..9deac73d9fc 100644 --- a/etc/EGLOT-NEWS +++ b/etc/EGLOT-NEWS @@ -20,6 +20,12 @@ https://github.com/joaotavora/eglot/issues/1234. * Changes in upcoming Eglot +** New 'eglot-advertise-cancellation' variable + +Tweaking this variable may help some LSP servers avoid doing costly but +ultimately useless work on behalf of the client, improving overall +performance. + * Changes in Eglot 1.18 (20/1/2025) diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el index df612e63d05..c1743c13611 100644 --- a/lisp/jsonrpc.el +++ b/lisp/jsonrpc.el @@ -399,10 +399,12 @@ error of type `jsonrpc-error'. DEFERRED and TIMEOUT as in `jsonrpc-async-request', which see. -If CANCEL-ON-INPUT is non-nil and the user inputs something while -the function is waiting, then it exits immediately, returning -CANCEL-ON-INPUT-RETVAL. Any future replies (normal or error) are -ignored." +If CANCEL-ON-INPUT is non-nil and the user inputs something while the +function is waiting, then any future replies to the request by the +remote endpoint (normal or error) are ignored and the function exits +returning CANCEL-ON-INPUT-RETVAL. If CANCEL-ON-INPUT is a function, it +is invoked with one argument, an integer identifying the cancelled +request as specified in the JSONRPC 2.0 spec." (let* ((tag (cl-gensym "jsonrpc-request-catch-tag")) id-and-timer canceled (throw-on-input nil) @@ -435,6 +437,8 @@ ignored." (unwind-protect (let ((inhibit-quit t)) (while (sit-for 30))) (setq canceled t)) + (when (functionp cancel-on-input) + (funcall cancel-on-input (car id-and-timer))) `(canceled ,cancel-on-input-retval)) (t (while t (accept-process-output nil 30))))) ;; In normal operation, continuations for error/success is diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 166b3936102..b87a45e9912 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -569,6 +569,14 @@ under cursor." (const :tag "Execute custom commands" :executeCommandProvider) (const :tag "Inlay hints" :inlayHintProvider))) +(defcustom eglot-advertise-cancellation nil + "If non-nil, Eglot attemps to inform server of cancelled requests. +This is done by sending an additional '$/cancelRequest' notification +every time Eglot decides to forget a request. The effect of this +notification is implementation defined, and is only useful for some +servers." + :type 'boolean) + (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.") @@ -1747,7 +1755,12 @@ Unless IMMEDIATE, send pending changes before making request." (unless immediate (eglot--signal-textDocument/didChange)) (jsonrpc-request server method params :timeout timeout - :cancel-on-input cancel-on-input + :cancel-on-input + (cond ((and cancel-on-input + eglot-advertise-cancellation) + (lambda (id) + (jsonrpc-notify server '$/cancelRequest `(:id ,id)))) + (cancel-on-input)) :cancel-on-input-retval cancel-on-input-retval)) -- 2.39.5