-
-
Couldn't load subscription status.
- Fork 939
Feature/handle disabled code actions #2402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2a261d8
046ffd1
4865379
7286bca
22d46fd
0f48af3
e6342b4
d9d26c3
9b3602f
cb1897a
801d014
ace8758
74a828e
abedf27
8367f49
7f65d1c
7b55270
980144d
05d5271
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1272,9 +1272,15 @@ the lists according to METHOD." | |||||
|
|
||||||
| TRANSFORM-FN will be used to transform each of the items before displaying. | ||||||
|
|
||||||
| PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF | ||||||
| INHERIT-INPUT-METHOD will be proxied to `completing-read' without changes." | ||||||
| IF DEF is a number, it specifies the zero-based index into the | ||||||
| COLLECTION that is to be used as the default result and that is | ||||||
| to be preselected. | ||||||
|
|
||||||
| PROMPT COLLECTION PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST | ||||||
| INHERIT-INPUT-METHOD will be proxied to `completing-read' without | ||||||
| changes." | ||||||
| (let* ((col (--map (cons (funcall transform-fn it) it) collection)) | ||||||
| (def (if (numberp def) (nth def col) def)) | ||||||
| (completion (completing-read prompt col | ||||||
| predicate require-match initial-input hist | ||||||
| def inherit-input-method))) | ||||||
|
|
@@ -3245,6 +3251,7 @@ disappearing, unset all the variables related to it." | |||||
| (rename . ((dynamicRegistration . t) (prepareSupport . t))) | ||||||
| (codeAction . ((dynamicRegistration . t) | ||||||
| (isPreferredSupport . t) | ||||||
| (disabledSupport . t) | ||||||
| (codeActionLiteralSupport . ((codeActionKind . ((valueSet . ["" | ||||||
| "quickfix" | ||||||
| "refactor" | ||||||
|
|
@@ -4834,18 +4841,46 @@ When language is nil render as markup if `markdown-mode' is loaded." | |||||
| (format "%s (%s)" element count)) | ||||||
| (push element elements)))))) | ||||||
|
|
||||||
| (defface lsp-preferred-code-action-face '((t :underline t)) | ||||||
| "Face used to highlight preferred code actions. | ||||||
| Used by `lsp-execute-code-action' and | ||||||
| `lsp-execute-code-action-by-kind' if they have to prompt for an | ||||||
| action.") | ||||||
|
|
||||||
| (defface lsp-disabled-code-action-face '((t :inherit shadow)) | ||||||
| "Face used to highlight disabled code actions. | ||||||
| See `lsp-preferred-code-action-face' for details.") | ||||||
|
|
||||||
| (defface lsp-disabled-code-action-reason-face '((t :inherit lsp-details-face)) | ||||||
| "Face used to highlight the reason a code action is disabled. | ||||||
| Shown after the code action in `lsp-execute-code-action', | ||||||
| ... (see `lsp-preferred-code-action-face').") | ||||||
|
|
||||||
| (lsp-defun lsp--code-action-title ((action &as &CodeAction :title :is-preferred? :disabled?)) | ||||||
| "Render an `&CodeAction' as a propertized string." | ||||||
| (--doto (copy-sequence title) | ||||||
| (let ((len (length it))) | ||||||
| (when is-preferred? | ||||||
| (add-face-text-property 0 len 'lsp-preferred-code-action-face nil it)) | ||||||
| (when disabled? | ||||||
| (add-face-text-property 0 len 'lsp-disabled-code-action-face nil it) | ||||||
| (-when-let ((&CodeActionDisabled :reason) disabled?) | ||||||
| (cl-callf concat it " " (propertize reason 'face 'lsp-disabled-code-action-reason-face))))))) | ||||||
|
|
||||||
| (defun lsp--select-action (actions) | ||||||
| "Select an action to execute from ACTIONS." | ||||||
| (cond | ||||||
| ((seq-empty-p actions) (signal 'lsp-no-code-actions nil)) | ||||||
| ((and (eq (seq-length actions) 1) lsp-auto-execute-action) | ||||||
| (lsp-seq-first actions)) | ||||||
| (t (let ((completion-ignore-case t)) | ||||||
| (lsp--completing-read "Select code action: " | ||||||
| (seq-into actions 'list) | ||||||
| (-compose (lsp--create-unique-string-fn) | ||||||
| #'lsp:code-action-title) | ||||||
| nil t))))) | ||||||
| (let ((actions (seq-into actions 'list))) | ||||||
| (pcase actions | ||||||
| (`nil (signal 'lsp-no-code-actions nil)) | ||||||
| ((and `(,action) (guard lsp-auto-execute-action)) action) | ||||||
| (_ (let ((completion-ignore-case t)) | ||||||
| (lsp--completing-read "Select code action: " | ||||||
| actions | ||||||
| (-compose (lsp--create-unique-string-fn) | ||||||
| #'lsp--code-action-title) | ||||||
| nil t nil nil | ||||||
| (or (-find-index #'lsp:code-action-is-preferred? actions) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have implemented that for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, sorry, I missed that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the COLLECTION is a list of strings that is to be transformed, there would be an ambiguitiy: should DEF be used to look up the corresponding string in COLLECTION, or be the DEF argument to completing read? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't that ambiguity be resolved if we document it? For example, DEF should be the corresponding string in COLLECTION (since it's what's returned). That would also be aligned with original |
||||||
| (-find-index (-not #'lsp:code-action-disabled?) actions)))))))) | ||||||
|
|
||||||
| (defun lsp--workspace-server-id (workspace) | ||||||
| "Return the server ID of WORKSPACE." | ||||||
|
|
@@ -5211,19 +5246,57 @@ It will show up only if current point has signature help." | |||||
| :context `( :diagnostics ,(lsp-cur-line-diagnostics) | ||||||
| ,@(when kind (list :only (vector kind)))))) | ||||||
|
|
||||||
| (defun lsp-code-actions-at-point (&optional kind) | ||||||
| (defun lsp-code-actions-at-point (&optional kind enabled) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While we're here... could we make this a list of kinds? The protocol supports that. |
||||||
| "Retrieve the code actions for the active region or the current line. | ||||||
| It will filter by KIND if non nil." | ||||||
| (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))) | ||||||
| If ENABLED is specified, only return code actions that are not | ||||||
| disabled." | ||||||
| (--doto (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind)) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this section is on the edge of being too trickier. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I had in mind here was the following: currently there is a single filter: ENABLED. In the future, more filters could be added, and using (let ((actions (lsp-request "textDocument/codeAction" (lsp--text-document-code-action-params kind))))
(if enabled
(seq-remove #'lsp:code-action-disabled? actions)
acitons))I just realized that the code has a bug: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it could stay as it is, just it is a bit unusual. Once There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something like that (clj) |
||||||
| (when enabled | ||||||
| (cl-callf2 seq-remove #'lsp:code-action-disabled? it)))) | ||||||
|
|
||||||
| (defun lsp-execute-code-action-by-kind (command-kind) | ||||||
| "Execute code action by COMMAND-KIND." | ||||||
| (if-let ((action (->> (lsp-get-or-calculate-code-actions command-kind) | ||||||
| (-filter (-lambda ((&CodeAction :kind?)) | ||||||
| (and kind? (equal command-kind kind?)))) | ||||||
| lsp--select-action))) | ||||||
| (lsp-execute-code-action action) | ||||||
| (signal 'lsp-no-code-actions '(command-kind)))) | ||||||
| "Execute code action by name." | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| (let* ((all-actions (->> (lsp-get-or-calculate-code-actions command-kind) | ||||||
| (-filter (-lambda ((&CodeAction :kind?)) | ||||||
| (and kind? (equal command-kind kind?)))))) | ||||||
| (enabled-actions (-remove #'lsp:code-action-disabled? all-actions)) | ||||||
| (action (lsp--select-action (or enabled-actions all-actions)))) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this ok? In case of all actions are disabled, we show all actions instead of empty list? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea is to show an error message in case there is no enabled code action, if the user has enabled As for guarding, we needn't do that, since the user cannot exit from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't |
||||||
| (lsp-execute-code-action action))) | ||||||
|
|
||||||
| (defun lsp-execute-code-action-by-type (kind &optional enabled) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference between this and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is a distinction the spec really knows about - certainly "base type" doesn't appear. They do talk about "base kinds", but I'm not sure they're meaningfully distinct from "kinds", it just indicates that they should be hierarchical parents of a set of kinds. What about just having an |
||||||
| "Execute a code action with a given base KIND. | ||||||
| When ENABLED is given, filter out all disabled code actions. This | ||||||
| is in contrast with the spec's recommended behavior." | ||||||
| (->> (lsp-get-or-calculate-code-actions kind enabled) | ||||||
kiennq marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| (-filter (-lambda ((&CodeAction :kind?)) | ||||||
| (and kind? (string-match-p (format "\\`%s\\(\\.\\|\\'\\)" | ||||||
| (regexp-quote kind)) | ||||||
| kind?)))) | ||||||
| lsp--select-action | ||||||
| lsp-execute-code-action)) | ||||||
|
|
||||||
| (defmacro lsp-make-interactive-code-action-by-type (name kind) | ||||||
| "Make interactive function NAME to execute a KIND code action. | ||||||
| Like `lsp-make-interactive-code-action' but using | ||||||
| `lsp-execute-code-action-by-type'." | ||||||
| `(defun ,name () | ||||||
| ,(format "Execute a %S code action at point." kind) | ||||||
| (interactive) | ||||||
| (condition-case nil | ||||||
| (lsp-execute-code-action-by-kind ,kind) | ||||||
| (lsp-no-code-actions | ||||||
| (user-error "No %S actions available"))))) | ||||||
|
|
||||||
| (lsp-make-interactive-code-action-by-type lsp-quickfix "quickfix") | ||||||
| (lsp-make-interactive-code-action-by-type lsp-refactor "refactor") | ||||||
|
|
||||||
| (defun lsp-auto-fix () | ||||||
| "Execute a preferred code action at point." | ||||||
| (interactive) | ||||||
| (->> (lsp-get-or-calculate-code-actions) | ||||||
kiennq marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| (-filter #'lsp:code-action-is-preferred?) | ||||||
| lsp--select-action | ||||||
| lsp-execute-code-action)) | ||||||
|
|
||||||
| (defalias 'lsp-get-or-calculate-code-actions 'lsp-code-actions-at-point) | ||||||
|
|
||||||
|
|
@@ -5242,11 +5315,19 @@ It will filter by KIND if non nil." | |||||
| (funcall action-handler action) | ||||||
| (lsp-send-execute-command command arguments?)))))) | ||||||
|
|
||||||
| (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit?)) | ||||||
| (lsp-defun lsp-execute-code-action ((action &as &CodeAction :command? :edit? :disabled?)) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this have an optional There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not in this case. |
||||||
| "Execute code action ACTION. | ||||||
| If ACTION is not set it will be selected from `lsp-code-actions-at-point'. | ||||||
| Request codeAction/resolve for more info if server supports." | ||||||
| (interactive (list (lsp--select-action (lsp-code-actions-at-point)))) | ||||||
| Interactively, ask for a code action. With the prefix argument, | ||||||
| filter those that are not disabled. Request codeAction/resolve | ||||||
| for more info if server supports." | ||||||
| (interactive | ||||||
| (list (lsp--select-action | ||||||
| (lsp-code-actions-at-point nil (not current-prefix-arg))))) | ||||||
| (when disabled? | ||||||
| (error "%s is disabled%s" | ||||||
| (lsp:code-action-title action) | ||||||
| (or (-some->> (lsp:code-action-disabled-reason disabled?) (concat ": ")) ""))) | ||||||
|
|
||||||
| (if (and (lsp-feature? "codeAction/resolve") | ||||||
| (not command?) | ||||||
| (not edit?)) | ||||||
|
|
||||||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. I wanted to add a help-echo to disabled code actions at some point, but then realized that it wouldn't work with completing-read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, this is a regression from the commit to support both preferred and disabled code actions at the same time. It was supposed to show the reason. I'll need to add it back.