2020-02-02 06:06:51 -06:00
|
|
|
use hir::{db::HirDatabase, ModPath};
|
2019-12-23 18:19:09 -06:00
|
|
|
use ra_syntax::{
|
|
|
|
ast::{self, AstNode},
|
2020-02-02 15:24:43 -06:00
|
|
|
SyntaxKind::{NAME_REF, USE_ITEM},
|
2019-12-23 18:19:09 -06:00
|
|
|
SyntaxNode,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
assist_ctx::{ActionBuilder, Assist, AssistCtx},
|
|
|
|
auto_import_text_edit, AssistId, ImportsLocator,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Assist: auto_import
|
|
|
|
//
|
|
|
|
// If the name is unresolved, provides all possible imports for it.
|
|
|
|
//
|
|
|
|
// ```
|
|
|
|
// fn main() {
|
|
|
|
// let map = HashMap<|>::new();
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
// ->
|
|
|
|
// ```
|
|
|
|
// use std::collections::HashMap;
|
|
|
|
//
|
|
|
|
// fn main() {
|
|
|
|
// let map = HashMap<|>::new();
|
|
|
|
// }
|
|
|
|
// ```
|
2020-01-23 13:15:16 -06:00
|
|
|
pub(crate) fn auto_import<F: ImportsLocator>(
|
2019-12-23 18:19:09 -06:00
|
|
|
ctx: AssistCtx<impl HirDatabase>,
|
|
|
|
imports_locator: &mut F,
|
|
|
|
) -> Option<Assist> {
|
2020-01-29 06:57:44 -06:00
|
|
|
let path_to_import: ast::Path = ctx.find_node_at_offset()?;
|
|
|
|
let path_to_import_syntax = path_to_import.syntax();
|
|
|
|
if path_to_import_syntax.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() {
|
|
|
|
return None;
|
|
|
|
}
|
2020-02-02 15:24:43 -06:00
|
|
|
let name_to_import =
|
|
|
|
path_to_import_syntax.descendants().find(|child| child.kind() == NAME_REF)?;
|
2020-01-29 06:57:44 -06:00
|
|
|
|
|
|
|
let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast);
|
2019-12-23 18:19:09 -06:00
|
|
|
let position = match module.and_then(|it| it.item_list()) {
|
|
|
|
Some(item_list) => item_list.syntax().clone(),
|
|
|
|
None => {
|
2020-01-29 06:57:44 -06:00
|
|
|
let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?;
|
2019-12-23 18:19:09 -06:00
|
|
|
current_file.syntax().clone()
|
|
|
|
}
|
|
|
|
};
|
2020-01-24 01:33:18 -06:00
|
|
|
let source_analyzer = ctx.source_analyzer(&position, None);
|
|
|
|
let module_with_name_to_import = source_analyzer.module()?;
|
|
|
|
if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() {
|
|
|
|
return None;
|
|
|
|
}
|
2019-12-23 18:19:09 -06:00
|
|
|
|
2020-01-24 01:33:18 -06:00
|
|
|
let proposed_imports = imports_locator
|
2020-02-02 15:24:43 -06:00
|
|
|
.find_imports(&name_to_import.to_string())
|
2020-01-24 01:33:18 -06:00
|
|
|
.into_iter()
|
|
|
|
.filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
|
|
|
|
.filter(|use_path| !use_path.segments.is_empty())
|
|
|
|
.take(20)
|
2020-01-26 17:53:59 -06:00
|
|
|
.collect::<std::collections::BTreeSet<_>>();
|
2019-12-23 18:19:09 -06:00
|
|
|
if proposed_imports.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2020-01-31 13:04:52 -06:00
|
|
|
ctx.add_assist_group(
|
|
|
|
AssistId("auto_import"),
|
|
|
|
format!("Import {}", path_to_import_syntax),
|
|
|
|
|| {
|
|
|
|
proposed_imports
|
|
|
|
.into_iter()
|
|
|
|
.map(|import| import_to_action(import, &position, &path_to_import_syntax))
|
|
|
|
.collect()
|
|
|
|
},
|
|
|
|
)
|
2019-12-23 18:19:09 -06:00
|
|
|
}
|
|
|
|
|
2020-02-02 06:06:51 -06:00
|
|
|
fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
|
2019-12-23 18:19:09 -06:00
|
|
|
let mut action_builder = ActionBuilder::default();
|
|
|
|
action_builder.label(format!("Import `{}`", &import));
|
2020-02-02 06:06:51 -06:00
|
|
|
auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder());
|
2019-12-23 18:19:09 -06:00
|
|
|
action_builder
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::helpers::{
|
|
|
|
check_assist_with_imports_locator, check_assist_with_imports_locator_not_applicable,
|
2020-01-26 16:16:18 -06:00
|
|
|
TestImportsLocator,
|
2019-12-23 18:19:09 -06:00
|
|
|
};
|
|
|
|
|
2020-01-26 16:16:18 -06:00
|
|
|
#[test]
|
|
|
|
fn applicable_when_found_an_import() {
|
|
|
|
check_assist_with_imports_locator(
|
|
|
|
auto_import,
|
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
2020-01-29 06:57:44 -06:00
|
|
|
<|>PubStruct
|
2019-12-23 18:19:09 -06:00
|
|
|
|
2020-01-26 16:16:18 -06:00
|
|
|
pub mod PubMod {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
",
|
|
|
|
r"
|
2020-01-29 06:57:44 -06:00
|
|
|
<|>use PubMod::PubStruct;
|
2019-12-23 18:19:09 -06:00
|
|
|
|
2020-01-29 06:57:44 -06:00
|
|
|
PubStruct
|
2020-01-26 16:16:18 -06:00
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
pub struct PubStruct;
|
2019-12-23 18:19:09 -06:00
|
|
|
}
|
2020-01-26 16:16:18 -06:00
|
|
|
",
|
|
|
|
);
|
2019-12-23 18:19:09 -06:00
|
|
|
}
|
|
|
|
|
2020-02-02 02:28:16 -06:00
|
|
|
#[test]
|
|
|
|
fn auto_imports_are_merged() {
|
|
|
|
check_assist_with_imports_locator(
|
|
|
|
auto_import,
|
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
|
|
|
use PubMod::PubStruct1;
|
|
|
|
|
2020-02-02 15:24:43 -06:00
|
|
|
struct Test {
|
|
|
|
test: Pub<|>Struct2<u8>,
|
|
|
|
}
|
2020-02-02 02:28:16 -06:00
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
pub struct PubStruct1;
|
2020-02-02 15:24:43 -06:00
|
|
|
pub struct PubStruct2<T> {
|
|
|
|
_t: T,
|
|
|
|
}
|
2020-02-02 02:28:16 -06:00
|
|
|
}
|
|
|
|
",
|
|
|
|
r"
|
|
|
|
use PubMod::{PubStruct2, PubStruct1};
|
|
|
|
|
2020-02-02 15:24:43 -06:00
|
|
|
struct Test {
|
|
|
|
test: Pub<|>Struct2<u8>,
|
|
|
|
}
|
2020-02-02 02:28:16 -06:00
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
pub struct PubStruct1;
|
2020-02-02 15:24:43 -06:00
|
|
|
pub struct PubStruct2<T> {
|
|
|
|
_t: T,
|
|
|
|
}
|
2020-02-02 02:28:16 -06:00
|
|
|
}
|
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-12-23 18:19:09 -06:00
|
|
|
#[test]
|
2020-01-26 16:16:18 -06:00
|
|
|
fn applicable_when_found_multiple_imports() {
|
2019-12-23 18:19:09 -06:00
|
|
|
check_assist_with_imports_locator(
|
|
|
|
auto_import,
|
2020-01-26 16:16:18 -06:00
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
2020-01-29 06:57:44 -06:00
|
|
|
PubSt<|>ruct
|
2020-01-26 16:16:18 -06:00
|
|
|
|
|
|
|
pub mod PubMod1 {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
pub mod PubMod2 {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
pub mod PubMod3 {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
",
|
|
|
|
r"
|
|
|
|
use PubMod1::PubStruct;
|
|
|
|
|
2020-01-29 06:57:44 -06:00
|
|
|
PubSt<|>ruct
|
2020-01-26 16:16:18 -06:00
|
|
|
|
|
|
|
pub mod PubMod1 {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
pub mod PubMod2 {
|
|
|
|
pub struct PubStruct;
|
2019-12-23 18:19:09 -06:00
|
|
|
}
|
2020-01-26 16:16:18 -06:00
|
|
|
pub mod PubMod3 {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn not_applicable_for_already_imported_types() {
|
|
|
|
check_assist_with_imports_locator_not_applicable(
|
|
|
|
auto_import,
|
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
|
|
|
use PubMod::PubStruct;
|
|
|
|
|
|
|
|
PubStruct<|>
|
2019-12-23 18:19:09 -06:00
|
|
|
|
2020-01-26 16:16:18 -06:00
|
|
|
pub mod PubMod {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}
|
|
|
|
",
|
2019-12-23 18:19:09 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-01-26 16:16:18 -06:00
|
|
|
fn not_applicable_for_types_with_private_paths() {
|
2019-12-23 18:19:09 -06:00
|
|
|
check_assist_with_imports_locator_not_applicable(
|
|
|
|
auto_import,
|
2020-01-26 16:16:18 -06:00
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
|
|
|
PrivateStruct<|>
|
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
struct PrivateStruct;
|
2019-12-23 18:19:09 -06:00
|
|
|
}
|
2020-01-26 16:16:18 -06:00
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
2019-12-23 18:19:09 -06:00
|
|
|
|
2020-01-26 16:16:18 -06:00
|
|
|
#[test]
|
|
|
|
fn not_applicable_when_no_imports_found() {
|
|
|
|
check_assist_with_imports_locator_not_applicable(
|
|
|
|
auto_import,
|
|
|
|
TestImportsLocator::new,
|
|
|
|
"
|
|
|
|
PubStruct<|>",
|
2019-12-23 18:19:09 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn not_applicable_in_import_statements() {
|
|
|
|
check_assist_with_imports_locator_not_applicable(
|
|
|
|
auto_import,
|
2020-01-26 16:16:18 -06:00
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
|
|
|
use PubStruct<|>;
|
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
pub struct PubStruct;
|
|
|
|
}",
|
2019-12-23 18:19:09 -06:00
|
|
|
);
|
|
|
|
}
|
2020-02-01 14:13:02 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn function_import() {
|
|
|
|
check_assist_with_imports_locator(
|
|
|
|
auto_import,
|
|
|
|
TestImportsLocator::new,
|
|
|
|
r"
|
|
|
|
test_function<|>
|
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
pub fn test_function() {};
|
|
|
|
}
|
|
|
|
",
|
|
|
|
r"
|
|
|
|
use PubMod::test_function;
|
|
|
|
|
|
|
|
test_function<|>
|
|
|
|
|
|
|
|
pub mod PubMod {
|
|
|
|
pub fn test_function() {};
|
|
|
|
}
|
|
|
|
",
|
|
|
|
);
|
|
|
|
}
|
2019-12-23 18:19:09 -06:00
|
|
|
}
|