diff options
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/add_derive.rs | 20 | ||||
-rw-r--r-- | crates/ra_assists/src/add_impl.rs | 17 | ||||
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 13 | ||||
-rw-r--r-- | crates/ra_assists/src/change_visibility.rs | 22 | ||||
-rw-r--r-- | crates/ra_assists/src/fill_match_arms.rs | 18 | ||||
-rw-r--r-- | crates/ra_assists/src/flip_comma.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/introduce_variable.rs | 35 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 100 | ||||
-rw-r--r-- | crates/ra_assists/src/remove_dbg.rs | 18 | ||||
-rw-r--r-- | crates/ra_assists/src/replace_if_let_with_match.rs | 25 | ||||
-rw-r--r-- | crates/ra_assists/src/split_import.rs | 8 |
12 files changed, 264 insertions, 22 deletions
diff --git a/Cargo.lock b/Cargo.lock index c330115b8..5ce4a8084 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1,3 +1,5 @@ | |||
1 | # This file is automatically @generated by Cargo. | ||
2 | # It is not intended for manual editing. | ||
1 | [[package]] | 3 | [[package]] |
2 | name = "aho-corasick" | 4 | name = "aho-corasick" |
3 | version = "0.6.9" | 5 | version = "0.6.9" |
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)] |
39 | mod tests { | 40 | mod 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 | " | ||
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 | } | ||
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)] |
38 | mod tests { | 39 | mod 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 | " | ||
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 | } | ||
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> { | |||
106 | pub(crate) struct AssistBuilder { | 106 | pub(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 | ||
111 | impl AssistBuilder { | 112 | impl 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 | ||
61 | fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> { | 62 | fn 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)] |
78 | mod tests { | 81 | mod 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)] |
74 | mod tests { | 75 | mod 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> { | |||
20 | mod tests { | 21 | mod 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 | |||
58 | fn valid_covering_node(node: &SyntaxNode) -> bool { | 59 | fn 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. |
63 | fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> { | 64 | fn 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. |
78 | fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> { | 79 | fn 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)] |
101 | mod tests { | 102 | mod 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 | " | ||
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 | } | ||
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 @@ | |||
8 | mod assist_ctx; | 8 | mod assist_ctx; |
9 | 9 | ||
10 | use ra_text_edit::TextEdit; | 10 | use ra_text_edit::TextEdit; |
11 | use ra_syntax::{TextUnit, SyntaxNode, Direction}; | 11 | use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction}; |
12 | use ra_db::FileRange; | 12 | use ra_db::FileRange; |
13 | use hir::db::HirDatabase; | 13 | use hir::db::HirDatabase; |
14 | 14 | ||
@@ -23,6 +23,7 @@ pub struct AssistLabel { | |||
23 | pub struct AssistAction { | 23 | pub 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)> | |||
53 | where | 54 | where |
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)] | ||
217 | mod 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)] |
79 | mod tests { | 80 | mod 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 | " | ||
130 | fn 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)] |
47 | mod tests { | 48 | mod 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 | " | ||
83 | impl 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)] |
34 | mod tests { | 35 | mod 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 | } |