aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-09 08:52:09 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-09 08:52:09 +0000
commit3e8351fb0607f8711749b00d80f68bf25de01a76 (patch)
tree97388dafe71ececcbaf97249021b9c8d49786ccf /crates
parent12e3b4c70b5ef23b2fdfc197296d483680e125f9 (diff)
parent4fdeb54bb5c7ba0704839a65996766d223c51fc1 (diff)
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]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/add_derive.rs20
-rw-r--r--crates/ra_assists/src/add_impl.rs17
-rw-r--r--crates/ra_assists/src/assist_ctx.rs13
-rw-r--r--crates/ra_assists/src/change_visibility.rs22
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs18
-rw-r--r--crates/ra_assists/src/flip_comma.rs8
-rw-r--r--crates/ra_assists/src/introduce_variable.rs35
-rw-r--r--crates/ra_assists/src/lib.rs100
-rw-r--r--crates/ra_assists/src/remove_dbg.rs18
-rw-r--r--crates/ra_assists/src/replace_if_let_with_match.rs25
-rw-r--r--crates/ra_assists/src/split_import.rs8
11 files changed, 262 insertions, 22 deletions
diff --git a/crates/ra_assists/src/add_derive.rs b/crates/ra_assists/src/add_derive.rs
index caf21e079..ea9707631 100644
--- a/crates/ra_assists/src/add_derive.rs
+++ b/crates/ra_assists/src/add_derive.rs
@@ -24,6 +24,7 @@ pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
24 } 24 }
25 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), 25 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
26 }; 26 };
27 edit.target(nominal.syntax().range());
27 edit.set_cursor(offset) 28 edit.set_cursor(offset)
28 }) 29 })
29} 30}
@@ -38,7 +39,7 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
38#[cfg(test)] 39#[cfg(test)]
39mod tests { 40mod tests {
40 use super::*; 41 use super::*;
41 use crate::helpers::check_assist; 42 use crate::helpers::{check_assist, check_assist_target};
42 43
43 #[test] 44 #[test]
44 fn add_derive_new() { 45 fn add_derive_new() {
@@ -80,4 +81,21 @@ struct Foo { a: i32, }
80 ", 81 ",
81 ); 82 );
82 } 83 }
84
85 #[test]
86 fn add_derive_target() {
87 check_assist_target(
88 add_derive,
89 "
90struct SomeThingIrrelevant;
91/// `Foo` is a pretty important struct.
92/// It does stuff.
93struct Foo { a: i32<|>, }
94struct EvenMoreIrrelevant;
95 ",
96 "/// `Foo` is a pretty important struct.
97/// It does stuff.
98struct Foo { a: i32, }",
99 );
100 }
83} 101}
diff --git a/crates/ra_assists/src/add_impl.rs b/crates/ra_assists/src/add_impl.rs
index f2360bc89..32fc074a6 100644
--- a/crates/ra_assists/src/add_impl.rs
+++ b/crates/ra_assists/src/add_impl.rs
@@ -11,6 +11,7 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 let nominal = ctx.node_at_offset::<ast::NominalDef>()?; 11 let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
12 let name = nominal.name()?; 12 let name = nominal.name()?;
13 ctx.build("add impl", |edit| { 13 ctx.build("add impl", |edit| {
14 edit.target(nominal.syntax().range());
14 let type_params = nominal.type_param_list(); 15 let type_params = nominal.type_param_list();
15 let start_offset = nominal.syntax().range().end(); 16 let start_offset = nominal.syntax().range().end();
16 let mut buf = String::new(); 17 let mut buf = String::new();
@@ -37,7 +38,7 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
37#[cfg(test)] 38#[cfg(test)]
38mod tests { 39mod tests {
39 use super::*; 40 use super::*;
40 use crate::helpers::check_assist; 41 use crate::helpers::{check_assist, check_assist_target};
41 42
42 #[test] 43 #[test]
43 fn test_add_impl() { 44 fn test_add_impl() {
@@ -54,4 +55,18 @@ mod tests {
54 ); 55 );
55 } 56 }
56 57
58 #[test]
59 fn add_impl_target() {
60 check_assist_target(
61 add_impl,
62 "
63struct SomeThingIrrelevant;
64/// Has a lifetime parameter
65struct Foo<'a, T: Foo<'a>> {<|>}
66struct EvenMoreIrrelevant;
67",
68 "/// Has a lifetime parameter
69struct Foo<'a, T: Foo<'a>> {}",
70 );
71 }
57} 72}
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 0bf640241..41c8ac2f6 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -16,7 +16,7 @@ pub(crate) enum Assist {
16 16
17/// `AssistCtx` allows to apply an assist or check if it could be applied. 17/// `AssistCtx` allows to apply an assist or check if it could be applied.
18/// 18///
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
20/// assists workflow consists of two phases. In the first phase, a user asks for 20/// assists workflow consists of two phases. In the first phase, a user asks for
21/// the list of available assists. In the second phase, the user picks a 21/// the list of available assists. In the second phase, the user picks a
22/// particular assist and it gets applied. 22/// particular assist and it gets applied.
@@ -106,6 +106,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
106pub(crate) struct AssistBuilder { 106pub(crate) struct AssistBuilder {
107 edit: TextEditBuilder, 107 edit: TextEditBuilder,
108 cursor_position: Option<TextUnit>, 108 cursor_position: Option<TextUnit>,
109 target: Option<TextRange>,
109} 110}
110 111
111impl AssistBuilder { 112impl AssistBuilder {
@@ -138,7 +139,15 @@ impl AssistBuilder {
138 self.cursor_position = Some(offset) 139 self.cursor_position = Some(offset)
139 } 140 }
140 141
142 pub(crate) fn target(&mut self, target: TextRange) {
143 self.target = Some(target)
144 }
145
141 fn build(self) -> AssistAction { 146 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 }
143 } 152 }
144} 153}
diff --git a/crates/ra_assists/src/change_visibility.rs b/crates/ra_assists/src/change_visibility.rs
index fa5f231c8..6d9a4eec2 100644
--- a/crates/ra_assists/src/change_visibility.rs
+++ b/crates/ra_assists/src/change_visibility.rs
@@ -20,7 +20,7 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
20 _ => false, 20 _ => false,
21 }); 21 });
22 22
23 let offset = if let Some(keyword) = item_keyword { 23 let (offset, target) = if let Some(keyword) = item_keyword {
24 let parent = keyword.parent()?; 24 let parent = keyword.parent()?;
25 let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; 25 let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
26 // Parent is not a definition, can't add visibility 26 // Parent is not a definition, can't add visibility
@@ -31,17 +31,18 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
31 if parent.children().any(|child| child.kind() == VISIBILITY) { 31 if parent.children().any(|child| child.kind() == VISIBILITY) {
32 return None; 32 return None;
33 } 33 }
34 vis_offset(parent) 34 (vis_offset(parent), keyword.range())
35 } else { 35 } else {
36 let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?; 36 let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
37 let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?; 37 let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
38 if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() { 38 if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
39 return None; 39 return None;
40 } 40 }
41 vis_offset(field.syntax()) 41 (vis_offset(field.syntax()), ident.range())
42 }; 42 };
43 43
44 ctx.build("make pub(crate)", |edit| { 44 ctx.build("make pub(crate)", |edit| {
45 edit.target(target);
45 edit.insert(offset, "pub(crate) "); 46 edit.insert(offset, "pub(crate) ");
46 edit.set_cursor(offset); 47 edit.set_cursor(offset);
47 }) 48 })
@@ -60,13 +61,15 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
60 61
61fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> { 62fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
62 if vis.syntax().text() == "pub" { 63 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());
64 edit.replace(vis.syntax().range(), "pub(crate)"); 66 edit.replace(vis.syntax().range(), "pub(crate)");
65 edit.set_cursor(vis.syntax().range().start()); 67 edit.set_cursor(vis.syntax().range().start());
66 }); 68 });
67 } 69 }
68 if vis.syntax().text() == "pub(crate)" { 70 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());
70 edit.replace(vis.syntax().range(), "pub"); 73 edit.replace(vis.syntax().range(), "pub");
71 edit.set_cursor(vis.syntax().range().start()); 74 edit.set_cursor(vis.syntax().range().start());
72 }); 75 });
@@ -77,7 +80,7 @@ fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option
77#[cfg(test)] 80#[cfg(test)]
78mod tests { 81mod tests {
79 use super::*; 82 use super::*;
80 use crate::helpers::check_assist; 83 use crate::helpers::{check_assist, check_assist_target};
81 84
82 #[test] 85 #[test]
83 fn change_visibility_adds_pub_crate_to_items() { 86 fn change_visibility_adds_pub_crate_to_items() {
@@ -135,4 +138,11 @@ mod tests {
135 ", 138 ",
136 ) 139 )
137 } 140 }
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 }
138} 148}
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index 741f75e2a..69b535a27 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -65,6 +65,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
65 buf.push_str(" => (),\n"); 65 buf.push_str(" => (),\n");
66 } 66 }
67 buf.push_str("}"); 67 buf.push_str("}");
68 edit.target(match_expr.syntax().range());
68 edit.set_cursor(expr.syntax().range().start()); 69 edit.set_cursor(expr.syntax().range().start());
69 edit.replace_node_and_indent(match_expr.syntax(), buf); 70 edit.replace_node_and_indent(match_expr.syntax(), buf);
70 }) 71 })
@@ -72,7 +73,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
72 73
73#[cfg(test)] 74#[cfg(test)]
74mod tests { 75mod tests {
75 use crate::helpers::check_assist; 76 use crate::helpers::{check_assist, check_assist_target};
76 77
77 use super::fill_match_arms; 78 use super::fill_match_arms;
78 79
@@ -139,4 +140,19 @@ mod tests {
139 "#, 140 "#,
140 ); 141 );
141 } 142 }
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 }
142} 158}
diff --git a/crates/ra_assists/src/flip_comma.rs b/crates/ra_assists/src/flip_comma.rs
index a49820c29..33da58f17 100644
--- a/crates/ra_assists/src/flip_comma.rs
+++ b/crates/ra_assists/src/flip_comma.rs
@@ -11,6 +11,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 let prev = non_trivia_sibling(comma, Direction::Prev)?; 11 let prev = non_trivia_sibling(comma, Direction::Prev)?;
12 let next = non_trivia_sibling(comma, Direction::Next)?; 12 let next = non_trivia_sibling(comma, Direction::Next)?;
13 ctx.build("flip comma", |edit| { 13 ctx.build("flip comma", |edit| {
14 edit.target(comma.range());
14 edit.replace(prev.range(), next.text()); 15 edit.replace(prev.range(), next.text());
15 edit.replace(next.range(), prev.text()); 16 edit.replace(next.range(), prev.text());
16 }) 17 })
@@ -20,7 +21,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
20mod tests { 21mod tests {
21 use super::*; 22 use super::*;
22 23
23 use crate::helpers::check_assist; 24 use crate::helpers::{check_assist, check_assist_target};
24 25
25 #[test] 26 #[test]
26 fn flip_comma_works_for_function_parameters() { 27 fn flip_comma_works_for_function_parameters() {
@@ -30,4 +31,9 @@ mod tests {
30 "fn foo(y: Result<(), ()>,<|> x: i32) {}", 31 "fn foo(y: Result<(), ()>,<|> x: i32) {}",
31 ) 32 )
32 } 33 }
34
35 #[test]
36 fn flip_comma_target() {
37 check_assist_target(flip_comma, "fn foo(x: i32,<|> y: Result<(), ()>) {}", ",")
38 }
33} 39}
diff --git a/crates/ra_assists/src/introduce_variable.rs b/crates/ra_assists/src/introduce_variable.rs
index 4f7c9f3c2..934d1d6b3 100644
--- a/crates/ra_assists/src/introduce_variable.rs
+++ b/crates/ra_assists/src/introduce_variable.rs
@@ -45,6 +45,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
45 } else { 45 } else {
46 buf.push_str(";"); 46 buf.push_str(";");
47 indent.text().push_to(&mut buf); 47 indent.text().push_to(&mut buf);
48 edit.target(expr.syntax().range());
48 edit.replace(expr.syntax().range(), "var_name".to_string()); 49 edit.replace(expr.syntax().range(), "var_name".to_string());
49 edit.insert(anchor_stmt.range().start(), buf); 50 edit.insert(anchor_stmt.range().start(), buf);
50 if wrap_in_block { 51 if wrap_in_block {
@@ -58,7 +59,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
58fn valid_covering_node(node: &SyntaxNode) -> bool { 59fn valid_covering_node(node: &SyntaxNode) -> bool {
59 node.kind() != COMMENT 60 node.kind() != COMMENT
60} 61}
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.
62/// In general that's true for any expression, but in some cases that would produce invalid code. 63/// In general that's true for any expression, but in some cases that would produce invalid code.
63fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> { 64fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
64 match node.kind() { 65 match node.kind() {
@@ -74,7 +75,7 @@ fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
74/// and a boolean indicating whether we have to wrap it within a { } block 75/// and a boolean indicating whether we have to wrap it within a { } block
75/// to produce correct code. 76/// to produce correct code.
76/// It can be a statement, the last in a block expression or a wanna be block 77/// 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.
78fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> { 79fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
79 expr.syntax().ancestors().find_map(|node| { 80 expr.syntax().ancestors().find_map(|node| {
80 if ast::Stmt::cast(node).is_some() { 81 if ast::Stmt::cast(node).is_some() {
@@ -100,7 +101,7 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
100#[cfg(test)] 101#[cfg(test)]
101mod tests { 102mod tests {
102 use super::*; 103 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};
104 105
105 #[test] 106 #[test]
106 fn test_introduce_var_simple() { 107 fn test_introduce_var_simple() {
@@ -425,4 +426,32 @@ fn main() {
425", 426",
426 ); 427 );
427 } 428 }
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 "
436fn foo() -> u32 {
437 r<|>eturn 2 + 2;
438}
439",
440 "2 + 2",
441 );
442
443 check_assist_range_target(
444 introduce_variable,
445 "
446fn 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 }
428} 457}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 881db6347..7928b4983 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -8,7 +8,7 @@
8mod assist_ctx; 8mod assist_ctx;
9 9
10use ra_text_edit::TextEdit; 10use ra_text_edit::TextEdit;
11use ra_syntax::{TextUnit, SyntaxNode, Direction}; 11use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction};
12use ra_db::FileRange; 12use ra_db::FileRange;
13use hir::db::HirDatabase; 13use hir::db::HirDatabase;
14 14
@@ -23,6 +23,7 @@ pub struct AssistLabel {
23pub struct AssistAction { 23pub struct AssistAction {
24 pub edit: TextEdit, 24 pub edit: TextEdit,
25 pub cursor_position: Option<TextUnit>, 25 pub cursor_position: Option<TextUnit>,
26 pub target: Option<TextRange>,
26} 27}
27 28
28/// Return all the assists applicable at the given position. 29/// Return all the assists applicable at the given position.
@@ -53,15 +54,24 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
53where 54where
54 H: HirDatabase + 'static, 55 H: HirDatabase + 'static,
55{ 56{
57 use std::cmp::Ordering;
58
56 AssistCtx::with_ctx(db, range, true, |ctx| { 59 AssistCtx::with_ctx(db, range, true, |ctx| {
57 all_assists() 60 let mut a = all_assists()
58 .iter() 61 .iter()
59 .filter_map(|f| f(ctx.clone())) 62 .filter_map(|f| f(ctx.clone()))
60 .map(|a| match a { 63 .map(|a| match a {
61 Assist::Resolved(label, action) => (label, action), 64 Assist::Resolved(label, action) => (label, action),
62 Assist::Unresolved(..) => unreachable!(), 65 Assist::Unresolved(..) => unreachable!(),
63 }) 66 })
64 .collect() 67 .collect::<Vec<(AssistLabel, AssistAction)>>();
68 a.sort_by(|a, b| match (a.1.target, b.1.target) {
69 (Some(a), Some(b)) => a.len().cmp(&b.len()),
70 (Some(_), None) => Ordering::Less,
71 (None, Some(_)) => Ordering::Greater,
72 (None, None) => Ordering::Equal,
73 });
74 a
65 }) 75 })
66} 76}
67 77
@@ -97,7 +107,7 @@ mod helpers {
97 use hir::mock::MockDatabase; 107 use hir::mock::MockDatabase;
98 use ra_syntax::TextRange; 108 use ra_syntax::TextRange;
99 use ra_db::FileRange; 109 use ra_db::FileRange;
100 use test_utils::{extract_offset, assert_eq_text, add_cursor, extract_range}; 110 use test_utils::{extract_offset, extract_range, assert_eq_text, add_cursor};
101 111
102 use crate::{AssistCtx, Assist}; 112 use crate::{AssistCtx, Assist};
103 113
@@ -151,6 +161,45 @@ mod helpers {
151 assert_eq_text!(after, &actual); 161 assert_eq_text!(after, &actual);
152 } 162 }
153 163
164 pub(crate) fn check_assist_target(
165 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
166 before: &str,
167 target: &str,
168 ) {
169 let (before_cursor_pos, before) = extract_offset(before);
170 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
171 let frange =
172 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
173 let assist =
174 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
175 let action = match assist {
176 Assist::Unresolved(_) => unreachable!(),
177 Assist::Resolved(_, it) => it,
178 };
179
180 let range = action.target.expect("expected target on action");
181 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
182 }
183
184 pub(crate) fn check_assist_range_target(
185 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
186 before: &str,
187 target: &str,
188 ) {
189 let (range, before) = extract_range(before);
190 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
191 let frange = FileRange { file_id, range };
192 let assist =
193 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
194 let action = match assist {
195 Assist::Unresolved(_) => unreachable!(),
196 Assist::Resolved(_, it) => it,
197 };
198
199 let range = action.target.expect("expected target on action");
200 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
201 }
202
154 pub(crate) fn check_assist_not_applicable( 203 pub(crate) fn check_assist_not_applicable(
155 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 204 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
156 before: &str, 205 before: &str,
@@ -162,5 +211,48 @@ mod helpers {
162 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 211 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
163 assert!(assist.is_none()); 212 assert!(assist.is_none());
164 } 213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use hir::mock::MockDatabase;
219 use ra_syntax::TextRange;
220 use ra_db::FileRange;
221 use test_utils::{extract_offset};
222
223 #[test]
224 fn assist_order_field_struct() {
225 let before = "struct Foo { <|>bar: u32 }";
226 let (before_cursor_pos, before) = extract_offset(before);
227 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
228 let frange =
229 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
230 let assists = super::assists(&db, frange);
231 let mut assists = assists.iter();
232
233 assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)");
234 assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
235 }
236
237 #[test]
238 fn assist_order_if_expr() {
239 let before = "
240 pub fn test_some_range(a: int) -> bool {
241 if let 2..6 = 5<|> {
242 true
243 } else {
244 false
245 }
246 }";
247 let (before_cursor_pos, before) = extract_offset(before);
248 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
249 let frange =
250 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
251 let assists = super::assists(&db, frange);
252 let mut assists = assists.iter();
253
254 assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable");
255 assert_eq!(assists.next().expect("expected assist").0.label, "replace with match");
256 }
165 257
166} 258}
diff --git a/crates/ra_assists/src/remove_dbg.rs b/crates/ra_assists/src/remove_dbg.rs
index 40f97a849..e9d0a635b 100644
--- a/crates/ra_assists/src/remove_dbg.rs
+++ b/crates/ra_assists/src/remove_dbg.rs
@@ -47,6 +47,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
47 }; 47 };
48 48
49 ctx.build("remove dbg!()", |edit| { 49 ctx.build("remove dbg!()", |edit| {
50 edit.target(macro_call.syntax().range());
50 edit.replace(macro_range, macro_content); 51 edit.replace(macro_range, macro_content);
51 edit.set_cursor(cursor_pos); 52 edit.set_cursor(cursor_pos);
52 }) 53 })
@@ -78,7 +79,7 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
78#[cfg(test)] 79#[cfg(test)]
79mod tests { 80mod tests {
80 use super::*; 81 use super::*;
81 use crate::helpers::{check_assist, check_assist_not_applicable}; 82 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
82 83
83 #[test] 84 #[test]
84 fn test_remove_dbg() { 85 fn test_remove_dbg() {
@@ -120,4 +121,19 @@ fn foo(n: usize) {
120 check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)"); 121 check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
121 check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7"); 122 check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
122 } 123 }
124
125 #[test]
126 fn remove_dbg_target() {
127 check_assist_target(
128 remove_dbg,
129 "
130fn foo(n: usize) {
131 if let Some(_) = dbg!(n.<|>checked_sub(4)) {
132 // ...
133 }
134}
135",
136 "dbg!(n.checked_sub(4))",
137 );
138 }
123} 139}
diff --git a/crates/ra_assists/src/replace_if_let_with_match.rs b/crates/ra_assists/src/replace_if_let_with_match.rs
index 683f0d119..a22ec5584 100644
--- a/crates/ra_assists/src/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/replace_if_let_with_match.rs
@@ -17,6 +17,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Opt
17 17
18 ctx.build("replace with match", |edit| { 18 ctx.build("replace with match", |edit| {
19 let match_expr = build_match_expr(expr, pat, then_block, else_block); 19 let match_expr = build_match_expr(expr, pat, then_block, else_block);
20 edit.target(if_expr.syntax().range());
20 edit.replace_node_and_indent(if_expr.syntax(), match_expr); 21 edit.replace_node_and_indent(if_expr.syntax(), match_expr);
21 edit.set_cursor(if_expr.syntax().range().start()) 22 edit.set_cursor(if_expr.syntax().range().start())
22 }) 23 })
@@ -46,7 +47,7 @@ fn format_arm(block: &ast::Block) -> String {
46#[cfg(test)] 47#[cfg(test)]
47mod tests { 48mod tests {
48 use super::*; 49 use super::*;
49 use crate::helpers::check_assist; 50 use crate::helpers::{check_assist, check_assist_target};
50 51
51 #[test] 52 #[test]
52 fn test_replace_if_let_with_match_unwraps_simple_expressions() { 53 fn test_replace_if_let_with_match_unwraps_simple_expressions() {
@@ -73,4 +74,26 @@ impl VariantData {
73} ", 74} ",
74 ) 75 )
75 } 76 }
77
78 #[test]
79 fn replace_if_let_with_match_target() {
80 check_assist_target(
81 replace_if_let_with_match,
82 "
83impl VariantData {
84 pub fn is_struct(&self) -> bool {
85 if <|>let VariantData::Struct(..) = *self {
86 true
87 } else {
88 false
89 }
90 }
91} ",
92 "if let VariantData::Struct(..) = *self {
93 true
94 } else {
95 false
96 }",
97 );
98 }
76} 99}
diff --git a/crates/ra_assists/src/split_import.rs b/crates/ra_assists/src/split_import.rs
index fb69cef9c..051bc6fec 100644
--- a/crates/ra_assists/src/split_import.rs
+++ b/crates/ra_assists/src/split_import.rs
@@ -24,6 +24,7 @@ pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
24 }; 24 };
25 25
26 ctx.build("split import", |edit| { 26 ctx.build("split import", |edit| {
27 edit.target(colon_colon.range());
27 edit.insert(l_curly, "{"); 28 edit.insert(l_curly, "{");
28 edit.insert(r_curly, "}"); 29 edit.insert(r_curly, "}");
29 edit.set_cursor(l_curly + TextUnit::of_str("{")); 30 edit.set_cursor(l_curly + TextUnit::of_str("{"));
@@ -33,7 +34,7 @@ pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
33#[cfg(test)] 34#[cfg(test)]
34mod tests { 35mod tests {
35 use super::*; 36 use super::*;
36 use crate::helpers::check_assist; 37 use crate::helpers::{check_assist, check_assist_target};
37 38
38 #[test] 39 #[test]
39 fn test_split_import() { 40 fn test_split_import() {
@@ -52,4 +53,9 @@ mod tests {
52 "use algo::{<|>visitor::{Visitor, visit}}", 53 "use algo::{<|>visitor::{Visitor, visit}}",
53 ) 54 )
54 } 55 }
56
57 #[test]
58 fn split_import_target() {
59 check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::");
60 }
55} 61}