Skip to content

Commit 3e8351f

Browse files
bors[bot]robojumper
andcommitted
Merge #768
768: Sort assists by the range of the affected element r=matklad a=robojumper Closes #763. https://github.com/rust-analyzer/rust-analyzer/blob/3be98f2ac93b278828e76eb813bdd8033f647b12/crates/ra_assists/src/lib.rs#L233-L236 This could be made more robust by a) adding a way to identify actions by things other than their label and b) allowing arbitrary actions to appear in the list as long as the tested actions are there in the correct order. Let me know if I should do any of that. Co-authored-by: robojumper <[email protected]>
2 parents 12e3b4c + 4fdeb54 commit 3e8351f

12 files changed

+264
-22
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_assists/src/add_derive.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
2424
}
2525
Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
2626
};
27+
edit.target(nominal.syntax().range());
2728
edit.set_cursor(offset)
2829
})
2930
}
@@ -38,7 +39,7 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
3839
#[cfg(test)]
3940
mod tests {
4041
use super::*;
41-
use crate::helpers::check_assist;
42+
use crate::helpers::{check_assist, check_assist_target};
4243

4344
#[test]
4445
fn add_derive_new() {
@@ -80,4 +81,21 @@ struct Foo { a: i32, }
8081
",
8182
);
8283
}
84+
85+
#[test]
86+
fn add_derive_target() {
87+
check_assist_target(
88+
add_derive,
89+
"
90+
struct SomeThingIrrelevant;
91+
/// `Foo` is a pretty important struct.
92+
/// It does stuff.
93+
struct Foo { a: i32<|>, }
94+
struct EvenMoreIrrelevant;
95+
",
96+
"/// `Foo` is a pretty important struct.
97+
/// It does stuff.
98+
struct Foo { a: i32, }",
99+
);
100+
}
83101
}

