diff options
| author | João Távora | 2025-01-26 23:26:51 +0000 |
|---|---|---|
| committer | João Távora | 2025-01-28 11:04:21 +0000 |
| commit | d6a502fc7a69dfa11aa100da5966a6962a82f613 (patch) | |
| tree | b3b50646ad931a0fe9d04fb3b3922a71fd8b0e24 | |
| parent | 7f0ef9655cdc28c3b8055e32c9e84ea57339b139 (diff) | |
| download | emacs-d6a502fc7a69dfa11aa100da5966a6962a82f613.tar.gz emacs-d6a502fc7a69dfa11aa100da5966a6962a82f613.zip | |
Eglot: suggest code actions at point
* lisp/progmodes/eglot.el (eglot-code-action-indicator-face): New face.
(eglot-code-action-indications, eglot-code-action-indicator): New defcustoms.
(eglot--highlights): Move up here.
(eglot--managed-mode): Rework.
(eglot--server-menu-map, eglot--main-menu-map): Extract maps into
variables (avoids odd mode-line bug).
(eglot--mode-line-format): Rework.
(eglot--code-action-params): New helper.
(eglot-code-actions): Rework.
(eglot--read-execute-code-action): Tweak.
(eglot-code-action-suggestion): New function.
* etc/EGLOT-NEWS: Mention new feature.
* doc/misc/eglot.texi (Eglot Features): Mention new feature.
(Customization Variables): Mention new variables.
| -rw-r--r-- | doc/misc/eglot.texi | 34 | ||||
| -rw-r--r-- | etc/EGLOT-NEWS | 8 | ||||
| -rw-r--r-- | lisp/progmodes/eglot.el | 193 |
3 files changed, 200 insertions, 35 deletions
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi index 47649455cec..93675210c59 100644 --- a/doc/misc/eglot.texi +++ b/doc/misc/eglot.texi | |||
| @@ -439,6 +439,13 @@ command (@pxref{Symbol Completion,,, emacs, GNU Emacs Manual}). This | |||
| 439 | uses the language-server's parser data for the completion candidates. | 439 | uses the language-server's parser data for the completion candidates. |
| 440 | 440 | ||
| 441 | @item | 441 | @item |
| 442 | Server-suggested code refactorings. The ElDoc package is also leveraged | ||
| 443 | to retrieve so-called @dfn{code actions} nearby point. When such | ||
| 444 | suggestions are available they are annotated with a special indication | ||
| 445 | and can be easily invoked by the user with the @code{eglot-code-action} | ||
| 446 | command (@pxref{Eglot Commands}). | ||
| 447 | |||
| 448 | @item | ||
| 442 | On-the-fly succinct informative annotations, so-called @dfn{inlay | 449 | On-the-fly succinct informative annotations, so-called @dfn{inlay |
| 443 | hints}. Eglot adds special intangible text nearby certain identifiers, | 450 | hints}. Eglot adds special intangible text nearby certain identifiers, |
| 444 | be it the type of a variable, or the name of a formal parameter in a | 451 | be it the type of a variable, or the name of a formal parameter in a |
| @@ -926,6 +933,31 @@ Setting this variable to true causes Eglot to send special cancellation | |||
| 926 | notification for certain stale client request. This may help some LSP | 933 | notification for certain stale client request. This may help some LSP |
| 927 | servers avoid doing costly but ultimately useless work on behalf of the | 934 | servers avoid doing costly but ultimately useless work on behalf of the |
| 928 | client, improving overall performance. | 935 | client, improving overall performance. |
| 936 | |||
| 937 | @item eglot-code-action-indications | ||
| 938 | This variable controls the indication of code actions available at | ||
| 939 | point. Value is a list of symbols, more than one can be specified: | ||
| 940 | |||
| 941 | @itemize @minus | ||
| 942 | @item | ||
| 943 | @code{eldoc-hint}: ElDoc is used to hint about at-point actions. | ||
| 944 | @item | ||
| 945 | @code{margin}: A special indicator appears in the margin of the line | ||
| 946 | that point is currently on. This indicator is not interactive (you | ||
| 947 | cannot click on it with the mouse). | ||
| 948 | @item | ||
| 949 | @code{nearby}: An interactive special indicator appears near point. | ||
| 950 | @item | ||
| 951 | @code{mode-line}: An interactive special indicator appears in the mode | ||
| 952 | line. | ||
| 953 | @end itemize | ||
| 954 | |||
| 955 | @code{margin} and @code{nearby} are incompatible. If the list is empty, | ||
| 956 | ElDoc will not hint about at-point actions. | ||
| 957 | |||
| 958 | @item eglot-code-action-indicator | ||
| 959 | This variable is a string determining what the special indicator looks | ||
| 960 | like. | ||
| 929 | @end vtable | 961 | @end vtable |
| 930 | 962 | ||
| 931 | @node Other Variables | 963 | @node Other Variables |
| @@ -1004,6 +1036,8 @@ about an identifier. | |||
| 1004 | signature information. | 1036 | signature information. |
| 1005 | @item @code{eglot-highlight-eldoc-function}, to highlight nearby | 1037 | @item @code{eglot-highlight-eldoc-function}, to highlight nearby |
| 1006 | manifestations of an identifier. | 1038 | manifestations of an identifier. |
| 1039 | @item @code{eglot-code-action-suggestion}, to retrieve relevant code | ||
| 1040 | actions at point. | ||
| 1007 | @end itemize | 1041 | @end itemize |
| 1008 | 1042 | ||
| 1009 | A simple tweak to remove at-point identifier information for | 1043 | A simple tweak to remove at-point identifier information for |
diff --git a/etc/EGLOT-NEWS b/etc/EGLOT-NEWS index 9deac73d9fc..18a4e9e2d9e 100644 --- a/etc/EGLOT-NEWS +++ b/etc/EGLOT-NEWS | |||
| @@ -26,6 +26,14 @@ Tweaking this variable may help some LSP servers avoid doing costly but | |||
| 26 | ultimately useless work on behalf of the client, improving overall | 26 | ultimately useless work on behalf of the client, improving overall |
| 27 | performance. | 27 | performance. |
| 28 | 28 | ||
| 29 | ** Suggests code actions at point | ||
| 30 | |||
| 31 | A commonly requested feature, Eglot will use ElDoc to ask the server for | ||
| 32 | code actions available at point, indicating to the user, who may use | ||
| 33 | execute them quickly via the usual 'eglot-code-actions' command. | ||
| 34 | Customize with 'eglot-code-action-indications' and | ||
| 35 | 'eglot-code-action-indicator'. | ||
| 36 | |||
| 29 | 37 | ||
| 30 | * Changes in Eglot 1.18 (20/1/2025) | 38 | * Changes in Eglot 1.18 (20/1/2025) |
| 31 | 39 | ||
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index f2fcbc0a4c2..b78c634012f 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el | |||
| @@ -579,6 +579,45 @@ notification is implementation defined, and is only useful for some | |||
| 579 | servers." | 579 | servers." |
| 580 | :type 'boolean) | 580 | :type 'boolean) |
| 581 | 581 | ||
| 582 | (defface eglot-code-action-indicator-face | ||
| 583 | '((t (:inherit font-lock-escape-face :weight bold))) | ||
| 584 | "Face used for code action suggestions.") | ||
| 585 | |||
| 586 | (defcustom eglot-code-action-indications | ||
| 587 | '(eldoc-hint mode-line margin) | ||
| 588 | "How Eglot indicates there's are code actions available at point. | ||
| 589 | Value is a list of symbols, more than one can be specified: | ||
| 590 | |||
| 591 | - `eldoc-hint': ElDoc is used to hint about at-point actions. | ||
| 592 | - `margin': A special indicator appears in the margin. | ||
| 593 | - `nearby': A special indicator appears near point. | ||
| 594 | - `mode-line': A special indicator appears in the mode-line. | ||
| 595 | |||
| 596 | `margin' and `nearby' are incompatible. `margin's indicator is not | ||
| 597 | interactive. If the list is empty, Eglot will not hint about code | ||
| 598 | actions at point." | ||
| 599 | :type '(set | ||
| 600 | :tag "Tick the ones you're interested in" | ||
| 601 | (const :tag "ElDoc textual hint" eldoc-hint) | ||
| 602 | (const :tag "Right besides point" nearby) | ||
| 603 | (const :tag "In mode line" mode-line) | ||
| 604 | (const :tag "In margin" margin)) | ||
| 605 | :package-version '(Eglot . "1.19")) | ||
| 606 | |||
| 607 | (defcustom eglot-code-action-indicator | ||
| 608 | (cl-loop for c in '(? ?⚡?✓ ?α ??) | ||
| 609 | when (char-displayable-p c) | ||
| 610 | return (make-string 1 c)) | ||
| 611 | "Indicator string for code action suggestions." | ||
| 612 | :type (let ((basic-choices | ||
| 613 | (cl-loop for c in '(? ?⚡?✓ ?α ??) | ||
| 614 | when (char-displayable-p c) | ||
| 615 | collect `(const :tag ,(format "Use `%c'" c) | ||
| 616 | ,(make-string 1 c))))) | ||
| 617 | `(choice ,@basic-choices | ||
| 618 | (string :tag "Specify your own"))) | ||
| 619 | :package-version '(Eglot . "1.19")) | ||
| 620 | |||
| 582 | (defvar eglot-withhold-process-id nil | 621 | (defvar eglot-withhold-process-id nil |
| 583 | "If non-nil, Eglot will not send the Emacs process id to the language server. | 622 | "If non-nil, Eglot will not send the Emacs process id to the language server. |
| 584 | This can be useful when using docker to run a language server.") | 623 | This can be useful when using docker to run a language server.") |
| @@ -2015,6 +2054,11 @@ For example, to keep your Company customization, add the symbol | |||
| 2015 | "A hook run by Eglot after it started/stopped managing a buffer. | 2054 | "A hook run by Eglot after it started/stopped managing a buffer. |
| 2016 | Use `eglot-managed-p' to determine if current buffer is managed.") | 2055 | Use `eglot-managed-p' to determine if current buffer is managed.") |
| 2017 | 2056 | ||
| 2057 | (defvar eglot--highlights nil "Overlays for `eglot-highlight-eldoc-function'.") | ||
| 2058 | |||
| 2059 | (defvar-local eglot--suggestion-overlay (make-overlay 0 0) | ||
| 2060 | "Overlay for `eglot-code-action-suggestion'.") | ||
| 2061 | |||
| 2018 | (define-minor-mode eglot--managed-mode | 2062 | (define-minor-mode eglot--managed-mode |
| 2019 | "Mode for source buffers managed by some Eglot project." | 2063 | "Mode for source buffers managed by some Eglot project." |
| 2020 | :init-value nil :lighter nil :keymap eglot-mode-map :interactive nil | 2064 | :init-value nil :lighter nil :keymap eglot-mode-map :interactive nil |
| @@ -2056,15 +2100,16 @@ Use `eglot-managed-p' to determine if current buffer is managed.") | |||
| 2056 | #'eglot-imenu)) | 2100 | #'eglot-imenu)) |
| 2057 | (unless (eglot--stay-out-of-p 'flymake) (flymake-mode 1)) | 2101 | (unless (eglot--stay-out-of-p 'flymake) (flymake-mode 1)) |
| 2058 | (unless (eglot--stay-out-of-p 'eldoc) | 2102 | (unless (eglot--stay-out-of-p 'eldoc) |
| 2059 | (add-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function | 2103 | (dolist (f (list #'eglot-signature-eldoc-function |
| 2060 | nil t) | 2104 | #'eglot-hover-eldoc-function |
| 2061 | (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function | 2105 | #'eglot-highlight-eldoc-function |
| 2062 | nil t) | 2106 | #'eglot-code-action-suggestion)) |
| 2063 | (add-hook 'eldoc-documentation-functions #'eglot-highlight-eldoc-function | 2107 | (add-hook 'eldoc-documentation-functions f t t)) |
| 2064 | nil t) | ||
| 2065 | (eldoc-mode 1)) | 2108 | (eldoc-mode 1)) |
| 2066 | (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) | 2109 | (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) |
| 2067 | (t | 2110 | (t |
| 2111 | (mapc #'delete-overlay eglot--highlights) | ||
| 2112 | (delete-overlay eglot--suggestion-overlay) | ||
| 2068 | (remove-hook 'after-change-functions #'eglot--after-change t) | 2113 | (remove-hook 'after-change-functions #'eglot--after-change t) |
| 2069 | (remove-hook 'before-change-functions #'eglot--before-change t) | 2114 | (remove-hook 'before-change-functions #'eglot--before-change t) |
| 2070 | (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t) | 2115 | (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t) |
| @@ -2080,9 +2125,11 @@ Use `eglot-managed-p' to determine if current buffer is managed.") | |||
| 2080 | (remove-hook 'change-major-mode-hook #'eglot--managed-mode-off t) | 2125 | (remove-hook 'change-major-mode-hook #'eglot--managed-mode-off t) |
| 2081 | (remove-hook 'post-self-insert-hook #'eglot--post-self-insert-hook t) | 2126 | (remove-hook 'post-self-insert-hook #'eglot--post-self-insert-hook t) |
| 2082 | (remove-hook 'pre-command-hook #'eglot--pre-command-hook t) | 2127 | (remove-hook 'pre-command-hook #'eglot--pre-command-hook t) |
| 2083 | (remove-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function t) | 2128 | (dolist (f (list #'eglot-hover-eldoc-function |
| 2084 | (remove-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function t) | 2129 | #'eglot-signature-eldoc-function |
| 2085 | (remove-hook 'eldoc-documentation-functions #'eglot-highlight-eldoc-function t) | 2130 | #'eglot-highlight-eldoc-function |
| 2131 | #'eglot-code-action-suggestion)) | ||
| 2132 | (remove-hook 'eldoc-documentation-functions f t)) | ||
| 2086 | (cl-loop for (var . saved-binding) in eglot--saved-bindings | 2133 | (cl-loop for (var . saved-binding) in eglot--saved-bindings |
| 2087 | do (set (make-local-variable var) saved-binding)) | 2134 | do (set (make-local-variable var) saved-binding)) |
| 2088 | (remove-function (local 'imenu-create-index-function) #'eglot-imenu) | 2135 | (remove-function (local 'imenu-create-index-function) #'eglot-imenu) |
| @@ -2265,6 +2312,16 @@ Uses THING, FACE, DEFS and PREPEND." | |||
| 2265 | keymap ,map help-echo ,(concat prepend blurb) | 2312 | keymap ,map help-echo ,(concat prepend blurb) |
| 2266 | mouse-face mode-line-highlight)))) | 2313 | mouse-face mode-line-highlight)))) |
| 2267 | 2314 | ||
| 2315 | (defconst eglot--main-menu-map | ||
| 2316 | (let ((map (make-sparse-keymap))) | ||
| 2317 | (define-key map [mode-line down-mouse-1] eglot-menu) | ||
| 2318 | map)) | ||
| 2319 | |||
| 2320 | (defconst eglot--server-menu-map | ||
| 2321 | (let ((map (make-sparse-keymap))) | ||
| 2322 | (define-key map [mode-line down-mouse-1] eglot-server-menu) | ||
| 2323 | map)) | ||
| 2324 | |||
| 2268 | (defun eglot--mode-line-format () | 2325 | (defun eglot--mode-line-format () |
| 2269 | "Compose Eglot's mode-line." | 2326 | "Compose Eglot's mode-line." |
| 2270 | (let* ((server (eglot-current-server)) | 2327 | (let* ((server (eglot-current-server)) |
| @@ -2277,9 +2334,7 @@ Uses THING, FACE, DEFS and PREPEND." | |||
| 2277 | 'face 'eglot-mode-line | 2334 | 'face 'eglot-mode-line |
| 2278 | 'mouse-face 'mode-line-highlight | 2335 | 'mouse-face 'mode-line-highlight |
| 2279 | 'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu" | 2336 | 'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu" |
| 2280 | 'keymap (let ((map (make-sparse-keymap))) | 2337 | 'keymap eglot--main-menu-map)) |
| 2281 | (define-key map [mode-line down-mouse-1] eglot-menu) | ||
| 2282 | map))) | ||
| 2283 | (when nick | 2338 | (when nick |
| 2284 | `(":" | 2339 | `(":" |
| 2285 | ,(propertize | 2340 | ,(propertize |
| @@ -2287,9 +2342,7 @@ Uses THING, FACE, DEFS and PREPEND." | |||
| 2287 | 'face 'eglot-mode-line | 2342 | 'face 'eglot-mode-line |
| 2288 | 'mouse-face 'mode-line-highlight | 2343 | 'mouse-face 'mode-line-highlight |
| 2289 | 'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" nick) | 2344 | 'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" nick) |
| 2290 | 'keymap (let ((map (make-sparse-keymap))) | 2345 | 'keymap eglot--server-menu-map) |
| 2291 | (define-key map [mode-line down-mouse-1] eglot-server-menu) | ||
| 2292 | map)) | ||
| 2293 | ,@(when last-error | 2346 | ,@(when last-error |
| 2294 | `("/" ,(eglot--mode-line-props | 2347 | `("/" ,(eglot--mode-line-props |
| 2295 | "error" 'compilation-mode-line-fail | 2348 | "error" 'compilation-mode-line-fail |
| @@ -2310,7 +2363,11 @@ still unanswered LSP requests to the server\n"))) | |||
| 2310 | 'eglot-mode-line | 2363 | 'eglot-mode-line |
| 2311 | nil | 2364 | nil |
| 2312 | (format "(%s) %s %s" (nth 1 pr) | 2365 | (format "(%s) %s %s" (nth 1 pr) |
| 2313 | (nth 2 pr) (nth 3 pr)))))))))) | 2366 | (nth 2 pr) (nth 3 pr))))) |
| 2367 | ,@(when (and | ||
| 2368 | (memq 'mode-line eglot-code-action-indications) | ||
| 2369 | (overlay-buffer eglot--suggestion-overlay)) | ||
| 2370 | `("/" ,(overlay-get eglot--suggestion-overlay 'eglot--suggestion-tooltip)))))))) | ||
| 2314 | 2371 | ||
| 2315 | (add-to-list 'mode-line-misc-info | 2372 | (add-to-list 'mode-line-misc-info |
| 2316 | `(eglot--managed-mode (" [" eglot--mode-line-format "] "))) | 2373 | `(eglot--managed-mode (" [" eglot--mode-line-format "] "))) |
| @@ -3513,8 +3570,6 @@ for which LSP on-type-formatting should be requested." | |||
| 3513 | :deferred :textDocument/hover)) | 3570 | :deferred :textDocument/hover)) |
| 3514 | t)) | 3571 | t)) |
| 3515 | 3572 | ||
| 3516 | (defvar eglot--highlights nil "Overlays for textDocument/documentHighlight.") | ||
| 3517 | |||
| 3518 | (defun eglot-highlight-eldoc-function (_cb &rest _ignored) | 3573 | (defun eglot-highlight-eldoc-function (_cb &rest _ignored) |
| 3519 | "A member of `eldoc-documentation-functions', for highlighting symbols'." | 3574 | "A member of `eldoc-documentation-functions', for highlighting symbols'." |
| 3520 | ;; Obviously, we're not using ElDoc for documentation, but merely its | 3575 | ;; Obviously, we're not using ElDoc for documentation, but merely its |
| @@ -3760,6 +3815,20 @@ edit proposed by the server." | |||
| 3760 | (t | 3815 | (t |
| 3761 | (list (point) (point)))))) | 3816 | (list (point) (point)))))) |
| 3762 | 3817 | ||
| 3818 | (cl-defun eglot--code-action-params (&key (beg (point)) (end beg) | ||
| 3819 | only triggerKind) | ||
| 3820 | (list :textDocument (eglot--TextDocumentIdentifier) | ||
| 3821 | :range (list :start (eglot--pos-to-lsp-position beg) | ||
| 3822 | :end (eglot--pos-to-lsp-position end)) | ||
| 3823 | :context | ||
| 3824 | `(:diagnostics | ||
| 3825 | [,@(cl-loop for diag in (flymake-diagnostics beg end) | ||
| 3826 | when (cdr (assoc 'eglot-lsp-diag | ||
| 3827 | (eglot--diag-data diag))) | ||
| 3828 | collect it)] | ||
| 3829 | ,@(when only `(:only [,only])) | ||
| 3830 | ,@(when triggerKind `(:triggerKind ,triggerKind))))) | ||
| 3831 | |||
| 3763 | (defun eglot-code-actions (beg &optional end action-kind interactive) | 3832 | (defun eglot-code-actions (beg &optional end action-kind interactive) |
| 3764 | "Find LSP code actions of type ACTION-KIND between BEG and END. | 3833 | "Find LSP code actions of type ACTION-KIND between BEG and END. |
| 3765 | Interactively, offer to execute them. | 3834 | Interactively, offer to execute them. |
| @@ -3776,29 +3845,31 @@ at point. With prefix argument, prompt for ACTION-KIND." | |||
| 3776 | t)) | 3845 | t)) |
| 3777 | (eglot-server-capable-or-lose :codeActionProvider) | 3846 | (eglot-server-capable-or-lose :codeActionProvider) |
| 3778 | (let* ((server (eglot--current-server-or-lose)) | 3847 | (let* ((server (eglot--current-server-or-lose)) |
| 3848 | (shortcut (and interactive | ||
| 3849 | (not (listp last-nonmenu-event)) ;; not run by mouse | ||
| 3850 | (overlayp eglot--suggestion-overlay) | ||
| 3851 | (overlay-buffer eglot--suggestion-overlay) | ||
| 3852 | (= beg (overlay-start eglot--suggestion-overlay)) | ||
| 3853 | (= end (overlay-end eglot--suggestion-overlay)))) | ||
| 3779 | (actions | 3854 | (actions |
| 3780 | (eglot--request | 3855 | (if shortcut |
| 3781 | server | 3856 | (overlay-get eglot--suggestion-overlay 'eglot--actions) |
| 3782 | :textDocument/codeAction | 3857 | (eglot--request |
| 3783 | (list :textDocument (eglot--TextDocumentIdentifier) | 3858 | server |
| 3784 | :range (list :start (eglot--pos-to-lsp-position beg) | 3859 | :textDocument/codeAction |
| 3785 | :end (eglot--pos-to-lsp-position end)) | 3860 | (eglot--code-action-params :beg beg :end end :only action-kind)))) |
| 3786 | :context | ||
| 3787 | `(:diagnostics | ||
| 3788 | [,@(cl-loop for diag in (flymake-diagnostics beg end) | ||
| 3789 | when (cdr (assoc 'eglot-lsp-diag | ||
| 3790 | (eglot--diag-data diag))) | ||
| 3791 | collect it)] | ||
| 3792 | ,@(when action-kind `(:only [,action-kind])))))) | ||
| 3793 | ;; Redo filtering, in case the `:only' didn't go through. | 3861 | ;; Redo filtering, in case the `:only' didn't go through. |
| 3794 | (actions (cl-loop for a across actions | 3862 | (actions (cl-loop for a across actions |
| 3795 | when (or (not action-kind) | 3863 | when (or (not action-kind) |
| 3796 | ;; github#847 | 3864 | ;; github#847 |
| 3797 | (string-prefix-p action-kind (plist-get a :kind))) | 3865 | (string-prefix-p action-kind (plist-get a :kind))) |
| 3798 | collect a))) | 3866 | collect a))) |
| 3799 | (if interactive | 3867 | (cond |
| 3800 | (eglot--read-execute-code-action actions server action-kind) | 3868 | ((and shortcut actions (null (cdr actions))) |
| 3801 | actions))) | 3869 | (eglot-execute server (car actions))) |
| 3870 | (interactive | ||
| 3871 | (eglot--read-execute-code-action actions server action-kind)) | ||
| 3872 | (t actions)))) | ||
| 3802 | 3873 | ||
| 3803 | (defalias 'eglot-code-actions-at-mouse (eglot--mouse-call 'eglot-code-actions) | 3874 | (defalias 'eglot-code-actions-at-mouse (eglot--mouse-call 'eglot-code-actions) |
| 3804 | "Like `eglot-code-actions', but intended for mouse events.") | 3875 | "Like `eglot-code-actions', but intended for mouse events.") |
| @@ -3826,7 +3897,8 @@ at point. With prefix argument, prompt for ACTION-KIND." | |||
| 3826 | default-action) | 3897 | default-action) |
| 3827 | menu-items nil t nil nil default-action) | 3898 | menu-items nil t nil nil default-action) |
| 3828 | menu-items)))))) | 3899 | menu-items)))))) |
| 3829 | (eglot-execute server chosen))) | 3900 | (when chosen |
| 3901 | (eglot-execute server chosen)))) | ||
| 3830 | 3902 | ||
| 3831 | (defmacro eglot--code-action (name kind) | 3903 | (defmacro eglot--code-action (name kind) |
| 3832 | "Define NAME to execute KIND code action." | 3904 | "Define NAME to execute KIND code action." |
| @@ -3841,6 +3913,57 @@ at point. With prefix argument, prompt for ACTION-KIND." | |||
| 3841 | (eglot--code-action eglot-code-action-rewrite "refactor.rewrite") | 3913 | (eglot--code-action eglot-code-action-rewrite "refactor.rewrite") |
| 3842 | (eglot--code-action eglot-code-action-quickfix "quickfix") | 3914 | (eglot--code-action eglot-code-action-quickfix "quickfix") |
| 3843 | 3915 | ||
| 3916 | (defun eglot-code-action-suggestion (cb &rest _ignored) | ||
| 3917 | "A member of `eldoc-documentation-functions', for suggesting actions." | ||
| 3918 | (when (and (eglot-server-capable :codeActionProvider) | ||
| 3919 | eglot-code-action-indications) | ||
| 3920 | (let ((buf (current-buffer)) | ||
| 3921 | (bounds (eglot--code-action-bounds)) | ||
| 3922 | (use-text-p (memq 'eldoc-hint eglot-code-action-indications)) | ||
| 3923 | tooltip blurb) | ||
| 3924 | (jsonrpc-async-request | ||
| 3925 | (eglot--current-server-or-lose) | ||
| 3926 | :textDocument/codeAction | ||
| 3927 | (eglot--code-action-params :beg (car bounds) :end (cadr bounds) | ||
| 3928 | :triggerKind 2) | ||
| 3929 | :success-fn | ||
| 3930 | (lambda (actions) | ||
| 3931 | (eglot--when-buffer-window buf | ||
| 3932 | (delete-overlay eglot--suggestion-overlay) | ||
| 3933 | (when (cl-plusp (length actions)) | ||
| 3934 | (setq blurb | ||
| 3935 | (substitute-command-keys | ||
| 3936 | (eglot--format "\\[eglot-code-actions]: %s" | ||
| 3937 | (plist-get (aref actions 0) :title)))) | ||
| 3938 | (if (>= (length actions) 2) | ||
| 3939 | (setq blurb (concat blurb (format " (and %s more actions)" | ||
| 3940 | (1- (length actions)))))) | ||
| 3941 | (setq tooltip | ||
| 3942 | (propertize eglot-code-action-indicator | ||
| 3943 | 'face 'eglot-code-action-indicator-face | ||
| 3944 | 'help-echo blurb | ||
| 3945 | 'mouse-face 'highlight | ||
| 3946 | 'keymap eglot-diagnostics-map)) | ||
| 3947 | (save-excursion | ||
| 3948 | (goto-char (car bounds)) | ||
| 3949 | (let ((ov (make-overlay (car bounds) (cadr bounds)))) | ||
| 3950 | (overlay-put ov 'eglot--actions actions) | ||
| 3951 | (overlay-put ov 'eglot--suggestion-tooltip tooltip) | ||
| 3952 | (overlay-put | ||
| 3953 | ov | ||
| 3954 | 'before-string | ||
| 3955 | (cond ((memq 'nearby eglot-code-action-indications) | ||
| 3956 | tooltip) | ||
| 3957 | ((memq 'margin eglot-code-action-indications) | ||
| 3958 | (propertize "⚡" | ||
| 3959 | 'display | ||
| 3960 | `((margin left-margin) | ||
| 3961 | ,tooltip))))) | ||
| 3962 | (setq eglot--suggestion-overlay ov))))) | ||
| 3963 | (when use-text-p (funcall cb blurb))) | ||
| 3964 | :deferred :textDocument/codeAction) | ||
| 3965 | (and use-text-p t)))) | ||
| 3966 | |||
| 3844 | 3967 | ||
| 3845 | ;;; Dynamic registration | 3968 | ;;; Dynamic registration |
| 3846 | ;;; | 3969 | ;;; |