diff options
| author | Edwin Cheng <[email protected]> | 2020-05-02 20:24:55 +0100 |
|---|---|---|
| committer | Edwin Cheng <[email protected]> | 2020-05-02 20:24:55 +0100 |
| commit | 2034002413a62fb999d9372231905393ed5c0383 (patch) | |
| tree | 2eb19def576aef6814f4a3275e563883394b6775 | |
| parent | 89e1f97515c36ab97bd378d972cabec0feb6d77e (diff) | |
Support auto-import in macro
| -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 | ||