crates/ra_assists/src/add_impl.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
1111
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
1212
let name = nominal.name()?;
1313
ctx.build("add impl", |edit| {
14+
edit.target(nominal.syntax().range());
1415
let type_params = nominal.type_param_list();
1516
let start_offset = nominal.syntax().range().end();
1617
let mut buf = String::new();
@@ -37,7 +38,7 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
3738
#[cfg(test)]
3839
mod tests {
3940
use super::*;
40-
use crate::helpers::check_assist;
41+
use crate::helpers::{check_assist, check_assist_target};
4142

4243
#[test]
4344
fn test_add_impl() {
@@ -54,4 +55,18 @@ mod tests {
5455
);
5556
}
5657

58+
#[test]
59+
fn add_impl_target() {
60+
check_assist_target(
61+
add_impl,
62+
"
63+
struct SomeThingIrrelevant;
64+
/// Has a lifetime parameter
65+
struct Foo<'a, T: Foo<'a>> {<|>}
66+
struct EvenMoreIrrelevant;
67+
",
68+
"/// Has a lifetime parameter
69+
struct Foo<'a, T: Foo<'a>> {}",
70+
);
71+
}
5772
}

crates/ra_assists/src/assist_ctx.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub(crate) enum Assist {
1616

1717
/// `AssistCtx` allows to apply an assist or check if it could be applied.
1818
///
19-
/// Assists use a somewhat overengineered approach, given the current needs. The
19+
/// Assists use a somewhat over-engineered approach, given the current needs. The
2020
/// assists workflow consists of two phases. In the first phase, a user asks for
2121
/// the list of available assists. In the second phase, the user picks a
2222
/// particular assist and it gets applied.
@@ -106,6 +106,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
106106
pub(crate) struct AssistBuilder {
107107
edit: TextEditBuilder,
108108
cursor_position: Option<TextUnit>,
109+
target: Option<TextRange>,
109110
}
110111

111112
impl AssistBuilder {
@@ -138,7 +139,15 @@ impl AssistBuilder {
138139
self.cursor_position = Some(offset)
139140
}
140141

142+
pub(crate) fn target(&mut self, target: TextRange) {
143+
self.target = Some(target)
144+
}
145+
141146
fn build(self) -> AssistAction {
142-
AssistAction { edit: self.edit.finish(), cursor_position: self.cursor_position }
147+
AssistAction {
148+
edit: self.edit.finish(),
149+
cursor_position: self.cursor_position,
150+
target: self.target,
151+
}
143152
}
144153
}

crates/ra_assists/src/change_visibility.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
2020
_ => false,
2121
});
2222

23-
let offset = if let Some(keyword) = item_keyword {
23+
let (offset, target) = if let Some(keyword) = item_keyword {
2424
let parent = keyword.parent()?;
2525
let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
2626
// Parent is not a definition, can't add visibility
@@ -31,17 +31,18 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
3131
if parent.children().any(|child| child.kind() == VISIBILITY) {
3232
return None;
3333
}
34-
vis_offset(parent)
34+
(vis_offset(parent), keyword.range())
3535
} else {
3636
let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
3737
let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
3838
if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
3939
return None;
4040
}
41-
vis_offset(field.syntax())
41+
(vis_offset(field.syntax()), ident.range())
4242
};
4343

4444
ctx.build("make pub(crate)", |edit| {
45+
edit.target(target);
4546
edit.insert(offset, "pub(crate) ");
4647
edit.set_cursor(offset);
4748
})
@@ -60,13 +61,15 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
6061

6162
fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
6263
if vis.syntax().text() == "pub" {
63-
return ctx.build("chage to pub(crate)", |edit| {
64+
return ctx.build("change to pub(crate)", |edit| {
65+
edit.target(vis.syntax().range());
6466
edit.replace(vis.syntax().range(), "pub(crate)");
6567
edit.set_cursor(vis.syntax().range().start());
6668
});
6769
}
6870
if vis.syntax().text() == "pub(crate)" {
69-
return ctx.build("chage to pub", |edit| {
71+
return ctx.build("change to pub", |edit| {
72+
edit.target(vis.syntax().range());
7073
edit.replace(vis.syntax().range(), "pub");
7174
edit.set_cursor(vis.syntax().range().start());
7275
});
@@ -77,7 +80,7 @@ fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option
7780
#[cfg(test)]
7881
mod tests {
7982
use super::*;
80-
use crate::helpers::check_assist;
83+
use crate::helpers::{check_assist, check_assist_target};
8184

8285
#[test]
8386
fn change_visibility_adds_pub_crate_to_items() {
@@ -135,4 +138,11 @@ mod tests {
135138
",
136139
)
137140
}
141+
142+
#[test]
143+
fn change_visibility_target() {
144+
check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
145+
check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
146+
check_assist_target(change_visibility, "struct S { <|>field: u32 }", "field");
147+
}
138148
}

crates/ra_assists/src/fill_match_arms.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,15 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
6565
buf.push_str(" => (),\n");
6666
}
6767
buf.push_str("}");
68+
edit.target(match_expr.syntax().range());
6869
edit.set_cursor(expr.syntax().range().start());
6970
edit.replace_node_and_indent(match_expr.syntax(), buf);
7071
})
7172
}
7273

7374
#[cfg(test)]
7475
mod tests {
75-
use crate::helpers::check_assist;
76+
use crate::helpers::{check_assist, check_assist_target};
7677

7778
use super::fill_match_arms;
7879

@@ -139,4 +140,19 @@ mod tests {
139140
"#,
140141
);
141142
}
143+
144+
#[test]
145+
fn fill_match_arms_target() {
146+
check_assist_target(
147+
fill_match_arms,
148+
r#"
149+
enum E { X, Y}
150+
151+
fn main() {
152+
match E::X<|> {}
153+
}
154+
"#,
155+
"match E::X {}",
156+
);
157+
}
142158
}

crates/ra_assists/src/flip_comma.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
1111
let prev = non_trivia_sibling(comma, Direction::Prev)?;
1212
let next = non_trivia_sibling(comma, Direction::Next)?;
1313
ctx.build("flip comma", |edit| {
14+
edit.target(comma.range());
1415
edit.replace(prev.range(), next.text());
1516
edit.replace(next.range(), prev.text());
1617
})
@@ -20,7 +21,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
2021
mod tests {
2122
use super::*;
2223

23-
use crate::helpers::check_assist;
24+
use crate::helpers::{check_assist, check_assist_target};
2425

2526
#[test]
2627
fn flip_comma_works_for_function_parameters() {
@@ -30,4 +31,9 @@ mod tests {
3031
"fn foo(y: Result<(), ()>,<|> x: i32) {}",
3132
)
3233
}
34+
35+
#[test]
36+
fn flip_comma_target() {
37+
check_assist_target(flip_comma, "fn foo(x: i32,<|> y: Result<(), ()>) {}", ",")
38+
}
3339
}

crates/ra_assists/src/introduce_variable.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
4545
} else {
4646
buf.push_str(";");
4747
indent.text().push_to(&mut buf);
48+
edit.target(expr.syntax().range());
4849
edit.replace(expr.syntax().range(), "var_name".to_string());
4950
edit.insert(anchor_stmt.range().start(), buf);
5051
if wrap_in_block {
@@ -58,7 +59,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
5859
fn valid_covering_node(node: &SyntaxNode) -> bool {
5960
node.kind() != COMMENT
6061
}
61-
/// Check wether the node is a valid expression which can be extracted to a variable.
62+
/// Check whether the node is a valid expression which can be extracted to a variable.
6263
/// In general that's true for any expression, but in some cases that would produce invalid code.
6364
fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
6465
match node.kind() {
@@ -74,7 +75,7 @@ fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
7475
/// and a boolean indicating whether we have to wrap it within a { } block
7576
/// to produce correct code.
7677
/// It can be a statement, the last in a block expression or a wanna be block
77-
/// expression like a lamba or match arm.
78+
/// expression like a lambda or match arm.
7879
fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
7980
expr.syntax().ancestors().find_map(|node| {
8081
if ast::Stmt::cast(node).is_some() {
@@ -100,7 +101,7 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
100101
#[cfg(test)]
101102
mod tests {
102103
use super::*;
103-
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_range};
104+
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_range, check_assist_target, check_assist_range_target};
104105

105106
#[test]
106107
fn test_introduce_var_simple() {
@@ -425,4 +426,32 @@ fn main() {
425426
",
426427
);
427428
}
429+
430+
// FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
431+
#[test]
432+
fn introduce_var_target() {
433+
check_assist_target(
434+
introduce_variable,
435+
"
436+
fn foo() -> u32 {
437+
r<|>eturn 2 + 2;
438+
}
439+
",
440+
"2 + 2",
441+
);
442+
443+
check_assist_range_target(
444+
introduce_variable,
445+
"
446+
fn main() {
447+
let x = true;
448+
let tuple = match x {
449+
true => (<|>2 + 2<|>, true)
450+
_ => (0, false)
451+
};
452+
}
453+
",
454+
"2 + 2",
455+
);
456+
}
428457
}

0 commit comments

Comments
 (0)