1 use ide_db::helpers::insert_use::{insert_use, ImportScope};
2 use syntax::{ast, match_ast, ted, AstNode, SyntaxNode};
4 use crate::{AssistContext, AssistId, AssistKind, Assists};
6 // Assist: replace_qualified_name_with_use
8 // Adds a use statement for a given fully-qualified name.
11 // fn process(map: std::collections::$0HashMap<String, String>) {}
15 // use std::collections::HashMap;
17 // fn process(map: HashMap<String, String>) {}
19 pub(crate) fn replace_qualified_name_with_use(
23 let path: ast::Path = ctx.find_node_at_offset()?;
24 // We don't want to mess with use statements
25 if path.syntax().ancestors().find_map(ast::Use::cast).is_some() {
28 if path.qualifier().is_none() {
29 cov_mark::hit!(dont_import_trivial_paths);
33 let target = path.syntax().text_range();
34 let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?;
36 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
37 "Replace qualified path with use",
40 // Now that we've brought the name into scope, re-qualify all paths that could be
41 // affected (that is, all paths inside the node we added the `use` to).
42 let scope = match scope {
43 ImportScope::File(it) => ImportScope::File(builder.make_mut(it)),
44 ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)),
45 ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)),
47 shorten_paths(scope.as_syntax_node(), &path.clone_for_update());
48 insert_use(&scope, path, &ctx.config.insert_use);
53 /// Adds replacements to `re` that shorten `path` in all descendants of `node`.
54 fn shorten_paths(node: &SyntaxNode, path: &ast::Path) {
55 for child in node.children() {
58 // Don't modify `use` items, as this can break the `use` item when injecting a new
59 // import into the use tree.
60 ast::Use(_it) => continue,
61 // Don't descend into submodules, they don't have the same `use` items in scope.
62 ast::Module(_it) => continue,
63 ast::Path(p) => if maybe_replace_path(p.clone(), path.clone()).is_none() {
64 shorten_paths(p.syntax(), path);
66 _ => shorten_paths(&child, path),
72 fn maybe_replace_path(path: ast::Path, target: ast::Path) -> Option<()> {
73 if !path_eq(path.clone(), target) {
77 // Shorten `path`, leaving only its last segment.
78 if let Some(parent) = path.qualifier() {
79 ted::remove(parent.syntax());
81 if let Some(double_colon) = path.coloncolon_token() {
82 ted::remove(&double_colon);
88 fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
89 let mut lhs_curr = lhs;
90 let mut rhs_curr = rhs;
92 match (lhs_curr.segment(), rhs_curr.segment()) {
93 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
97 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
98 (Some(lhs), Some(rhs)) => {
102 (None, None) => return true,
110 use crate::tests::{check_assist, check_assist_not_applicable};
115 fn test_replace_already_imported() {
117 replace_qualified_name_with_use,
132 fn test_replace_add_use_no_anchor() {
134 replace_qualified_name_with_use,
147 fn test_replace_add_use_no_anchor_with_item_below() {
149 replace_qualified_name_with_use,
168 fn test_replace_add_use_no_anchor_with_item_above() {
170 replace_qualified_name_with_use,
189 fn test_replace_add_use_no_anchor_2seg() {
191 replace_qualified_name_with_use,
204 fn test_replace_add_use() {
206 replace_qualified_name_with_use,
210 impl std::fmt::Debug$0 for Foo {
225 fn test_replace_file_use_other_anchor() {
227 replace_qualified_name_with_use,
229 impl std::fmt::Debug$0 for Foo {
242 fn test_replace_add_use_other_anchor_indent() {
244 replace_qualified_name_with_use,
246 impl std::fmt::Debug$0 for Foo {
259 fn test_replace_split_different() {
261 replace_qualified_name_with_use,
265 impl std::io$0 for Foo {
278 fn test_replace_split_self_for_use() {
280 replace_qualified_name_with_use,
284 impl std::fmt::Debug$0 for Foo {
288 use std::fmt::{self, Debug};
297 fn test_replace_split_self_for_target() {
299 replace_qualified_name_with_use,
303 impl std::fmt$0 for Foo {
307 use std::fmt::{self, Debug};
316 fn test_replace_add_to_nested_self_nested() {
318 replace_qualified_name_with_use,
320 use std::fmt::{Debug, nested::{Display}};
322 impl std::fmt::nested$0 for Foo {
326 use std::fmt::{Debug, nested::{self, Display}};
328 impl nested for Foo {
335 fn test_replace_add_to_nested_self_already_included() {
337 replace_qualified_name_with_use,
339 use std::fmt::{Debug, nested::{self, Display}};
341 impl std::fmt::nested$0 for Foo {
345 use std::fmt::{Debug, nested::{self, Display}};
347 impl nested for Foo {
354 fn test_replace_add_to_nested_nested() {
356 replace_qualified_name_with_use,
358 use std::fmt::{Debug, nested::{Display}};
360 impl std::fmt::nested::Debug$0 for Foo {
364 use std::fmt::{Debug, nested::{Debug, Display}};
373 fn test_replace_split_common_target_longer() {
375 replace_qualified_name_with_use,
379 impl std::fmt::nested::Display$0 for Foo {
383 use std::fmt::{Debug, nested::Display};
385 impl Display for Foo {
392 fn test_replace_split_common_use_longer() {
394 replace_qualified_name_with_use,
396 use std::fmt::nested::Debug;
398 impl std::fmt::Display$0 for Foo {
402 use std::fmt::{Display, nested::Debug};
404 impl Display for Foo {
411 fn test_replace_use_nested_import() {
413 replace_qualified_name_with_use,
420 fn foo() { crate::ty::lower$0::trait_env() }
423 use crate::{AssocItem, ty::{Substs, Ty, lower}};
425 fn foo() { lower::trait_env() }
431 fn test_replace_alias() {
433 replace_qualified_name_with_use,
437 impl foo::Debug$0 for Foo {
452 fn dont_import_trivial_paths() {
453 cov_mark::check!(dont_import_trivial_paths);
454 check_assist_not_applicable(
455 replace_qualified_name_with_use,
464 fn test_replace_not_applicable_in_use() {
465 check_assist_not_applicable(
466 replace_qualified_name_with_use,
474 fn test_replace_add_use_no_anchor_in_mod_mod() {
476 replace_qualified_name_with_use,
497 fn inserts_imports_after_inner_attributes() {
499 replace_qualified_name_with_use,
520 fn replaces_all_affected_paths() {
522 replace_qualified_name_with_use,
526 let x: std::fmt::Debug = std::fmt::Debug;
534 let x: Debug = Debug;
541 fn replaces_all_affected_paths_mod() {
543 replace_qualified_name_with_use,
548 let x: std::fmt::Debug = std::fmt::Debug;
565 let x: Debug = Debug;
580 fn does_not_replace_in_submodules() {
582 replace_qualified_name_with_use,
611 fn does_not_replace_in_use() {
613 replace_qualified_name_with_use,
615 use std::fmt::Display;
622 use std::fmt::{self, Display};
632 fn does_not_replace_pub_use() {
634 replace_qualified_name_with_use,
638 impl std::io$0 for Foo {
652 fn does_not_replace_pub_crate_use() {
654 replace_qualified_name_with_use,
656 pub(crate) use std::fmt;
658 impl std::io$0 for Foo {
662 pub(crate) use std::fmt;