mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-14 08:57:34 +00:00
Support auto-import in macro
This commit is contained in:
parent
89e1f97515
commit
2034002413
5 changed files with 66 additions and 19 deletions
|
@ -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())
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue