Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support auto-import in macro #4268

Merged
merged 1 commit into from May 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 25 additions & 5 deletions crates/ra_assists/src/assist_ctx.rs
Expand Up @@ -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()
};
Expand All @@ -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)
}
Expand All @@ -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()
};
Expand All @@ -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())
Expand Down
42 changes: 34 additions & 8 deletions crates/ra_assists/src/handlers/auto_import.rs
Expand Up @@ -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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't get the AssistContext in this closure, so I pass the ctx to ActionBuilder. Another option is passing the ctx directly as closure arguments, but I don't want to change too much code. I will prefer to refactor it in other PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently trying to implement #1062 and looks like I will have to change it in order to use the insert_use_statement in ra_ide.

So give it a couple of weeks before doing the refactoring :)

insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit);
});
}
group.finish()
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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(
Expand Down
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion crates/ra_assists/src/utils.rs
Expand Up @@ -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>,
Expand Down
9 changes: 5 additions & 4 deletions crates/ra_assists/src/utils/insert_use.rs
Expand Up @@ -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},
Expand All @@ -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());
}
Expand All @@ -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());
}
}

Expand Down