]> git.lizzy.rs Git - rust.git/commitdiff
Support auto-import in macro
authorEdwin Cheng <edwin0cheng@gmail.com>
Sat, 2 May 2020 19:24:55 +0000 (03:24 +0800)
committerEdwin Cheng <edwin0cheng@gmail.com>
Sat, 2 May 2020 19:24:55 +0000 (03:24 +0800)
crates/ra_assists/src/assist_ctx.rs
crates/ra_assists/src/handlers/auto_import.rs
crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
crates/ra_assists/src/utils.rs
crates/ra_assists/src/utils/insert_use.rs

index 2fe7c3de3d1424ae5fe927d4b7efde645fe2ea2c..da28800376363974df9f6cb1024577ffeaaf9f4a 100644 (file)
@@ -105,7 +105,7 @@ pub(crate) fn add_assist(
         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 @@ pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToke
     pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
         find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
     }
+
+    pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
+        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 @@ pub(crate) fn add_assist(
         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 @@ pub(crate) fn finish(self) -> Option<Assist> {
     }
 }
 
-#[derive(Default)]
-pub(crate) struct ActionBuilder {
+pub(crate) struct ActionBuilder<'a, 'b> {
     edit: TextEditBuilder,
     cursor_position: Option<TextSize>,
     target: Option<TextRange>,
     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<String>) {
         self.edit.replace(range, replace_with.into())
index 99682e023e92048fb513082dc6d2253411edf8d7..db6c4d2facdc577a4d0a006c73787c2865cc18d5 100644 (file)
@@ -45,15 +45,12 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
         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<Self> {
-        if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() {
+        if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
             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 @@ pub mod PubMod {
         );
     }
 
+    #[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(
index 918e8dd8dae5ce36abc80e41979c66101561af3b..ff2463c77f98155a9b3f0ec2100fe2e1eee72e3b 100644 (file)
@@ -38,7 +38,7 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist>
         "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
index efd9886978a59b8f9aedec2663cf0a13441a010f..6be704ce3af0c1f25b827a3733b8771421a7bb7e 100644 (file)
@@ -11,7 +11,7 @@
 };
 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<RootDatabase>,
index c507e71e08faf8ad9df03681e68a7228957bf9e4..c1f447efe7ab3e96e7f914981ca8ab6c65d7a197 100644 (file)
@@ -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},
 /// 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::<Vec<_>>();
-    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());
     }
 }