1 use ide_db::helpers::insert_use::{insert_use, ImportScope};
2 use syntax::{algo::SyntaxRewriter, ast, match_ast, 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(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 mut rewriter = SyntaxRewriter::default();
44 shorten_paths(&mut rewriter, syntax.clone(), &path);
45 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
46 rewriter += insert_use(import_scope, path, ctx.config.insert_use);
47 builder.rewrite(rewriter);
53 /// Adds replacements to `re` that shorten `path` in all descendants of `node`.
54 fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, 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,
65 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
67 None => shorten_paths(rewriter, p.syntax().clone(), path),
70 _ => shorten_paths(rewriter, child, path),
76 fn maybe_replace_path(
77 rewriter: &mut SyntaxRewriter<'static>,
81 if !path_eq(path.clone(), target) {
85 // Shorten `path`, leaving only its last segment.
86 if let Some(parent) = path.qualifier() {
87 rewriter.delete(parent.syntax());
89 if let Some(double_colon) = path.coloncolon_token() {
90 rewriter.delete(&double_colon);
96 fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
97 let mut lhs_curr = lhs;
98 let mut rhs_curr = rhs;
100 match (lhs_curr.segment(), rhs_curr.segment()) {
101 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
105 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
106 (Some(lhs), Some(rhs)) => {
110 (None, None) => return true,
118 use crate::tests::{check_assist, check_assist_not_applicable};
123 fn test_replace_already_imported() {
125 replace_qualified_name_with_use,
140 fn test_replace_add_use_no_anchor() {
142 replace_qualified_name_with_use,
154 fn test_replace_add_use_no_anchor_with_item_below() {
156 replace_qualified_name_with_use,
175 fn test_replace_add_use_no_anchor_with_item_above() {
177 replace_qualified_name_with_use,
196 fn test_replace_add_use_no_anchor_2seg() {
198 replace_qualified_name_with_use,
211 fn test_replace_add_use() {
213 replace_qualified_name_with_use,
217 impl std::fmt::Debug$0 for Foo {
232 fn test_replace_file_use_other_anchor() {
234 replace_qualified_name_with_use,
236 impl std::fmt::Debug$0 for Foo {
249 fn test_replace_add_use_other_anchor_indent() {
251 replace_qualified_name_with_use,
253 impl std::fmt::Debug$0 for Foo {
266 fn test_replace_split_different() {
268 replace_qualified_name_with_use,
272 impl std::io$0 for Foo {
285 fn test_replace_split_self_for_use() {
287 replace_qualified_name_with_use,
291 impl std::fmt::Debug$0 for Foo {
295 use std::fmt::{self, Debug};
304 fn test_replace_split_self_for_target() {
306 replace_qualified_name_with_use,
310 impl std::fmt$0 for Foo {
314 use std::fmt::{self, Debug};
323 fn test_replace_add_to_nested_self_nested() {
325 replace_qualified_name_with_use,
327 use std::fmt::{Debug, nested::{Display}};
329 impl std::fmt::nested$0 for Foo {
333 use std::fmt::{Debug, nested::{self, Display}};
335 impl nested for Foo {
342 fn test_replace_add_to_nested_self_already_included() {
344 replace_qualified_name_with_use,
346 use std::fmt::{Debug, nested::{self, Display}};
348 impl std::fmt::nested$0 for Foo {
352 use std::fmt::{Debug, nested::{self, Display}};
354 impl nested for Foo {
361 fn test_replace_add_to_nested_nested() {
363 replace_qualified_name_with_use,
365 use std::fmt::{Debug, nested::{Display}};
367 impl std::fmt::nested::Debug$0 for Foo {
371 use std::fmt::{Debug, nested::{Debug, Display}};
380 fn test_replace_split_common_target_longer() {
382 replace_qualified_name_with_use,
386 impl std::fmt::nested::Display$0 for Foo {
390 use std::fmt::{Debug, nested::Display};
392 impl Display for Foo {
399 fn test_replace_split_common_use_longer() {
401 replace_qualified_name_with_use,
403 use std::fmt::nested::Debug;
405 impl std::fmt::Display$0 for Foo {
409 use std::fmt::{Display, nested::Debug};
411 impl Display for Foo {
418 fn test_replace_use_nested_import() {
420 replace_qualified_name_with_use,
427 fn foo() { crate::ty::lower$0::trait_env() }
430 use crate::{AssocItem, ty::{Substs, Ty, lower}};
432 fn foo() { lower::trait_env() }
438 fn test_replace_alias() {
440 replace_qualified_name_with_use,
444 impl foo::Debug$0 for Foo {
459 fn dont_import_trivial_paths() {
460 cov_mark::check!(dont_import_trivial_paths);
461 check_assist_not_applicable(
462 replace_qualified_name_with_use,
471 fn test_replace_not_applicable_in_use() {
472 check_assist_not_applicable(
473 replace_qualified_name_with_use,
481 fn test_replace_add_use_no_anchor_in_mod_mod() {
483 replace_qualified_name_with_use,
504 fn inserts_imports_after_inner_attributes() {
506 replace_qualified_name_with_use,
527 fn replaces_all_affected_paths() {
529 replace_qualified_name_with_use,
533 let x: std::fmt::Debug = std::fmt::Debug;
541 let x: Debug = Debug;
548 fn replaces_all_affected_paths_mod() {
550 replace_qualified_name_with_use,
555 let x: std::fmt::Debug = std::fmt::Debug;
572 let x: Debug = Debug;
587 fn does_not_replace_in_submodules() {
589 replace_qualified_name_with_use,
618 fn does_not_replace_in_use() {
620 replace_qualified_name_with_use,
622 use std::fmt::Display;
629 use std::fmt::{self, Display};
639 fn does_not_replace_pub_use() {
641 replace_qualified_name_with_use,
645 impl std::io$0 for Foo {
659 fn does_not_replace_pub_crate_use() {
661 replace_qualified_name_with_use,
663 pub(crate) use std::fmt;
665 impl std::io$0 for Foo {
669 pub(crate) use std::fmt;