diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-05-03 23:52:10 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-05-03 23:52:10 +0100 |
commit | 6a48a94d47bfd6a340cbdb3f68b5bce4167c8168 (patch) | |
tree | 9f349e75c0dc35364896c0887d9e335f822c0f43 /crates/ra_assists/src | |
parent | baa35b02b2f1a197beec65bcd68f56ffb89abfa1 (diff) | |
parent | 2034002413a62fb999d9372231905393ed5c0383 (diff) |
Merge #4268
4268: Support auto-import in macro r=SomeoneToIgnore a=edwin0cheng
Fixed: #3854
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_assists/src')
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 30 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 42 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/utils.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/utils/insert_use.rs | 9 |
5 files changed, 66 insertions, 19 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 2fe7c3de3..da2880037 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -105,7 +105,7 @@ impl<'a> AssistCtx<'a> { | |||
105 | let mut info = AssistInfo::new(label); | 105 | let mut info = AssistInfo::new(label); |
106 | if self.should_compute_edit { | 106 | if self.should_compute_edit { |
107 | let action = { | 107 | let action = { |
108 | let mut edit = ActionBuilder::default(); | 108 | let mut edit = ActionBuilder::new(&self); |
109 | f(&mut edit); | 109 | f(&mut edit); |
110 | edit.build() | 110 | edit.build() |
111 | }; | 111 | }; |
@@ -130,6 +130,12 @@ impl<'a> AssistCtx<'a> { | |||
130 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { | 130 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { |
131 | find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) | 131 | find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) |
132 | } | 132 | } |
133 | |||
134 | pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { | ||
135 | self.sema | ||
136 | .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) | ||
137 | } | ||
138 | |||
133 | pub(crate) fn covering_element(&self) -> SyntaxElement { | 139 | pub(crate) fn covering_element(&self) -> SyntaxElement { |
134 | find_covering_element(self.source_file.syntax(), self.frange.range) | 140 | find_covering_element(self.source_file.syntax(), self.frange.range) |
135 | } | 141 | } |
@@ -156,7 +162,7 @@ impl<'a> AssistGroup<'a> { | |||
156 | let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); | 162 | let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); |
157 | if self.ctx.should_compute_edit { | 163 | if self.ctx.should_compute_edit { |
158 | let action = { | 164 | let action = { |
159 | let mut edit = ActionBuilder::default(); | 165 | let mut edit = ActionBuilder::new(&self.ctx); |
160 | f(&mut edit); | 166 | f(&mut edit); |
161 | edit.build() | 167 | edit.build() |
162 | }; | 168 | }; |
@@ -175,15 +181,29 @@ impl<'a> AssistGroup<'a> { | |||
175 | } | 181 | } |
176 | } | 182 | } |
177 | 183 | ||
178 | #[derive(Default)] | 184 | pub(crate) struct ActionBuilder<'a, 'b> { |
179 | pub(crate) struct ActionBuilder { | ||
180 | edit: TextEditBuilder, | 185 | edit: TextEditBuilder, |
181 | cursor_position: Option<TextSize>, | 186 | cursor_position: Option<TextSize>, |
182 | target: Option<TextRange>, | 187 | target: Option<TextRange>, |
183 | file: AssistFile, | 188 | file: AssistFile, |
189 | ctx: &'a AssistCtx<'b>, | ||
184 | } | 190 | } |
185 | 191 | ||
186 | impl ActionBuilder { | 192 | impl<'a, 'b> ActionBuilder<'a, 'b> { |
193 | fn new(ctx: &'a AssistCtx<'b>) -> Self { | ||
194 | Self { | ||
195 | edit: TextEditBuilder::default(), | ||
196 | cursor_position: None, | ||
197 | target: None, | ||
198 | file: AssistFile::default(), | ||
199 | ctx, | ||
200 | } | ||
201 | } | ||
202 | |||
203 | pub(crate) fn ctx(&self) -> &AssistCtx<'b> { | ||
204 | &self.ctx | ||
205 | } | ||
206 | |||
187 | /// Replaces specified `range` of text with a given string. | 207 | /// Replaces specified `range` of text with a given string. |
188 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | 208 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { |
189 | self.edit.replace(range, replace_with.into()) | 209 | self.edit.replace(range, replace_with.into()) |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 99682e023..db6c4d2fa 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -45,15 +45,12 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | |||
45 | return None; | 45 | return None; |
46 | } | 46 | } |
47 | 47 | ||
48 | let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; | ||
48 | let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); | 49 | let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); |
49 | for import in proposed_imports { | 50 | for import in proposed_imports { |
50 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { | 51 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { |
51 | edit.target(auto_import_assets.syntax_under_caret.text_range()); | 52 | edit.target(range); |
52 | insert_use_statement( | 53 | insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit); |
53 | &auto_import_assets.syntax_under_caret, | ||
54 | &import, | ||
55 | edit.text_edit_builder(), | ||
56 | ); | ||
57 | }); | 54 | }); |
58 | } | 55 | } |
59 | group.finish() | 56 | group.finish() |
@@ -68,10 +65,10 @@ struct AutoImportAssets { | |||
68 | 65 | ||
69 | impl AutoImportAssets { | 66 | impl AutoImportAssets { |
70 | fn new(ctx: &AssistCtx) -> Option<Self> { | 67 | fn new(ctx: &AssistCtx) -> Option<Self> { |
71 | if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() { | 68 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { |
72 | Self::for_regular_path(path_under_caret, &ctx) | 69 | Self::for_regular_path(path_under_caret, &ctx) |
73 | } else { | 70 | } else { |
74 | Self::for_method_call(ctx.find_node_at_offset()?, &ctx) | 71 | Self::for_method_call(ctx.find_node_at_offset_with_descend()?, &ctx) |
75 | } | 72 | } |
76 | } | 73 | } |
77 | 74 | ||
@@ -306,6 +303,35 @@ mod tests { | |||
306 | } | 303 | } |
307 | 304 | ||
308 | #[test] | 305 | #[test] |
306 | fn applicable_when_found_an_import_in_macros() { | ||
307 | check_assist( | ||
308 | auto_import, | ||
309 | r" | ||
310 | macro_rules! foo { | ||
311 | ($i:ident) => { fn foo(a: $i) {} } | ||
312 | } | ||
313 | foo!(Pub<|>Struct); | ||
314 | |||
315 | pub mod PubMod { | ||
316 | pub struct PubStruct; | ||
317 | } | ||
318 | ", | ||
319 | r" | ||
320 | use PubMod::PubStruct; | ||
321 | |||
322 | macro_rules! foo { | ||
323 | ($i:ident) => { fn foo(a: $i) {} } | ||
324 | } | ||
325 | foo!(Pub<|>Struct); | ||
326 | |||
327 | pub mod PubMod { | ||
328 | pub struct PubStruct; | ||
329 | } | ||
330 | ", | ||
331 | ); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
309 | fn auto_imports_are_merged() { | 335 | fn auto_imports_are_merged() { |
310 | check_assist( | 336 | check_assist( |
311 | auto_import, | 337 | auto_import, |
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index 918e8dd8d..ff2463c77 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -38,7 +38,7 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> | |||
38 | "Replace qualified path with use", | 38 | "Replace qualified path with use", |
39 | |edit| { | 39 | |edit| { |
40 | let path_to_import = hir_path.mod_path().clone(); | 40 | let path_to_import = hir_path.mod_path().clone(); |
41 | insert_use_statement(path.syntax(), &path_to_import, edit.text_edit_builder()); | 41 | insert_use_statement(path.syntax(), &path_to_import, edit); |
42 | 42 | ||
43 | if let Some(last) = path.segment() { | 43 | if let Some(last) = path.segment() { |
44 | // Here we are assuming the assist will provide a correct use statement | 44 | // Here we are assuming the assist will provide a correct use statement |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index efd988697..6be704ce3 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | }; | 11 | }; |
12 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
13 | 13 | ||
14 | pub use insert_use::insert_use_statement; | 14 | pub(crate) use insert_use::insert_use_statement; |
15 | 15 | ||
16 | pub fn get_missing_impl_items( | 16 | pub fn get_missing_impl_items( |
17 | sema: &Semantics<RootDatabase>, | 17 | sema: &Semantics<RootDatabase>, |
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index c507e71e0..c1f447efe 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | // FIXME: rewrite according to the plan, outlined in | 2 | // FIXME: rewrite according to the plan, outlined in |
3 | // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 | 3 | // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 |
4 | 4 | ||
5 | use crate::assist_ctx::ActionBuilder; | ||
5 | use hir::{self, ModPath}; | 6 | use hir::{self, ModPath}; |
6 | use ra_syntax::{ | 7 | use ra_syntax::{ |
7 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
@@ -14,14 +15,14 @@ use ra_text_edit::TextEditBuilder; | |||
14 | /// Creates and inserts a use statement for the given path to import. | 15 | /// Creates and inserts a use statement for the given path to import. |
15 | /// The use statement is inserted in the scope most appropriate to the | 16 | /// The use statement is inserted in the scope most appropriate to the |
16 | /// the cursor position given, additionally merged with the existing use imports. | 17 | /// the cursor position given, additionally merged with the existing use imports. |
17 | pub fn insert_use_statement( | 18 | pub(crate) fn insert_use_statement( |
18 | // Ideally the position of the cursor, used to | 19 | // Ideally the position of the cursor, used to |
19 | position: &SyntaxNode, | 20 | position: &SyntaxNode, |
20 | path_to_import: &ModPath, | 21 | path_to_import: &ModPath, |
21 | edit: &mut TextEditBuilder, | 22 | edit: &mut ActionBuilder, |
22 | ) { | 23 | ) { |
23 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); | 24 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); |
24 | let container = position.ancestors().find_map(|n| { | 25 | let container = edit.ctx().sema.ancestors_with_macros(position.clone()).find_map(|n| { |
25 | if let Some(module) = ast::Module::cast(n.clone()) { | 26 | if let Some(module) = ast::Module::cast(n.clone()) { |
26 | return module.item_list().map(|it| it.syntax().clone()); | 27 | return module.item_list().map(|it| it.syntax().clone()); |
27 | } | 28 | } |
@@ -30,7 +31,7 @@ pub fn insert_use_statement( | |||
30 | 31 | ||
31 | if let Some(container) = container { | 32 | if let Some(container) = container { |
32 | let action = best_action_for_target(container, position.clone(), &target); | 33 | let action = best_action_for_target(container, position.clone(), &target); |
33 | make_assist(&action, &target, edit); | 34 | make_assist(&action, &target, edit.text_edit_builder()); |
34 | } | 35 | } |
35 | } | 36 | } |
36 | 37 | ||