From 4f5f7e280025783a0c9d3f82f4a4cb989255b7ca Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Sun, 22 Oct 2023 21:39:00 +0800 Subject: [PATCH 1/3] feat: import trait with alias --- .../ide-assists/src/handlers/auto_import.rs | 52 +++++++++++++++---- crates/ide-db/src/imports/insert_use.rs | 37 +++++++++++-- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index 7acf2ea0a0..a2efa4d10b 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -5,7 +5,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::{ 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}; @@ -129,10 +129,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< for import in proposed_imports { let import_path = import.import_path; + let (assist_id, import_name) = + (AssistId("auto_import", AssistKind::QuickFix), import_path.display(ctx.db())); acc.add_group( &group_label, - AssistId("auto_import", AssistKind::QuickFix), - format!("Import `{}`", import_path.display(ctx.db())), + assist_id, + format!("Import `{}`", import_name), range, |builder| { 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); }, ); + + 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(()) } @@ -723,7 +757,7 @@ fn main() { } ", r" - use test_mod::TestTrait; + use test_mod::TestTrait as _; mod test_mod { pub trait TestTrait { @@ -794,7 +828,7 @@ fn main() { } ", r" - use test_mod::TestTrait; + use test_mod::TestTrait as _; mod test_mod { pub trait TestTrait { @@ -866,7 +900,7 @@ fn main() { } ", r" - use test_mod::TestTrait; + use test_mod::TestTrait as _; mod test_mod { pub trait TestTrait { @@ -908,7 +942,7 @@ fn main() { } ", r" - use dep::test_mod::TestTrait; + use dep::test_mod::TestTrait as _; fn main() { let test_struct = dep::test_mod::TestStruct {}; @@ -939,7 +973,7 @@ fn main() { } ", r" - use dep::test_mod::TestTrait; + use dep::test_mod::TestTrait as _; fn main() { dep::test_mod::TestStruct::test_function @@ -969,7 +1003,7 @@ fn main() { } ", r" - use dep::test_mod::TestTrait; + use dep::test_mod::TestTrait as _; fn main() { dep::test_mod::TestStruct::CONST diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index 9be1d36634..6caa6e3511 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -9,7 +9,7 @@ use syntax::{ algo, ast::{ self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, - PathSegmentKind, + PathSegmentKind, Rename, UseTree, }, ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode, }; @@ -157,6 +157,33 @@ 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. 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 = match parse.tree().syntax().descendants().find_map(UseTree::cast) { + Some(it) => it, + None => { + panic!( + "Failed to make ast node `{}` from text {}", + std::any::type_name::(), + text + ) + } + }; + 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, +) { let _p = profile::span("insert_use"); let mut mb = match cfg.granularity { ImportGranularity::Crate => Some(MergeBehavior::Crate), @@ -175,8 +202,12 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { }; } - let use_item = - make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); + let use_item = if alias.is_some() { + make::use_(None, make::use_tree(path.clone(), None, alias, false)).clone_for_update() + } else { + make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update() + }; + // merge into existing imports if possible if let Some(mb) = mb { let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); From a723acf3469e4585a3da2b230e67afe4683d92a2 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Tue, 31 Oct 2023 21:03:26 +0800 Subject: [PATCH 2/3] simplify the code --- crates/ide-db/src/imports/insert_use.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index 6caa6e3511..a0cfd3836d 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -9,7 +9,7 @@ use syntax::{ algo, ast::{ self, edit_in_place::Removable, make, AstNode, HasAttrs, HasModuleItem, HasVisibility, - PathSegmentKind, Rename, UseTree, + PathSegmentKind, UseTree, }, ted, Direction, NodeOrToken, SyntaxKind, SyntaxNode, }; @@ -163,16 +163,12 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { 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 = match parse.tree().syntax().descendants().find_map(UseTree::cast) { - Some(it) => it, - None => { - panic!( - "Failed to make ast node `{}` from text {}", - std::any::type_name::(), - 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); @@ -202,11 +198,8 @@ fn insert_use_with_alias_option( }; } - let use_item = if alias.is_some() { - make::use_(None, make::use_tree(path.clone(), None, alias, false)).clone_for_update() - } else { - make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update() - }; + let use_item = + make::use_(None, make::use_tree(path.clone(), None, alias, false)).clone_for_update(); // merge into existing imports if possible if let Some(mb) = mb { From 929544ef2886a0823868ac9b7011c516ce697476 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Tue, 31 Oct 2023 22:13:07 +0800 Subject: [PATCH 3/3] use `check_assist_by_label` to pick assist --- .../ide-assists/src/handlers/auto_import.rs | 220 +++++++++++++++++- 1 file changed, 213 insertions(+), 7 deletions(-) diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index a2efa4d10b..cafd57a977 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -287,7 +287,8 @@ mod 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]) { @@ -739,7 +740,44 @@ fn main() { #[test] fn associated_trait_function() { - check_assist( + 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; + + 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`", + ); + + check_assist_by_label( auto_import, r" mod test_mod { @@ -773,6 +811,7 @@ fn main() { test_mod::TestStruct::test_function } ", + "Import `test_mod::TestTrait as _`", ); } @@ -810,7 +849,7 @@ fn main() { #[test] fn associated_trait_const() { - check_assist( + check_assist_by_label( auto_import, r" mod test_mod { @@ -844,6 +883,44 @@ fn main() { test_mod::TestStruct::TEST_CONST } ", + "Import `test_mod::TestTrait as _`", + ); + + 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; + + 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`", ); } @@ -881,7 +958,7 @@ fn main() { #[test] fn trait_method() { - check_assist( + check_assist_by_label( auto_import, r" mod test_mod { @@ -917,12 +994,52 @@ fn main() { test_struct.test_method() } ", + "Import `test_mod::TestTrait as _`", + ); + + 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; + + 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`", ); } #[test] fn trait_method_cross_crate() { - check_assist( + check_assist_by_label( auto_import, r" //- /main.rs crate:main deps:dep @@ -949,12 +1066,43 @@ fn main() { test_struct.test_method() } ", + "Import `dep::test_mod::TestTrait as _`", + ); + + 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; + + fn main() { + let test_struct = dep::test_mod::TestStruct {}; + test_struct.test_method() + } + ", + "Import `dep::test_mod::TestTrait`", ); } #[test] fn assoc_fn_cross_crate() { - check_assist( + check_assist_by_label( auto_import, r" //- /main.rs crate:main deps:dep @@ -979,12 +1127,41 @@ fn main() { dep::test_mod::TestStruct::test_function } ", + "Import `dep::test_mod::TestTrait as _`", + ); + + 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; + + fn main() { + dep::test_mod::TestStruct::test_function + } + ", + "Import `dep::test_mod::TestTrait`", ); } #[test] fn assoc_const_cross_crate() { - check_assist( + check_assist_by_label( auto_import, r" //- /main.rs crate:main deps:dep @@ -1009,6 +1186,35 @@ fn main() { dep::test_mod::TestStruct::CONST } ", + "Import `dep::test_mod::TestTrait as _`", + ); + + 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; + + fn main() { + dep::test_mod::TestStruct::CONST + } + ", + "Import `dep::test_mod::TestTrait`", ); }