From 0db599231109c13e0c870987cee318f9cd8310eb Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jul 2021 17:03:24 +0200 Subject: [PATCH] `replace_qualified_name_with_use` keeps qualifier for import --- .../replace_qualified_name_with_use.rs | 66 ++++++++++++++++--- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index 7aad4a44b6af..4a30b83b5a3d 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs @@ -3,7 +3,10 @@ use ide_db::helpers::{ insert_use::{insert_use, ImportScope}, mod_path_to_ast, }; -use syntax::{ast, match_ast, ted, AstNode, SyntaxNode}; +use syntax::{ + ast::{self, make}, + match_ast, ted, AstNode, SyntaxNode, +}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -38,20 +41,26 @@ pub(crate) fn replace_qualified_name_with_use( return None; } - let res = ctx.sema.resolve_path(&path)?; - let def: hir::ItemInNs = match res { - hir::PathResolution::Def(def) if def.as_assoc_item(ctx.sema.db).is_none() => def.into(), - hir::PathResolution::Macro(mac) => mac.into(), + // only offer replacement for non assoc items + match ctx.sema.resolve_path(&path)? { + hir::PathResolution::Def(def) if def.as_assoc_item(ctx.sema.db).is_none() => (), + hir::PathResolution::Macro(_) => (), + _ => return None, + } + // then search for an import for the first path segment of what we want to replace + // that way it is less likely that we import the item from a different location due re-exports + let module = match ctx.sema.resolve_path(&path.first_qualifier_or_self())? { + hir::PathResolution::Def(module @ hir::ModuleDef::Module(_)) => module, _ => return None, }; - let target = path.syntax().text_range(); let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?; - let mod_path = ctx.sema.scope(path.syntax()).module()?.find_use_path_prefixed( + let path_to_qualifier = ctx.sema.scope(path.syntax()).module()?.find_use_path_prefixed( ctx.sema.db, - def, + module, ctx.config.insert_use.prefix_kind, )?; + let target = path.syntax().text_range(); acc.add( AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), "Replace qualified path with use", @@ -64,7 +73,11 @@ pub(crate) fn replace_qualified_name_with_use( ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)), }; - let path = mod_path_to_ast(&mod_path); + // stick the found import in front of the to be replaced path + let path = match mod_path_to_ast(&path_to_qualifier).qualifier() { + Some(qualifier) => make::path_concat(qualifier, path), + None => path, + }; shorten_paths(scope.as_syntax_node(), &path.clone_for_update()); insert_use(&scope, path, &ctx.config.insert_use); }, @@ -300,6 +313,41 @@ impl Foo { fn main() { Foo::foo$0(); } +", + ); + } + + #[test] + fn replace_reuses_path_qualifier() { + check_assist( + replace_qualified_name_with_use, + r" +pub mod foo { + struct Foo; +} + +mod bar { + pub use super::foo::Foo as Bar; +} + +fn main() { + foo::Foo$0; +} +", + r" +use foo::Foo; + +pub mod foo { + struct Foo; +} + +mod bar { + pub use super::foo::Foo as Bar; +} + +fn main() { + Foo; +} ", ); }