mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-15 14:43:58 +00:00
Auto merge of #15788 - Young-Flash:import_anonymously, r=lnicola
feat: import trait with alias ![import_trait_with_alias](https://github.com/rust-lang/rust-analyzer/assets/71162630/81601160-fe55-46e3-ab8d-b2705e1aa696) cc `@Veykril` close https://github.com/rust-lang/rust-analyzer/issues/15684
This commit is contained in:
commit
99e94d2938
2 changed files with 276 additions and 12 deletions
|
@ -5,7 +5,7 @@ use ide_db::{
|
||||||
helpers::mod_path_to_ast,
|
helpers::mod_path_to_ast,
|
||||||
imports::{
|
imports::{
|
||||||
import_assets::{ImportAssets, ImportCandidate, LocatedImport},
|
import_assets::{ImportAssets, ImportCandidate, LocatedImport},
|
||||||
insert_use::{insert_use, ImportScope},
|
insert_use::{insert_use, insert_use_as_alias, ImportScope},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use syntax::{ast, AstNode, NodeOrToken, SyntaxElement};
|
use syntax::{ast, AstNode, NodeOrToken, SyntaxElement};
|
||||||
|
@ -129,10 +129,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
||||||
for import in proposed_imports {
|
for import in proposed_imports {
|
||||||
let import_path = import.import_path;
|
let import_path = import.import_path;
|
||||||
|
|
||||||
|
let (assist_id, import_name) =
|
||||||
|
(AssistId("auto_import", AssistKind::QuickFix), import_path.display(ctx.db()));
|
||||||
acc.add_group(
|
acc.add_group(
|
||||||
&group_label,
|
&group_label,
|
||||||
AssistId("auto_import", AssistKind::QuickFix),
|
assist_id,
|
||||||
format!("Import `{}`", import_path.display(ctx.db())),
|
format!("Import `{}`", import_name),
|
||||||
range,
|
range,
|
||||||
|builder| {
|
|builder| {
|
||||||
let scope = match scope.clone() {
|
let scope = match scope.clone() {
|
||||||
|
@ -143,6 +145,38 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
||||||
insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use);
|
insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
match import_assets.import_candidate() {
|
||||||
|
ImportCandidate::TraitAssocItem(name) | ImportCandidate::TraitMethod(name) => {
|
||||||
|
let is_method =
|
||||||
|
matches!(import_assets.import_candidate(), ImportCandidate::TraitMethod(_));
|
||||||
|
let type_ = if is_method { "method" } else { "item" };
|
||||||
|
let group_label = GroupLabel(format!(
|
||||||
|
"Import a trait for {} {} by alias",
|
||||||
|
type_,
|
||||||
|
name.assoc_item_name.text()
|
||||||
|
));
|
||||||
|
acc.add_group(
|
||||||
|
&group_label,
|
||||||
|
assist_id,
|
||||||
|
format!("Import `{} as _`", import_name),
|
||||||
|
range,
|
||||||
|
|builder| {
|
||||||
|
let scope = match scope.clone() {
|
||||||
|
ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
|
||||||
|
ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
|
||||||
|
ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
|
||||||
|
};
|
||||||
|
insert_use_as_alias(
|
||||||
|
&scope,
|
||||||
|
mod_path_to_ast(&import_path),
|
||||||
|
&ctx.config.insert_use,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
@ -253,7 +287,8 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::tests::{
|
use crate::tests::{
|
||||||
check_assist, check_assist_not_applicable, check_assist_target, TEST_CONFIG,
|
check_assist, check_assist_by_label, check_assist_not_applicable, check_assist_target,
|
||||||
|
TEST_CONFIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn check_auto_import_order(before: &str, order: &[&str]) {
|
fn check_auto_import_order(before: &str, order: &[&str]) {
|
||||||
|
@ -705,7 +740,7 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn associated_trait_function() {
|
fn associated_trait_function() {
|
||||||
check_assist(
|
check_assist_by_label(
|
||||||
auto_import,
|
auto_import,
|
||||||
r"
|
r"
|
||||||
mod test_mod {
|
mod test_mod {
|
||||||
|
@ -739,6 +774,44 @@ fn main() {
|
||||||
test_mod::TestStruct::test_function
|
test_mod::TestStruct::test_function
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
"Import `test_mod::TestTrait`",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn test_function();
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
fn test_function() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_mod::TestStruct::test_function$0
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use test_mod::TestTrait as _;
|
||||||
|
|
||||||
|
mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn test_function();
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
fn test_function() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_mod::TestStruct::test_function
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"Import `test_mod::TestTrait as _`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,7 +849,44 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn associated_trait_const() {
|
fn associated_trait_const() {
|
||||||
check_assist(
|
check_assist_by_label(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
const TEST_CONST: u8;
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
const TEST_CONST: u8 = 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_mod::TestStruct::TEST_CONST$0
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use test_mod::TestTrait as _;
|
||||||
|
|
||||||
|
mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
const TEST_CONST: u8;
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
const TEST_CONST: u8 = 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_mod::TestStruct::TEST_CONST
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"Import `test_mod::TestTrait as _`",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
auto_import,
|
auto_import,
|
||||||
r"
|
r"
|
||||||
mod test_mod {
|
mod test_mod {
|
||||||
|
@ -810,6 +920,7 @@ fn main() {
|
||||||
test_mod::TestStruct::TEST_CONST
|
test_mod::TestStruct::TEST_CONST
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
"Import `test_mod::TestTrait`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,7 +958,46 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trait_method() {
|
fn trait_method() {
|
||||||
check_assist(
|
check_assist_by_label(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn test_method(&self);
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
fn test_method(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let test_struct = test_mod::TestStruct {};
|
||||||
|
test_struct.test_meth$0od()
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use test_mod::TestTrait as _;
|
||||||
|
|
||||||
|
mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn test_method(&self);
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
fn test_method(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let test_struct = test_mod::TestStruct {};
|
||||||
|
test_struct.test_method()
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"Import `test_mod::TestTrait as _`",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
auto_import,
|
auto_import,
|
||||||
r"
|
r"
|
||||||
mod test_mod {
|
mod test_mod {
|
||||||
|
@ -883,12 +1033,43 @@ fn main() {
|
||||||
test_struct.test_method()
|
test_struct.test_method()
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
"Import `test_mod::TestTrait`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trait_method_cross_crate() {
|
fn trait_method_cross_crate() {
|
||||||
check_assist(
|
check_assist_by_label(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
//- /main.rs crate:main deps:dep
|
||||||
|
fn main() {
|
||||||
|
let test_struct = dep::test_mod::TestStruct {};
|
||||||
|
test_struct.test_meth$0od()
|
||||||
|
}
|
||||||
|
//- /dep.rs crate:dep
|
||||||
|
pub mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn test_method(&self);
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
fn test_method(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use dep::test_mod::TestTrait as _;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let test_struct = dep::test_mod::TestStruct {};
|
||||||
|
test_struct.test_method()
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"Import `dep::test_mod::TestTrait as _`",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
auto_import,
|
auto_import,
|
||||||
r"
|
r"
|
||||||
//- /main.rs crate:main deps:dep
|
//- /main.rs crate:main deps:dep
|
||||||
|
@ -915,12 +1096,41 @@ fn main() {
|
||||||
test_struct.test_method()
|
test_struct.test_method()
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
"Import `dep::test_mod::TestTrait`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn assoc_fn_cross_crate() {
|
fn assoc_fn_cross_crate() {
|
||||||
check_assist(
|
check_assist_by_label(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
//- /main.rs crate:main deps:dep
|
||||||
|
fn main() {
|
||||||
|
dep::test_mod::TestStruct::test_func$0tion
|
||||||
|
}
|
||||||
|
//- /dep.rs crate:dep
|
||||||
|
pub mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn test_function();
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
fn test_function() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use dep::test_mod::TestTrait as _;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dep::test_mod::TestStruct::test_function
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"Import `dep::test_mod::TestTrait as _`",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
auto_import,
|
auto_import,
|
||||||
r"
|
r"
|
||||||
//- /main.rs crate:main deps:dep
|
//- /main.rs crate:main deps:dep
|
||||||
|
@ -945,12 +1155,41 @@ fn main() {
|
||||||
dep::test_mod::TestStruct::test_function
|
dep::test_mod::TestStruct::test_function
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
"Import `dep::test_mod::TestTrait`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn assoc_const_cross_crate() {
|
fn assoc_const_cross_crate() {
|
||||||
check_assist(
|
check_assist_by_label(
|
||||||
|
auto_import,
|
||||||
|
r"
|
||||||
|
//- /main.rs crate:main deps:dep
|
||||||
|
fn main() {
|
||||||
|
dep::test_mod::TestStruct::CONST$0
|
||||||
|
}
|
||||||
|
//- /dep.rs crate:dep
|
||||||
|
pub mod test_mod {
|
||||||
|
pub trait TestTrait {
|
||||||
|
const CONST: bool;
|
||||||
|
}
|
||||||
|
pub struct TestStruct {}
|
||||||
|
impl TestTrait for TestStruct {
|
||||||
|
const CONST: bool = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
use dep::test_mod::TestTrait as _;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dep::test_mod::TestStruct::CONST
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"Import `dep::test_mod::TestTrait as _`",
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist_by_label(
|
||||||
auto_import,
|
auto_import,
|
||||||
r"
|
r"
|
||||||
//- /main.rs crate:main deps:dep
|
//- /main.rs crate:main deps:dep
|
||||||
|
@ -975,6 +1214,7 @@ fn main() {
|
||||||
dep::test_mod::TestStruct::CONST
|
dep::test_mod::TestStruct::CONST
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
"Import `dep::test_mod::TestTrait`",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use syntax::{
|
||||||
algo,
|
algo,
|
||||||
ast::{
|
ast::{
|
||||||
self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility,
|
self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility,
|
||||||
PathSegmentKind,
|
PathSegmentKind, UseTree,
|
||||||
},
|
},
|
||||||
ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode,
|
ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode,
|
||||||
};
|
};
|
||||||
|
@ -157,6 +157,29 @@ impl ImportScope {
|
||||||
|
|
||||||
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
|
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
|
||||||
pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
|
pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
|
||||||
|
insert_use_with_alias_option(scope, path, cfg, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
|
||||||
|
let text: &str = "use foo as _";
|
||||||
|
let parse = syntax::SourceFile::parse(text);
|
||||||
|
let node = parse
|
||||||
|
.tree()
|
||||||
|
.syntax()
|
||||||
|
.descendants()
|
||||||
|
.find_map(UseTree::cast)
|
||||||
|
.expect("Failed to make ast node `Rename`");
|
||||||
|
let alias = node.rename();
|
||||||
|
|
||||||
|
insert_use_with_alias_option(scope, path, cfg, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_use_with_alias_option(
|
||||||
|
scope: &ImportScope,
|
||||||
|
path: ast::Path,
|
||||||
|
cfg: &InsertUseConfig,
|
||||||
|
alias: Option<ast::Rename>,
|
||||||
|
) {
|
||||||
let _p = profile::span("insert_use");
|
let _p = profile::span("insert_use");
|
||||||
let mut mb = match cfg.granularity {
|
let mut mb = match cfg.granularity {
|
||||||
ImportGranularity::Crate => Some(MergeBehavior::Crate),
|
ImportGranularity::Crate => Some(MergeBehavior::Crate),
|
||||||
|
@ -176,7 +199,8 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let use_item =
|
let use_item =
|
||||||
make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update();
|
make::use_(None, make::use_tree(path.clone(), None, alias, false)).clone_for_update();
|
||||||
|
|
||||||
// merge into existing imports if possible
|
// merge into existing imports if possible
|
||||||
if let Some(mb) = mb {
|
if let Some(mb) = mb {
|
||||||
let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it));
|
let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it));
|
||||||
|
|
Loading…
Reference in a new issue