Support auto-import in macro

This commit is contained in:
Edwin Cheng 2020-05-03 03:24:55 +08:00
parent 89e1f97515
commit 2034002413
5 changed files with 66 additions and 19 deletions

View file

@ -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<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 @@ 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<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())

View 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 @@ 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(

View 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

View file

@ -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<RootDatabase>,

View 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},
@ -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::<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());
}
}