diff options
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/add_derive.rs | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 13 | ||||
-rw-r--r-- | crates/ra_assists/src/change_visibility.rs | 13 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 40 | ||||
-rw-r--r-- | crates/ra_assists/src/split_import.rs | 1 |
6 files changed, 60 insertions, 10 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..de33b356c 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 | } |
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..73dd8319f 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), parent.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()), field.syntax().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 | }); |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 881db6347..fc4e95303 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,26 @@ 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_unstable_by(|a, b| match a { | ||
69 | // Some(y) < Some(x) < None for y < x | ||
70 | (_, AssistAction { target: Some(a), .. }) => match b { | ||
71 | (_, AssistAction { target: Some(b), .. }) => a.len().cmp(&b.len()), | ||
72 | _ => Ordering::Less, | ||
73 | }, | ||
74 | _ => Ordering::Greater, | ||
75 | }); | ||
76 | a | ||
65 | }) | 77 | }) |
66 | } | 78 | } |
67 | 79 | ||
@@ -162,5 +174,27 @@ mod helpers { | |||
162 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); | 174 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); |
163 | assert!(assist.is_none()); | 175 | assert!(assist.is_none()); |
164 | } | 176 | } |
177 | } | ||
178 | |||
179 | #[cfg(test)] | ||
180 | mod tests { | ||
181 | use hir::mock::MockDatabase; | ||
182 | use ra_syntax::TextRange; | ||
183 | use ra_db::FileRange; | ||
184 | use test_utils::extract_offset; | ||
185 | |||
186 | #[test] | ||
187 | fn assist_order() { | ||
188 | let before = "struct Foo { <|>bar: u32 }"; | ||
189 | let (before_cursor_pos, before) = extract_offset(before); | ||
190 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | ||
191 | let frange = | ||
192 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | ||
193 | let assists = super::assists(&db, frange); | ||
194 | let mut assists = assists.iter(); | ||
195 | |||
196 | assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)"); | ||
197 | assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`"); | ||
198 | } | ||
165 | 199 | ||
166 | } | 200 | } |
diff --git a/crates/ra_assists/src/split_import.rs b/crates/ra_assists/src/split_import.rs index fb69cef9c..287c05830 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("{")); |