2 use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
5 utils::{find_insert_use_container, insert_use_statement},
6 AssistContext, AssistId, Assists,
9 // Assist: replace_qualified_name_with_use
11 // Adds a use statement for a given fully-qualified name.
14 // fn process(map: std::collections::<|>HashMap<String, String>) {}
18 // use std::collections::HashMap;
20 // fn process(map: HashMap<String, String>) {}
22 pub(crate) fn replace_qualified_name_with_use(
26 let path: ast::Path = ctx.find_node_at_offset()?;
27 // We don't want to mess with use statements
28 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
32 let hir_path = ctx.sema.lower_path(&path)?;
33 let segments = collect_hir_path_segments(&hir_path)?;
34 if segments.len() < 2 {
38 let target = path.syntax().text_range();
40 AssistId("replace_qualified_name_with_use"),
41 "Replace qualified path with use",
44 let path_to_import = hir_path.mod_path().clone();
45 let container = match find_insert_use_container(path.syntax(), ctx) {
49 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
51 // Now that we've brought the name into scope, re-qualify all paths that could be
52 // affected (that is, all paths inside the node we added the `use` to).
53 let mut rewriter = SyntaxRewriter::default();
54 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
55 shorten_paths(&mut rewriter, syntax, path);
56 builder.rewrite(rewriter);
61 fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
62 let mut ps = Vec::<SmolStr>::with_capacity(10);
64 hir::PathKind::Abs => ps.push("".into()),
65 hir::PathKind::Crate => ps.push("crate".into()),
66 hir::PathKind::Plain => {}
67 hir::PathKind::Super(0) => ps.push("self".into()),
68 hir::PathKind::Super(lvl) => {
69 let mut chain = "super".to_string();
73 ps.push(chain.into());
75 hir::PathKind::DollarCrate(_) => return None,
77 ps.extend(path.segments().iter().map(|it| it.name.to_string().into()));
81 /// Adds replacements to `re` that shorten `path` in all descendants of `node`.
82 fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
83 for child in node.children() {
86 // Don't modify `use` items, as this can break the `use` item when injecting a new
87 // import into the use tree.
88 ast::UseItem(_it) => continue,
89 // Don't descend into submodules, they don't have the same `use` items in scope.
90 ast::Module(_it) => continue,
93 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
95 None => shorten_paths(rewriter, p.syntax().clone(), path.clone()),
98 _ => shorten_paths(rewriter, child, path.clone()),
104 fn maybe_replace_path(
105 rewriter: &mut SyntaxRewriter<'static>,
109 if !path_eq(path.clone(), target.clone()) {
113 // Shorten `path`, leaving only its last segment.
114 if let Some(parent) = path.qualifier() {
115 rewriter.delete(parent.syntax());
117 if let Some(double_colon) = path.coloncolon_token() {
118 rewriter.delete(&double_colon);
124 fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
125 let mut lhs_curr = lhs;
126 let mut rhs_curr = rhs;
128 match (lhs_curr.segment(), rhs_curr.segment()) {
129 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
133 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
134 (Some(lhs), Some(rhs)) => {
138 (None, None) => return true,
146 use crate::tests::{check_assist, check_assist_not_applicable};
151 fn test_replace_add_use_no_anchor() {
153 replace_qualified_name_with_use,
165 fn test_replace_add_use_no_anchor_with_item_below() {
167 replace_qualified_name_with_use,
186 fn test_replace_add_use_no_anchor_with_item_above() {
188 replace_qualified_name_with_use,
207 fn test_replace_add_use_no_anchor_2seg() {
209 replace_qualified_name_with_use,
222 fn test_replace_add_use() {
224 replace_qualified_name_with_use,
228 impl std::fmt::Debug<|> for Foo {
242 fn test_replace_file_use_other_anchor() {
244 replace_qualified_name_with_use,
246 impl std::fmt::Debug<|> for Foo {
259 fn test_replace_add_use_other_anchor_indent() {
261 replace_qualified_name_with_use,
263 impl std::fmt::Debug<|> for Foo {
276 fn test_replace_split_different() {
278 replace_qualified_name_with_use,
282 impl std::io<|> for Foo {
295 fn test_replace_split_self_for_use() {
297 replace_qualified_name_with_use,
301 impl std::fmt::Debug<|> for Foo {
305 use std::fmt::{self, Debug, };
314 fn test_replace_split_self_for_target() {
316 replace_qualified_name_with_use,
320 impl std::fmt<|> for Foo {
324 use std::fmt::{self, Debug};
333 fn test_replace_add_to_nested_self_nested() {
335 replace_qualified_name_with_use,
337 use std::fmt::{Debug, nested::{Display}};
339 impl std::fmt::nested<|> for Foo {
343 use std::fmt::{Debug, nested::{Display, self}};
345 impl nested for Foo {
352 fn test_replace_add_to_nested_self_already_included() {
354 replace_qualified_name_with_use,
356 use std::fmt::{Debug, nested::{self, Display}};
358 impl std::fmt::nested<|> for Foo {
362 use std::fmt::{Debug, nested::{self, Display}};
364 impl nested for Foo {
371 fn test_replace_add_to_nested_nested() {
373 replace_qualified_name_with_use,
375 use std::fmt::{Debug, nested::{Display}};
377 impl std::fmt::nested::Debug<|> for Foo {
381 use std::fmt::{Debug, nested::{Display, Debug}};
390 fn test_replace_split_common_target_longer() {
392 replace_qualified_name_with_use,
396 impl std::fmt::nested::Display<|> for Foo {
400 use std::fmt::{nested::Display, Debug};
402 impl Display for Foo {
409 fn test_replace_split_common_use_longer() {
411 replace_qualified_name_with_use,
413 use std::fmt::nested::Debug;
415 impl std::fmt::Display<|> for Foo {
419 use std::fmt::{Display, nested::Debug};
421 impl Display for Foo {
428 fn test_replace_use_nested_import() {
430 replace_qualified_name_with_use,
437 fn foo() { crate::ty::lower<|>::trait_env() }
441 ty::{Substs, Ty, lower},
445 fn foo() { lower::trait_env() }
451 fn test_replace_alias() {
453 replace_qualified_name_with_use,
457 impl foo::Debug<|> for Foo {
470 fn test_replace_not_applicable_one_segment() {
471 check_assist_not_applicable(
472 replace_qualified_name_with_use,
474 impl foo<|> for Foo {
481 fn test_replace_not_applicable_in_use() {
482 check_assist_not_applicable(
483 replace_qualified_name_with_use,
491 fn test_replace_add_use_no_anchor_in_mod_mod() {
493 replace_qualified_name_with_use,
514 fn inserts_imports_after_inner_attributes() {
516 replace_qualified_name_with_use,
536 fn replaces_all_affected_paths() {
538 replace_qualified_name_with_use,
542 let x: std::fmt::Debug = std::fmt::Debug;
550 let x: Debug = Debug;
557 fn replaces_all_affected_paths_mod() {
559 replace_qualified_name_with_use,
564 let x: std::fmt::Debug = std::fmt::Debug;
581 let x: Debug = Debug;
596 fn does_not_replace_in_submodules() {
598 replace_qualified_name_with_use,
627 fn does_not_replace_in_use() {
629 replace_qualified_name_with_use,
631 use std::fmt::Display;
638 use std::fmt::{self, Display};