Skip to content

Commit ebfd47f

Browse files
Allow actions with multiple selectable edits
1 parent d5a1a5e commit ebfd47f

34 files changed

+672
-504
lines changed

crates/ra_assists/src/assist_ctx.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{AssistAction, AssistId, AssistLabel};
1414
#[derive(Clone, Debug)]
1515
pub(crate) enum Assist {
1616
Unresolved { label: AssistLabel },
17-
Resolved { label: AssistLabel, action: AssistAction },
17+
Resolved { label: AssistLabel, available_actions: Vec<AssistAction> },
1818
}
1919

2020
/// `AssistCtx` allows to apply an assist or check if it could be applied.
@@ -77,20 +77,40 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
7777
f(ctx)
7878
}
7979

80-
pub(crate) fn add_assist(
80+
pub(crate) fn add_assist_with_single_action(
8181
self,
8282
id: AssistId,
8383
label: impl Into<String>,
84-
f: impl FnOnce(&mut AssistBuilder),
84+
f: impl FnOnce(&mut ActionBuilder),
8585
) -> Option<Assist> {
8686
let label = AssistLabel { label: label.into(), id };
8787
let assist = if self.should_compute_edit {
8888
let action = {
89-
let mut edit = AssistBuilder::default();
89+
let mut edit = ActionBuilder::default();
9090
f(&mut edit);
9191
edit.build()
9292
};
93-
Assist::Resolved { label, action }
93+
Assist::Resolved { label, available_actions: vec![action] }
94+
} else {
95+
Assist::Unresolved { label }
96+
};
97+
98+
Some(assist)
99+
}
100+
101+
#[allow(dead_code)] // will be used for auto import assist with multiple actions
102+
pub(crate) fn add_assist(
103+
self,
104+
id: AssistId,
105+
label: impl Into<String>,
106+
f: impl FnOnce() -> Vec<ActionBuilder>,
107+
) -> Option<Assist> {
108+
let label = AssistLabel { label: label.into(), id };
109+
let assist = if self.should_compute_edit {
110+
Assist::Resolved {
111+
label,
112+
available_actions: f().into_iter().map(ActionBuilder::build).collect(),
113+
}
94114
} else {
95115
Assist::Unresolved { label }
96116
};
@@ -126,13 +146,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
126146
}
127147

128148
#[derive(Default)]
129-
pub(crate) struct AssistBuilder {
149+
pub(crate) struct ActionBuilder {
130150
edit: TextEditBuilder,
131151
cursor_position: Option<TextUnit>,
132152
target: Option<TextRange>,
153+
label: Option<String>,
133154
}
134155

135-
impl AssistBuilder {
156+
impl ActionBuilder {
157+
#[allow(dead_code)]
158+
/// Adds a custom label to the action, if it needs to be different from the assist label
159+
pub fn label(&mut self, label: impl Into<String>) {
160+
self.label = Some(label.into())
161+
}
162+
136163
/// Replaces specified `range` of text with a given string.
137164
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
138165
self.edit.replace(range, replace_with.into())
@@ -191,6 +218,7 @@ impl AssistBuilder {
191218
edit: self.edit.finish(),
192219
cursor_position: self.cursor_position,
193220
target: self.target,
221+
label: self.label,
194222
}
195223
}
196224
}

crates/ra_assists/src/assists/add_custom_impl.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
4949
let annotated_name = annotated.syntax().text().to_string();
5050
let start_offset = annotated.syntax().parent()?.text_range().end();
5151

52-
ctx.add_assist(AssistId("add_custom_impl"), "add custom impl", |edit| {
52+
ctx.add_assist_with_single_action(AssistId("add_custom_impl"), "add custom impl", |edit| {
5353
edit.target(attr.syntax().text_range());
5454

5555
let new_attr_input = input
@@ -98,11 +98,11 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
9898
#[cfg(test)]
9999
mod tests {
100100
use super::*;
101-
use crate::helpers::{check_assist, check_assist_not_applicable};
101+
use crate::helpers::{check_assist_first_action, check_assist_not_applicable};
102102

103103
#[test]
104104
fn add_custom_impl_for_unique_input() {
105-
check_assist(
105+
check_assist_first_action(
106106
add_custom_impl,
107107
"
108108
#[derive(Debu<|>g)]
@@ -124,7 +124,7 @@ impl Debug for Foo {
124124

125125
#[test]
126126
fn add_custom_impl_for_with_visibility_modifier() {
127-
check_assist(
127+
check_assist_first_action(
128128
add_custom_impl,
129129
"
130130
#[derive(Debug<|>)]
@@ -146,7 +146,7 @@ impl Debug for Foo {
146146

147147
#[test]
148148
fn add_custom_impl_when_multiple_inputs() {
149-
check_assist(
149+
check_assist_first_action(
150150
add_custom_impl,
151151
"
152152
#[derive(Display, Debug<|>, Serialize)]

crates/ra_assists/src/assists/add_derive.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId};
2828
pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
2929
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
3030
let node_start = derive_insertion_offset(&nominal)?;
31-
ctx.add_assist(AssistId("add_derive"), "add `#[derive]`", |edit| {
31+
ctx.add_assist_with_single_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
3232
let derive_attr = nominal
3333
.attrs()
3434
.filter_map(|x| x.as_simple_call())
@@ -43,7 +43,7 @@ pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
4343
Some(tt) => tt.syntax().text_range().end() - TextUnit::of_char(')'),
4444
};
4545
edit.target(nominal.syntax().text_range());
46-
edit.set_cursor(offset)
46+
edit.set_cursor(offset);
4747
})
4848
}
4949

@@ -59,16 +59,16 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
5959
#[cfg(test)]
6060
mod tests {
6161
use super::*;
62-
use crate::helpers::{check_assist, check_assist_target};
62+
use crate::helpers::{check_assist_first_action, check_assist_first_action_target};
6363

6464
#[test]
6565
fn add_derive_new() {
66-
check_assist(
66+
check_assist_first_action(
6767
add_derive,
6868
"struct Foo { a: i32, <|>}",
6969
"#[derive(<|>)]\nstruct Foo { a: i32, }",
7070
);
71-
check_assist(
71+
check_assist_first_action(
7272
add_derive,
7373
"struct Foo { <|> a: i32, }",
7474
"#[derive(<|>)]\nstruct Foo { a: i32, }",
@@ -77,7 +77,7 @@ mod tests {
7777

7878
#[test]
7979
fn add_derive_existing() {
80-
check_assist(
80+
check_assist_first_action(
8181
add_derive,
8282
"#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
8383
"#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
@@ -86,7 +86,7 @@ mod tests {
8686

8787
#[test]
8888
fn add_derive_new_with_doc_comment() {
89-
check_assist(
89+
check_assist_first_action(
9090
add_derive,
9191
"
9292
/// `Foo` is a pretty important struct.
@@ -104,7 +104,7 @@ struct Foo { a: i32, }
104104

105105
#[test]
106106
fn add_derive_target() {
107-
check_assist_target(
107+
check_assist_first_action_target(
108108
add_derive,
109109
"
110110
struct SomeThingIrrelevant;

crates/ra_assists/src/assists/add_explicit_type.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
4747
return None;
4848
}
4949

50-
ctx.add_assist(AssistId("add_explicit_type"), "add explicit type", |edit| {
50+
ctx.add_assist_with_single_action(AssistId("add_explicit_type"), "add explicit type", |edit| {
5151
edit.target(pat_range);
5252
edit.insert(name_range.end(), format!(": {}", ty.display(db)));
5353
})
@@ -57,16 +57,18 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
5757
mod tests {
5858
use super::*;
5959

60-
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
60+
use crate::helpers::{
61+
check_assist_first_action, check_assist_first_action_target, check_assist_not_applicable,
62+
};
6163

6264
#[test]
6365
fn add_explicit_type_target() {
64-
check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a");
66+
check_assist_first_action_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a");
6567
}
6668

6769
#[test]
6870
fn add_explicit_type_works_for_simple_expr() {
69-
check_assist(
71+
check_assist_first_action(
7072
add_explicit_type,
7173
"fn f() { let a<|> = 1; }",
7274
"fn f() { let a<|>: i32 = 1; }",
@@ -75,7 +77,7 @@ mod tests {
7577

7678
#[test]
7779
fn add_explicit_type_works_for_macro_call() {
78-
check_assist(
80+
check_assist_first_action(
7981
add_explicit_type,
8082
"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
8183
"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
@@ -84,7 +86,7 @@ mod tests {
8486

8587
#[test]
8688
fn add_explicit_type_works_for_macro_call_recursive() {
87-
check_assist(
89+
check_assist_first_action(
8890
add_explicit_type,
8991
"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
9092
"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",

crates/ra_assists/src/assists/add_impl.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::{Assist, AssistCtx, AssistId};
3030
pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
3131
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
3232
let name = nominal.name()?;
33-
ctx.add_assist(AssistId("add_impl"), "add impl", |edit| {
33+
ctx.add_assist_with_single_action(AssistId("add_impl"), "add impl", |edit| {
3434
edit.target(nominal.syntax().text_range());
3535
let type_params = nominal.type_param_list();
3636
let start_offset = nominal.syntax().text_range().end();
@@ -60,17 +60,21 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
6060
#[cfg(test)]
6161
mod tests {
6262
use super::*;
63-
use crate::helpers::{check_assist, check_assist_target};
63+
use crate::helpers::{check_assist_first_action, check_assist_first_action_target};
6464

6565
#[test]
6666
fn test_add_impl() {
67-
check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n");
68-
check_assist(
67+
check_assist_first_action(
68+
add_impl,
69+
"struct Foo {<|>}\n",
70+
"struct Foo {}\n\nimpl Foo {\n<|>\n}\n",
71+
);
72+
check_assist_first_action(
6973
add_impl,
7074
"struct Foo<T: Clone> {<|>}",
7175
"struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
7276
);
73-
check_assist(
77+
check_assist_first_action(
7478
add_impl,
7579
"struct Foo<'a, T: Foo<'a>> {<|>}",
7680
"struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}",
@@ -79,7 +83,7 @@ mod tests {
7983

8084
#[test]
8185
fn add_impl_target() {
82-
check_assist_target(
86+
check_assist_first_action_target(
8387
add_impl,
8488
"
8589
struct SomeThingIrrelevant;

0 commit comments

Comments
 (0)