1 use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode, TextRange};
5 utils::{insert_use, ImportScope},
6 AssistContext, AssistId, AssistKind, Assists,
10 // Assist: replace_qualified_name_with_use
12 // Adds a use statement for a given fully-qualified name.
15 // fn process(map: std::collections::<|>HashMap<String, String>) {}
19 // use std::collections::HashMap;
21 // fn process(map: HashMap<String, String>) {}
23 pub(crate) fn replace_qualified_name_with_use(
27 let path: ast::Path = ctx.find_node_at_offset()?;
28 // We don't want to mess with use statements
29 if path.syntax().ancestors().find_map(ast::Use::cast).is_some() {
32 if path.qualifier().is_none() {
33 mark::hit!(dont_import_trivial_paths);
36 let path_to_import = path.to_string();
37 let path_to_import = match path.segment()?.generic_arg_list() {
38 Some(generic_args) => {
39 let generic_args_start =
40 generic_args.syntax().text_range().start() - path.syntax().text_range().start();
41 &path_to_import[TextRange::up_to(generic_args_start)]
43 None => path_to_import.as_str(),
46 let target = path.syntax().text_range();
47 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
48 let syntax = scope.as_syntax_node();
50 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
51 "Replace qualified path with use",
54 // Now that we've brought the name into scope, re-qualify all paths that could be
55 // affected (that is, all paths inside the node we added the `use` to).
56 let mut rewriter = SyntaxRewriter::default();
57 shorten_paths(&mut rewriter, syntax.clone(), path);
58 let rewritten_syntax = rewriter.rewrite(&syntax);
59 if let Some(ref import_scope) = ImportScope::from(rewritten_syntax) {
60 let new_syntax = insert_use(
62 make::path_from_text(path_to_import),
63 ctx.config.insert_use.merge,
65 builder.replace(syntax.text_range(), new_syntax.to_string())
71 /// Adds replacements to `re` that shorten `path` in all descendants of `node`.
72 fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
73 for child in node.children() {
76 // Don't modify `use` items, as this can break the `use` item when injecting a new
77 // import into the use tree.
78 ast::Use(_it) => continue,
79 // Don't descend into submodules, they don't have the same `use` items in scope.
80 ast::Module(_it) => continue,
83 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
85 None => shorten_paths(rewriter, p.syntax().clone(), path.clone()),
88 _ => shorten_paths(rewriter, child, path.clone()),
94 fn maybe_replace_path(
95 rewriter: &mut SyntaxRewriter<'static>,
99 if !path_eq(path.clone(), target) {
103 // Shorten `path`, leaving only its last segment.
104 if let Some(parent) = path.qualifier() {
105 rewriter.delete(parent.syntax());
107 if let Some(double_colon) = path.coloncolon_token() {
108 rewriter.delete(&double_colon);
114 fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
115 let mut lhs_curr = lhs;
116 let mut rhs_curr = rhs;
118 match (lhs_curr.segment(), rhs_curr.segment()) {
119 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
123 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
124 (Some(lhs), Some(rhs)) => {
128 (None, None) => return true,
136 use crate::tests::{check_assist, check_assist_not_applicable};
141 fn test_replace_add_use_no_anchor() {
143 replace_qualified_name_with_use,
155 fn test_replace_add_use_no_anchor_with_item_below() {
157 replace_qualified_name_with_use,
176 fn test_replace_add_use_no_anchor_with_item_above() {
178 replace_qualified_name_with_use,
197 fn test_replace_add_use_no_anchor_2seg() {
199 replace_qualified_name_with_use,
212 fn test_replace_add_use() {
214 replace_qualified_name_with_use,
218 impl std::fmt::Debug<|> for Foo {
233 fn test_replace_file_use_other_anchor() {
235 replace_qualified_name_with_use,
237 impl std::fmt::Debug<|> for Foo {
250 fn test_replace_add_use_other_anchor_indent() {
252 replace_qualified_name_with_use,
254 impl std::fmt::Debug<|> for Foo {
267 fn test_replace_split_different() {
269 replace_qualified_name_with_use,
273 impl std::io<|> for Foo {
286 fn test_replace_split_self_for_use() {
288 replace_qualified_name_with_use,
292 impl std::fmt::Debug<|> for Foo {
296 use std::fmt::{self, Debug};
305 fn test_replace_split_self_for_target() {
307 replace_qualified_name_with_use,
311 impl std::fmt<|> for Foo {
315 use std::fmt::{self, Debug};
324 fn test_replace_add_to_nested_self_nested() {
326 replace_qualified_name_with_use,
328 use std::fmt::{Debug, nested::{Display}};
330 impl std::fmt::nested<|> for Foo {
334 use std::fmt::{Debug, nested::{self, Display}};
336 impl nested for Foo {
343 fn test_replace_add_to_nested_self_already_included() {
345 replace_qualified_name_with_use,
347 use std::fmt::{Debug, nested::{self, Display}};
349 impl std::fmt::nested<|> for Foo {
353 use std::fmt::{Debug, nested::{self, Display}};
355 impl nested for Foo {
362 fn test_replace_add_to_nested_nested() {
364 replace_qualified_name_with_use,
366 use std::fmt::{Debug, nested::{Display}};
368 impl std::fmt::nested::Debug<|> for Foo {
372 use std::fmt::{Debug, nested::{Debug, Display}};
381 fn test_replace_split_common_target_longer() {
383 replace_qualified_name_with_use,
387 impl std::fmt::nested::Display<|> for Foo {
391 use std::fmt::{Debug, nested::Display};
393 impl Display for Foo {
400 fn test_replace_split_common_use_longer() {
402 replace_qualified_name_with_use,
404 use std::fmt::nested::Debug;
406 impl std::fmt::Display<|> for Foo {
410 use std::fmt::{Display, nested::Debug};
412 impl Display for Foo {
419 fn test_replace_use_nested_import() {
421 replace_qualified_name_with_use,
428 fn foo() { crate::ty::lower<|>::trait_env() }
431 use crate::{AssocItem, ty::{Substs, Ty, lower}};
433 fn foo() { lower::trait_env() }
439 fn test_replace_alias() {
441 replace_qualified_name_with_use,
445 impl foo::Debug<|> for Foo {
460 fn dont_import_trivial_paths() {
461 mark::check!(dont_import_trivial_paths);
462 check_assist_not_applicable(
463 replace_qualified_name_with_use,
465 impl foo<|> for Foo {
472 fn test_replace_not_applicable_in_use() {
473 check_assist_not_applicable(
474 replace_qualified_name_with_use,
482 fn test_replace_add_use_no_anchor_in_mod_mod() {
484 replace_qualified_name_with_use,
505 fn inserts_imports_after_inner_attributes() {
507 replace_qualified_name_with_use,
528 fn replaces_all_affected_paths() {
530 replace_qualified_name_with_use,
534 let x: std::fmt::Debug = std::fmt::Debug;
542 let x: Debug = Debug;
549 fn replaces_all_affected_paths_mod() {
551 replace_qualified_name_with_use,
556 let x: std::fmt::Debug = std::fmt::Debug;
573 let x: Debug = Debug;
588 fn does_not_replace_in_submodules() {
590 replace_qualified_name_with_use,
619 fn does_not_replace_in_use() {
621 replace_qualified_name_with_use,
623 use std::fmt::Display;
630 use std::fmt::{self, Display};
640 fn does_not_replace_pub_use() {
642 replace_qualified_name_with_use,
646 impl std::io<|> for Foo {
660 fn does_not_replace_pub_crate_use() {
662 replace_qualified_name_with_use,
664 pub(crate) use std::fmt;
666 impl std::io<|> for Foo {
670 pub(crate) use std::fmt;