-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Allow assists with multiple selectable actions #2716
Allow assists with multiple selectable actions #2716
Conversation
540c1de
to
3e352e3
Compare
I had to use I wanted to include those metods in this PR nevertheless since they are more related to this change and showcase the API for the assist with multiple actions. |
|
The CI logs load for me, there are two failing heavy tests (test_missing_module_code_action). I think those are only run on CI. |
TIL we have heavy tests, thank you. |
7c9719b
to
ebfd47f
Compare
For future reference you can enable the slow test by setting |
If I understand it correctly, this change would break other existing clients like the emacs integration, until they're adapted. Maybe there's a way of extending the protocol in a compatible way? |
Good point. What are the compatibility guarantees RA promises to keep currently? There are obviously ways to keep the compatibility, we can either create a new command or fiddle with the parameters of an existing one, for instance. What if we fix the emacs client instead? |
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.
Left high-level design advice!
crates/ra_assists/src/assist_ctx.rs
Outdated
@@ -77,20 +77,40 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
f(ctx) | |||
} | |||
|
|||
pub(crate) fn add_assist( | |||
pub(crate) fn add_assist_with_single_action( |
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.
Assists with a single action will always be a majority. For this reason, it makes sense to organize the API such that it builds a single-action assist by default, and you need extra steps to add more actions.
That means that, ideally, existing assists and tests should not not see any changes at tall, as the existing API is already optimized for single action use-case.
So, I suggest leaving add_assist
/ check_assist
APIs as is, and instead adding a separate add_assist_group
API.
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.
Evaluating this direction further, since currently this is the only way of grouping that works for sure.
What if we add an extra Vec<AssistAction>
field to the Assist
struct, something like:
pub(crate) enum Assist {
Unresolved { label: AssistLabel },
Resolved { label: AssistLabel, action: AssistAction, alternative_actions: Vec<AssistAction> },
}
and modify the protocol part correspondingly:
export async function applySourceChange(ctx: Ctx, change: SourceChange, alternative_changes: SourceChange[] | undefined)
?
That way the backward compatibility is preserved (checked by adjusting the protocol part only on a fresh master) and we enforce at least one assist presence on a compile time.
crates/ra_assists/src/lib.rs
Outdated
@@ -63,7 +64,7 @@ where | |||
/// | |||
/// Assists are returned in the "resolved" state, that is with edit fully | |||
/// computed. | |||
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)> | |||
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, Vec<AssistAction>)> |
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.
I thin the return type should look like this:
pub fn assists<H>(db: &H, range: FileRange) ->
(
Vec<WithLabel<
Either<
AssistId,
Vec<WithLabel<AssistId>>
>
>>,
FxHashMap<AssistId, AssistAction>
)
{ ... }
struct WithLabel<T> {
label: String,
value: T,
}
but we probably should introduce some named types to avoid >>>>>
.
In other words, with lhs we should be able to draw a depth-one tree
do foo (id1)
do baz (id2)
with spam (id3)
with eggs (id4)
do quux (id5)
and with rhs we shoudl be able to map each id to an action!
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.
I've tried this approach btw and it's not that straightforward, I think:
-
Currently
AssistAction
has an optional label that should be set in the builder, so we cannot really returnVec<WithLabel<AssistId>>
currently.
Forcing the users to input the label every time means changing all current assists and duplicating the Assist label for a singleAsssistAction
. -
There's no
FxHashMap
crate inra_assists
and it feels like we don't really need it that much: it looks like we should rather returnVec<Assist>
whereAssist
will be something like
struct Assist {
label: String,
id: AssistId,
action_data: Either<AssistAction, Vec<AssistAction>>,
}
or leave it as it is currently (with alternative_actions
).
Former appoach will also require some additional changes in tests again, since now they are relying on a single action to be present.
So it feels like maximum I can do here is to add some wrapper named struct to hold all data returned and a few methods to it.
I believe vim client(s) also support this extension. Really, we should "just" upstream our extensions for the assists into the protocol (I think the relevant disucssion is microsoft/language-server-protocol#724) |
Why doesn't returning multiple items in the reply handle this? See: https://microsoft.github.io/language-server-protocol/specifications/specification-3-15/#textDocument_codeAction |
@kjeremy , do you mean returning multiple actions instead of one action with multiple edits? I keep the auto import action case in my head and from its perspective my thoughts to go this paths are the following:
So, the current way is more like a convenient grouping, allowing the user to quickly select the needed action and then to think on the options provided by it. Your proposal is great in a sense that it's easier to implement, another thing that concerns me there (besides the bullets above) is the Does anybody have a preference on which approach to go with? |
I feel that we'll need assist groups eventually, so I'd love to add the required plumbing! But just returning, say, the first three candidates also seems fine! |
@SomeoneToIgnore yes I think that's what I mean grouped by |
I'd like to continue with grouping after all the time I've put into it, but have no idea how to achieve this with the way @kjeremy proposes. I've added the custom code action name to the action creation and to the server capabilities and to the client capabilities, but still get the non-grouped actions listed. Did I miss anything? |
6192ef4
to
0cb9092
Compare
I've implemented the #2716 (comment) idea of mine, no more breaking changes in the protocol, double checked that on master with client changes only. The #2716 (comment) is not addressed yet, let's pick the final approach first. |
editors/code/src/source_change.ts
Outdated
@@ -55,3 +55,13 @@ export async function applySourceChange(ctx: Ctx, change: SourceChange) { | |||
); | |||
} | |||
} | |||
|
|||
export async function applySourceChange(ctx: Ctx, change: SourceChange, alternativeChanges: SourceChange[] | undefined) { |
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.
It still seems to me that overloading this function is wrong. Depending on the arguments you pass to it, it can be either just apply some side-effect, or it can interactively ask user for some input. I'd rather avoid mixing interactive and non-interactive usages in the same function.
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.
Yeah, the client side split is great, I like it.
The question is how to pass the data from a backend, that what I wanted to discuss in
#2716 (comment)
So far it seems that I'd better go with the option #2 from #2716 (comment)
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.
I think you'll need to pick a different action name in conv_rs, but other than that, the current backend setup looks reasonable to me.
I can't say that I feel that it is unquestionably perfect, but I can't suggest any immediate obvious improvements :)
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.
Ok, I've come up with the changes that hopefully make the overall thing better.
d93f75e
to
7c6a71b
Compare
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.
I like this now!
I'd love to try one other way to encode groups, but I'll do this after this PR is merged, as the idea is vague, and I don't want to block on it.
r=me with itertools replaced with either bors delegate+ |
✌️ SomeoneToIgnore can now approve this pull request. To approve and merge a pull request, simply reply with |
7c6a71b
to
79b7740
Compare
Thanks for spending the time on reviewing it! bors r+ |
2716: Allow assists with multiple selectable actions r=SomeoneToIgnore a=SomeoneToIgnore This PR prepares an infra for #2180 task by adding a possibility to specify multiple actions in one assist as multiple edit parameters to the `applySourceChange` command. When this is done, the command opens a selection dialog, allowing the user to pick the edit to be applied. I have no working example to test in this PR, but here's a demo of an auto import feature (a separate PR coming later for that one) using this functionality: ![out](https://user-images.githubusercontent.com/2690773/71633614-f8ea4d80-2c1d-11ea-9b15-0e13611a7aa4.gif) The PR is not that massive as it may seem: all the assist files' changes are very generic and similar. Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Build succeeded
|
Actually, I'll hold up until the auto-import PR is submitted and merged, to avoid merge conflicts and to have a better example to work from |
This PR prepares an infra for #2180 task by adding a possibility to specify multiple actions in one assist as multiple edit parameters to the
applySourceChange
command.When this is done, the command opens a selection dialog, allowing the user to pick the edit to be applied.
I have no working example to test in this PR, but here's a demo of an auto import feature (a separate PR coming later for that one) using this functionality:
The PR is not that massive as it may seem: all the assist files' changes are very generic and similar.