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()
};
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)
}
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()
};
}
}
-#[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())
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()
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)
}
}
);
}
+ #[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(
"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
};
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>,
// 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());
}
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());
}
}