aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-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}