From 2034002413a62fb999d9372231905393ed5c0383 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 3 May 2020 03:24:55 +0800 Subject: Support auto-import in macro --- crates/ra_assists/src/assist_ctx.rs | 30 +++++++++++++--- crates/ra_assists/src/handlers/auto_import.rs | 42 +++++++++++++++++----- .../handlers/replace_qualified_name_with_use.rs | 2 +- crates/ra_assists/src/utils.rs | 2 +- crates/ra_assists/src/utils/insert_use.rs | 9 ++--- 5 files changed, 66 insertions(+), 19 deletions(-) (limited to 'crates/ra_assists') 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> { let mut info = AssistInfo::new(label); if self.should_compute_edit { let action = { - let mut edit = ActionBuilder::default(); + let mut edit = ActionBuilder::new(&self); f(&mut edit); edit.build() }; @@ -130,6 +130,12 @@ impl<'a> AssistCtx<'a> { pub(crate) fn find_node_at_offset(&self) -> Option { find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) } + + pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { + self.sema + .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) + } + pub(crate) fn covering_element(&self) -> SyntaxElement { find_covering_element(self.source_file.syntax(), self.frange.range) } @@ -156,7 +162,7 @@ impl<'a> AssistGroup<'a> { let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone())); if self.ctx.should_compute_edit { let action = { - let mut edit = ActionBuilder::default(); + let mut edit = ActionBuilder::new(&self.ctx); f(&mut edit); edit.build() }; @@ -175,15 +181,29 @@ impl<'a> AssistGroup<'a> { } } -#[derive(Default)] -pub(crate) struct ActionBuilder { +pub(crate) struct ActionBuilder<'a, 'b> { edit: TextEditBuilder, cursor_position: Option, target: Option, file: AssistFile, + ctx: &'a AssistCtx<'b>, } -impl ActionBuilder { +impl<'a, 'b> ActionBuilder<'a, 'b> { + fn new(ctx: &'a AssistCtx<'b>) -> Self { + Self { + edit: TextEditBuilder::default(), + cursor_position: None, + target: None, + file: AssistFile::default(), + ctx, + } + } + + pub(crate) fn ctx(&self) -> &AssistCtx<'b> { + &self.ctx + } + /// Replaces specified `range` of text with a given string. pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { 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 { return None; } + let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); for import in proposed_imports { group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { - edit.target(auto_import_assets.syntax_under_caret.text_range()); - insert_use_statement( - &auto_import_assets.syntax_under_caret, - &import, - edit.text_edit_builder(), - ); + edit.target(range); + insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit); }); } group.finish() @@ -68,10 +65,10 @@ struct AutoImportAssets { impl AutoImportAssets { fn new(ctx: &AssistCtx) -> Option { - if let Some(path_under_caret) = ctx.find_node_at_offset::() { + if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::() { Self::for_regular_path(path_under_caret, &ctx) } else { - Self::for_method_call(ctx.find_node_at_offset()?, &ctx) + Self::for_method_call(ctx.find_node_at_offset_with_descend()?, &ctx) } } @@ -305,6 +302,35 @@ mod tests { ); } + #[test] + fn applicable_when_found_an_import_in_macros() { + check_assist( + auto_import, + r" + macro_rules! foo { + ($i:ident) => { fn foo(a: $i) {} } + } + foo!(Pub<|>Struct); + + pub mod PubMod { + pub struct PubStruct; + } + ", + r" + use PubMod::PubStruct; + + macro_rules! foo { + ($i:ident) => { fn foo(a: $i) {} } + } + foo!(Pub<|>Struct); + + pub mod PubMod { + pub struct PubStruct; + } + ", + ); + } + #[test] fn auto_imports_are_merged() { check_assist( 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 "Replace qualified path with use", |edit| { let path_to_import = hir_path.mod_path().clone(); - insert_use_statement(path.syntax(), &path_to_import, edit.text_edit_builder()); + insert_use_statement(path.syntax(), &path_to_import, edit); if let Some(last) = path.segment() { // 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::{ }; use rustc_hash::FxHashSet; -pub use insert_use::insert_use_statement; +pub(crate) use insert_use::insert_use_statement; pub fn get_missing_impl_items( sema: &Semantics, 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 @@ // FIXME: rewrite according to the plan, outlined in // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 +use crate::assist_ctx::ActionBuilder; use hir::{self, ModPath}; use ra_syntax::{ ast::{self, NameOwner}, @@ -14,14 +15,14 @@ use ra_text_edit::TextEditBuilder; /// Creates and inserts a use statement for the given path to import. /// The use statement is inserted in the scope most appropriate to the /// the cursor position given, additionally merged with the existing use imports. -pub fn insert_use_statement( +pub(crate) fn insert_use_statement( // Ideally the position of the cursor, used to position: &SyntaxNode, path_to_import: &ModPath, - edit: &mut TextEditBuilder, + edit: &mut ActionBuilder, ) { let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::>(); - let container = position.ancestors().find_map(|n| { + let container = edit.ctx().sema.ancestors_with_macros(position.clone()).find_map(|n| { if let Some(module) = ast::Module::cast(n.clone()) { return module.item_list().map(|it| it.syntax().clone()); } @@ -30,7 +31,7 @@ pub fn insert_use_statement( if let Some(container) = container { let action = best_action_for_target(container, position.clone(), &target); - make_assist(&action, &target, edit); + make_assist(&action, &target, edit.text_edit_builder()); } } -- cgit v1.2.3