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)?;
35 let syntax = scope.as_syntax_node();
37 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
38 "Replace qualified path with use",
41 // Now that we've brought the name into scope, re-qualify all paths that could be
42 // affected (that is, all paths inside the node we added the `use` to).
43 let syntax = builder.make_syntax_mut(syntax.clone());
44 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
45 shorten_paths(&syntax, &path.clone_for_update());
46 insert_use(import_scope, path, ctx.config.insert_use);
52 /// Adds replacements to `re` that shorten `path` in all descendants of `node`.
53 fn shorten_paths(node: &SyntaxNode, path: &ast::Path) {
54 for child in node.children() {
57 // Don't modify `use` items, as this can break the `use` item when injecting a new
58 // import into the use tree.
59 ast::Use(_it) => continue,
60 // Don't descend into submodules, they don't have the same `use` items in scope.
61 ast::Module(_it) => continue,
62 ast::Path(p) => if maybe_replace_path(p.clone(), path.clone()).is_none() {
63 shorten_paths(p.syntax(), path);
65 _ => shorten_paths(&child, path),
71 fn maybe_replace_path(path: ast::Path, target: ast::Path) -> Option<()> {
72 if !path_eq(path.clone(), target) {
76 // Shorten `path`, leaving only its last segment.
77 if let Some(parent) = path.qualifier() {
78 ted::remove(parent.syntax());
80 if let Some(double_colon) = path.coloncolon_token() {
81 ted::remove(&double_colon);
87 fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
88 let mut lhs_curr = lhs;
89 let mut rhs_curr = rhs;
91 match (lhs_curr.segment(), rhs_curr.segment()) {
92 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
96 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
97 (Some(lhs), Some(rhs)) => {
101 (None, None) => return true,
109 use crate::tests::{check_assist, check_assist_not_applicable};
114 fn test_replace_already_imported() {
116 replace_qualified_name_with_use,
131 fn test_replace_add_use_no_anchor() {
133 replace_qualified_name_with_use,
146 fn test_replace_add_use_no_anchor_with_item_below() {
148 replace_qualified_name_with_use,
167 fn test_replace_add_use_no_anchor_with_item_above() {
169 replace_qualified_name_with_use,
188 fn test_replace_add_use_no_anchor_2seg() {
190 replace_qualified_name_with_use,
203 fn test_replace_add_use() {
205 replace_qualified_name_with_use,
209 impl std::fmt::Debug$0 for Foo {
224 fn test_replace_file_use_other_anchor() {
226 replace_qualified_name_with_use,
228 impl std::fmt::Debug$0 for Foo {
241 fn test_replace_add_use_other_anchor_indent() {
243 replace_qualified_name_with_use,
245 impl std::fmt::Debug$0 for Foo {
258 fn test_replace_split_different() {
260 replace_qualified_name_with_use,
264 impl std::io$0 for Foo {
277 fn test_replace_split_self_for_use() {
279 replace_qualified_name_with_use,
283 impl std::fmt::Debug$0 for Foo {
287 use std::fmt::{self, Debug};
296 fn test_replace_split_self_for_target() {
298 replace_qualified_name_with_use,
302 impl std::fmt$0 for Foo {
306 use std::fmt::{self, Debug};
315 fn test_replace_add_to_nested_self_nested() {
317 replace_qualified_name_with_use,
319 use std::fmt::{Debug, nested::{Display}};
321 impl std::fmt::nested$0 for Foo {
325 use std::fmt::{Debug, nested::{self, Display}};
327 impl nested for Foo {
334 fn test_replace_add_to_nested_self_already_included() {
336 replace_qualified_name_with_use,
338 use std::fmt::{Debug, nested::{self, Display}};
340 impl std::fmt::nested$0 for Foo {
344 use std::fmt::{Debug, nested::{self, Display}};
346 impl nested for Foo {
353 fn test_replace_add_to_nested_nested() {
355 replace_qualified_name_with_use,
357 use std::fmt::{Debug, nested::{Display}};
359 impl std::fmt::nested::Debug$0 for Foo {
363 use std::fmt::{Debug, nested::{Debug, Display}};
372 fn test_replace_split_common_target_longer() {
374 replace_qualified_name_with_use,
378 impl std::fmt::nested::Display$0 for Foo {
382 use std::fmt::{Debug, nested::Display};
384 impl Display for Foo {
391 fn test_replace_split_common_use_longer() {
393 replace_qualified_name_with_use,
395 use std::fmt::nested::Debug;
397 impl std::fmt::Display$0 for Foo {
401 use std::fmt::{Display, nested::Debug};
403 impl Display for Foo {
410 fn test_replace_use_nested_import() {
412 replace_qualified_name_with_use,
419 fn foo() { crate::ty::lower$0::trait_env() }
422 use crate::{AssocItem, ty::{Substs, Ty, lower}};
424 fn foo() { lower::trait_env() }
430 fn test_replace_alias() {
432 replace_qualified_name_with_use,
436 impl foo::Debug$0 for Foo {
451 fn dont_import_trivial_paths() {
452 cov_mark::check!(dont_import_trivial_paths);
453 check_assist_not_applicable(
454 replace_qualified_name_with_use,
463 fn test_replace_not_applicable_in_use() {
464 check_assist_not_applicable(
465 replace_qualified_name_with_use,
473 fn test_replace_add_use_no_anchor_in_mod_mod() {
475 replace_qualified_name_with_use,
496 fn inserts_imports_after_inner_attributes() {
498 replace_qualified_name_with_use,
519 fn replaces_all_affected_paths() {
521 replace_qualified_name_with_use,
525 let x: std::fmt::Debug = std::fmt::Debug;
533 let x: Debug = Debug;
540 fn replaces_all_affected_paths_mod() {
542 replace_qualified_name_with_use,
547 let x: std::fmt::Debug = std::fmt::Debug;
564 let x: Debug = Debug;
579 fn does_not_replace_in_submodules() {
581 replace_qualified_name_with_use,
610 fn does_not_replace_in_use() {
612 replace_qualified_name_with_use,
614 use std::fmt::Display;
621 use std::fmt::{self, Display};
631 fn does_not_replace_pub_use() {
633 replace_qualified_name_with_use,
637 impl std::io$0 for Foo {
651 fn does_not_replace_pub_crate_use() {
653 replace_qualified_name_with_use,
655 pub(crate) use std::fmt;
657 impl std::io$0 for Foo {
661 pub(crate) use std::fmt;