814: auto_import: import in enclosing module by default r=matklad a=eulerdisk

Simpler version of #795 

Co-authored-by: Andrea Pretto <eulerdisk@gmail.com>
This commit is contained in:
bors[bot] 2019-02-13 08:26:34 +00:00
commit 74d03d57e7

View file

@ -1,6 +1,6 @@
use hir::db::HirDatabase;
use ra_syntax::{
ast, AstNode, SyntaxNode, Direction, TextRange,
ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange,
SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA }
};
use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder};
@ -345,9 +345,9 @@ fn best_action_for_target<'b, 'a: 'b>(
match best_action {
Some(action) => return action,
None => {
// We have no action we no use item was found in container so we find
// We have no action and no UseItem was found in container so we find
// another item and we use it as anchor.
// If there are not items, we choose the target path itself as anchor.
// If there are no items, we choose the target path itself as anchor.
let anchor = container
.children()
.find_map(ast::ModuleItem::cast)
@ -480,6 +480,24 @@ fn make_assist_add_nested_import(
}
}
fn apply_auto_import<'a>(
container: &SyntaxNode,
path: &ast::Path,
target: &[&'a ast::PathSegment],
edit: &mut AssistBuilder,
) {
let action = best_action_for_target(container, path, target);
make_assist(&action, target, edit);
if let (Some(first), Some(last)) = (target.first(), target.last()) {
// Here we are assuming the assist will provide a correct use statement
// so we can delete the path qualifier
edit.delete(TextRange::from_to(
first.syntax().range().start(),
last.syntax().range().start(),
));
}
}
pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let node = ctx.covering_node();
let current_file = node.ancestors().find_map(ast::SourceFile::cast)?;
@ -495,18 +513,20 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
return None;
}
ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| {
let action = best_action_for_target(current_file.syntax(), path, &segments);
make_assist(&action, segments.as_slice(), edit);
if let Some(last_segment) = path.segment() {
// Here we are assuming the assist will provide a correct use statement
// so we can delete the path qualifier
edit.delete(TextRange::from_to(
path.syntax().range().start(),
last_segment.syntax().range().start(),
));
if let Some(module) = path.syntax().ancestors().find_map(ast::Module::cast) {
if let (Some(item_list), Some(name)) = (module.item_list(), module.name()) {
ctx.add_action(
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|edit| {
apply_auto_import(item_list.syntax(), path, &segments, edit);
},
);
}
});
} else {
ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| {
apply_auto_import(current_file.syntax(), path, &segments, edit);
});
}
ctx.build()
}
@ -531,6 +551,21 @@ Debug<|>
);
}
#[test]
fn test_auto_import_file_add_use_no_anchor_2seg() {
check_assist(
auto_import,
"
std::fmt<|>::Debug
",
"
use std::fmt;
fmt<|>::Debug
",
);
}
#[test]
fn test_auto_import_file_add_use() {
check_assist(
@ -728,4 +763,37 @@ impl foo<|> for Foo {
",
);
}
#[test]
fn test_auto_import_not_applicable_in_use() {
check_assist_not_applicable(
auto_import,
"
use std::fmt<|>;
",
);
}
#[test]
fn test_auto_import_file_add_use_no_anchor_in_mod_mod() {
check_assist(
auto_import,
"
mod foo {
mod bar {
std::fmt::Debug<|>
}
}
",
"
mod foo {
mod bar {
use std::fmt::Debug;
Debug<|>
}
}
",
);
}
}