aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2025-02-16 18:27:48 +0000
committerJoão Távora2025-02-24 23:38:15 +0000
commite4c911adeaa679a92fab58b196b27c502aaed2f3 (patch)
tree34714f0b29c309f3efb8d3b699c0ff5da25d7f19
parent0ac0f355e50639fb2cdacb6cbfca696d5592a89e (diff)
downloademacs-e4c911adeaa679a92fab58b196b27c502aaed2f3.tar.gz
emacs-e4c911adeaa679a92fab58b196b27c502aaed2f3.zip
Eglot: use eglot-advertise-cancellation in more situations
The async requests frequently issued by ElDoc are a significant source of request pile-up on the server side (for some servers). With this change, Eglot will issue additional LSP $/cancelRequest notifications for in-flight requests of certain kinds in the pre-command hook. This required a small change to the 'jsonrpc-async-request' entrypoint. This feature is experimental. * lisp/jsonrpc.el (jsonrpc-async-request): No longer returns nil. * lisp/progmodes/eglot.el (eglot--inflight-async-requests): New variable. (eglot--cancel-inflight-async-requests): New function. (eglot--async-request): New function. (eglot--pre-command-hook): Call eglot--cancel-inflight-async-requests. (eglot-signature-eldoc-function, eglot-hover-eldoc-function) (eglot-highlight-eldoc-function, eglot-code-action-suggestion): Use eglot--async-request.
-rw-r--r--lisp/jsonrpc.el6
-rw-r--r--lisp/progmodes/eglot.el75
2 files changed, 69 insertions, 12 deletions
diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
index be9f4917e80..c5a099af8ec 100644
--- a/lisp/jsonrpc.el
+++ b/lisp/jsonrpc.el
@@ -377,9 +377,9 @@ never be sent at all, in case it is overridden in the meantime by
377a new request with identical DEFERRED and for the same buffer. 377a new request with identical DEFERRED and for the same buffer.
378However, in that situation, the original timeout is kept. 378However, in that situation, the original timeout is kept.
379 379
380Returns nil." 380Returns a list whose first element is an integer identifying the request
381 (apply #'jsonrpc--async-request-1 connection method params args) 381as specified in the JSONRPC 2.0 spec."
382 nil) 382 (apply #'jsonrpc--async-request-1 connection method params args))
383 383
384(cl-defun jsonrpc-request (connection 384(cl-defun jsonrpc-request (connection
385 method params &key 385 method params &key
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index df93f899069..f0891e700a8 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -1832,6 +1832,62 @@ Unless IMMEDIATE, send pending changes before making request."
1832 (cancel-on-input)) 1832 (cancel-on-input))
1833 :cancel-on-input-retval cancel-on-input-retval)) 1833 :cancel-on-input-retval cancel-on-input-retval))
1834 1834
1835(defvar-local eglot--inflight-async-requests nil
1836 "An plist of symbols to lists of JSONRPC ids.
1837The ids designate in-flight asynchronous requests that may be cancelled
1838according to `eglot-advertise-cancellation'.")
1839
1840(cl-defun eglot--cancel-inflight-async-requests
1841 (&optional (hints '(:textDocument/signatureHelp
1842 :textDocument/hover
1843 :textDocument/documentHighlight
1844 :textDocument/codeAction)))
1845 (when-let* ((server (and hints
1846 eglot-advertise-cancellation
1847 (eglot-current-server))))
1848 (dolist (hint hints)
1849 (dolist (id (plist-get eglot--inflight-async-requests hint))
1850 ;; FIXME: in theory, as `jsonrpc-async-request' explains, this
1851 ;; request may never have been sent at all. But that's rare, and
1852 ;; it's only a problem if the server borks on cancellation of
1853 ;; never-sent requests.
1854 (jsonrpc-notify server '$/cancelRequest `(:id ,id)))
1855 (cl-remf eglot--inflight-async-requests hint))))
1856
1857(cl-defun eglot--async-request (server
1858 method
1859 params
1860 &key
1861 (success-fn nil success-fn-supplied-p)
1862 (error-fn nil error-fn-supplied-p)
1863 (timeout-fn nil timeout-fn-supplied-p)
1864 (timeout nil timeout-supplied-p)
1865 hint
1866 &aux moreargs)
1867 "Like `jsonrpc-async-request', but for Eglot LSP requests.
1868HINT argument is a symbol passed as DEFERRED to `jsonrpc-async-request'
1869and also used as a hint of the request cancellation mechanism (see
1870`eglot-advertise-cancellation')."
1871 (cl-labels ((clearing-fn (fn)
1872 (lambda (&rest args)
1873 (when fn (apply fn args))
1874 (cl-remf eglot--inflight-async-requests hint))))
1875 (eglot--cancel-inflight-async-requests (list hint))
1876 (when timeout-supplied-p
1877 (setq moreargs (nconc `(:timeout ,timeout) moreargs)))
1878 (when hint
1879 (setq moreargs (nconc `(:deferred ,hint) moreargs)))
1880 (let ((id
1881 (car (apply #'jsonrpc-async-request
1882 server method params
1883 :success-fn (clearing-fn success-fn)
1884 :error-fn (clearing-fn error-fn)
1885 :timeout-fn (clearing-fn timeout-fn)
1886 moreargs))))
1887 (when (and hint eglot-advertise-cancellation)
1888 (push id
1889 (plist-get eglot--inflight-async-requests hint))))))
1890
1835 1891
1836;;; Encoding fever 1892;;; Encoding fever
1837;;; 1893;;;
@@ -2799,8 +2855,9 @@ buffer."
2799 "Cache of `workspace/Symbol' results used by `xref-find-definitions'.") 2855 "Cache of `workspace/Symbol' results used by `xref-find-definitions'.")
2800 2856
2801(defun eglot--pre-command-hook () 2857(defun eglot--pre-command-hook ()
2802 "Reset some temporary variables." 2858 "Reset some state."
2803 (clrhash eglot--workspace-symbols-cache) 2859 (clrhash eglot--workspace-symbols-cache)
2860 (eglot--cancel-inflight-async-requests)
2804 (setq eglot--last-inserted-char nil)) 2861 (setq eglot--last-inserted-char nil))
2805 2862
2806(defun eglot--CompletionParams () 2863(defun eglot--CompletionParams ()
@@ -3644,7 +3701,7 @@ for which LSP on-type-formatting should be requested."
3644 "A member of `eldoc-documentation-functions', for signatures." 3701 "A member of `eldoc-documentation-functions', for signatures."
3645 (when (eglot-server-capable :signatureHelpProvider) 3702 (when (eglot-server-capable :signatureHelpProvider)
3646 (let ((buf (current-buffer))) 3703 (let ((buf (current-buffer)))
3647 (jsonrpc-async-request 3704 (eglot--async-request
3648 (eglot--current-server-or-lose) 3705 (eglot--current-server-or-lose)
3649 :textDocument/signatureHelp (eglot--TextDocumentPositionParams) 3706 :textDocument/signatureHelp (eglot--TextDocumentPositionParams)
3650 :success-fn 3707 :success-fn
@@ -3661,14 +3718,14 @@ for which LSP on-type-formatting should be requested."
3661 nil)) 3718 nil))
3662 signatures "\n") 3719 signatures "\n")
3663 :echo (eglot--sig-info active-sig activeParameter t)))))) 3720 :echo (eglot--sig-info active-sig activeParameter t))))))
3664 :deferred :textDocument/signatureHelp)) 3721 :hint :textDocument/signatureHelp))
3665 t)) 3722 t))
3666 3723
3667(defun eglot-hover-eldoc-function (cb &rest _ignored) 3724(defun eglot-hover-eldoc-function (cb &rest _ignored)
3668 "A member of `eldoc-documentation-functions', for hover." 3725 "A member of `eldoc-documentation-functions', for hover."
3669 (when (eglot-server-capable :hoverProvider) 3726 (when (eglot-server-capable :hoverProvider)
3670 (let ((buf (current-buffer))) 3727 (let ((buf (current-buffer)))
3671 (jsonrpc-async-request 3728 (eglot--async-request
3672 (eglot--current-server-or-lose) 3729 (eglot--current-server-or-lose)
3673 :textDocument/hover (eglot--TextDocumentPositionParams) 3730 :textDocument/hover (eglot--TextDocumentPositionParams)
3674 :success-fn (eglot--lambda ((Hover) contents range) 3731 :success-fn (eglot--lambda ((Hover) contents range)
@@ -3677,7 +3734,7 @@ for which LSP on-type-formatting should be requested."
3677 (eglot--hover-info contents range)))) 3734 (eglot--hover-info contents range))))
3678 (funcall cb info 3735 (funcall cb info
3679 :echo (and info (string-match "\n" info)))))) 3736 :echo (and info (string-match "\n" info))))))
3680 :deferred :textDocument/hover)) 3737 :hint :textDocument/hover))
3681 t)) 3738 t))
3682 3739
3683(defun eglot-highlight-eldoc-function (_cb &rest _ignored) 3740(defun eglot-highlight-eldoc-function (_cb &rest _ignored)
@@ -3687,7 +3744,7 @@ for which LSP on-type-formatting should be requested."
3687 ;; ignore cb and return nil to say "no doc". 3744 ;; ignore cb and return nil to say "no doc".
3688 (when (eglot-server-capable :documentHighlightProvider) 3745 (when (eglot-server-capable :documentHighlightProvider)
3689 (let ((buf (current-buffer))) 3746 (let ((buf (current-buffer)))
3690 (jsonrpc-async-request 3747 (eglot--async-request
3691 (eglot--current-server-or-lose) 3748 (eglot--current-server-or-lose)
3692 :textDocument/documentHighlight (eglot--TextDocumentPositionParams) 3749 :textDocument/documentHighlight (eglot--TextDocumentPositionParams)
3693 :success-fn 3750 :success-fn
@@ -3705,7 +3762,7 @@ for which LSP on-type-formatting should be requested."
3705 `(,(lambda (o &rest _) (delete-overlay o)))) 3762 `(,(lambda (o &rest _) (delete-overlay o))))
3706 ov))) 3763 ov)))
3707 highlights)))) 3764 highlights))))
3708 :deferred :textDocument/documentHighlight) 3765 :hint :textDocument/documentHighlight)
3709 nil))) 3766 nil)))
3710 3767
3711(defun eglot--imenu-SymbolInformation (res) 3768(defun eglot--imenu-SymbolInformation (res)
@@ -4031,7 +4088,7 @@ at point. With prefix argument, prompt for ACTION-KIND."
4031 (bounds (eglot--code-action-bounds)) 4088 (bounds (eglot--code-action-bounds))
4032 (use-text-p (memq 'eldoc-hint eglot-code-action-indications)) 4089 (use-text-p (memq 'eldoc-hint eglot-code-action-indications))
4033 tooltip blurb) 4090 tooltip blurb)
4034 (jsonrpc-async-request 4091 (eglot--async-request
4035 (eglot--current-server-or-lose) 4092 (eglot--current-server-or-lose)
4036 :textDocument/codeAction 4093 :textDocument/codeAction
4037 (eglot--code-action-params :beg (car bounds) :end (cadr bounds) 4094 (eglot--code-action-params :beg (car bounds) :end (cadr bounds)
@@ -4071,7 +4128,7 @@ at point. With prefix argument, prompt for ACTION-KIND."
4071 ,tooltip))))) 4128 ,tooltip)))))
4072 (setq eglot--suggestion-overlay ov))))) 4129 (setq eglot--suggestion-overlay ov)))))
4073 (when use-text-p (funcall cb blurb))) 4130 (when use-text-p (funcall cb blurb)))
4074 :deferred :textDocument/codeAction) 4131 :hint :textDocument/codeAction)
4075 (and use-text-p t)))) 4132 (and use-text-p t))))
4076 4133
4077 4134