Skip to content

Commit 5d66bfe

Browse files
Shorten *all* qualified paths when adding use
1 parent b65c0a5 commit 5d66bfe

File tree

2 files changed

+192
-11
lines changed

2 files changed

+192
-11
lines changed

crates/ra_assists/src/assist_context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ impl AssistBuilder {
252252
pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
253253
let node = rewriter.rewrite_root().unwrap();
254254
let new = rewriter.rewrite(&node);
255-
algo::diff(&node, &new).into_text_edit(&mut self.edit)
255+
algo::diff(&node, &new).into_text_edit(&mut self.edit);
256256
}
257257

258258
// FIXME: kill this API

crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs

Lines changed: 191 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
use hir;
2-
use ra_syntax::{ast, AstNode, SmolStr, TextRange};
1+
use hir::{self, ModPath};
2+
use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
33

4-
use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists};
4+
use crate::{
5+
utils::{find_insert_use_container, insert_use_statement},
6+
AssistContext, AssistId, Assists,
7+
};
8+
use either::Either;
59

610
// Assist: replace_qualified_name_with_use
711
//
@@ -39,16 +43,28 @@ pub(crate) fn replace_qualified_name_with_use(
3943
target,
4044
|builder| {
4145
let path_to_import = hir_path.mod_path().clone();
46+
let container = match find_insert_use_container(path.syntax(), ctx) {
47+
Some(c) => c,
48+
None => return,
49+
};
4250
insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
4351

44-
if let Some(last) = path.segment() {
45-
// Here we are assuming the assist will provide a correct use statement
46-
// so we can delete the path qualifier
47-
builder.delete(TextRange::new(
48-
path.syntax().text_range().start(),
49-
last.syntax().text_range().start(),
50-
));
52+
// Now that we've brought the name into scope, re-qualify all paths that could be
53+
// affected (that is, all paths inside the node we added the `use` to).
54+
let hir_path = match hir::Path::from_ast(path.clone()) {
55+
Some(p) => p,
56+
None => return,
57+
};
58+
let mut rewriter = SyntaxRewriter::default();
59+
match container {
60+
Either::Left(l) => {
61+
shorten_paths(&mut rewriter, l.syntax().clone(), hir_path.mod_path());
62+
}
63+
Either::Right(r) => {
64+
shorten_paths(&mut rewriter, r.syntax().clone(), hir_path.mod_path());
65+
}
5166
}
67+
builder.rewrite(rewriter);
5268
},
5369
)
5470
}
@@ -73,6 +89,59 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
7389
Some(ps)
7490
}
7591

92+
/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
93+
fn shorten_paths(re: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: &ModPath) {
94+
for child in node.children() {
95+
match_ast! {
96+
match child {
97+
// Don't modify `use` items, as this can break the `use` item when injecting a new
98+
// import into the use tree.
99+
ast::UseItem(_it) => continue,
100+
// Don't descend into submodules, they don't have the same `use` items in scope.
101+
ast::Module(_it) => continue,
102+
103+
ast::Path(p) => {
104+
match maybe_replace_path(re, &p, path) {
105+
Some(()) => {},
106+
None => shorten_paths(re, p.syntax().clone(), path),
107+
}
108+
},
109+
_ => shorten_paths(re, child, path),
110+
}
111+
}
112+
}
113+
}
114+
115+
fn maybe_replace_path(
116+
re: &mut SyntaxRewriter<'static>,
117+
p: &ast::Path,
118+
path: &ModPath,
119+
) -> Option<()> {
120+
let hir_path = hir::Path::from_ast(p.clone())?;
121+
122+
if hir_path.mod_path() != path {
123+
return None;
124+
}
125+
126+
// Replace path with its last "plain" segment.
127+
let mut mod_path = hir_path.mod_path().clone();
128+
let last = mod_path.segments.len() - 1;
129+
mod_path.segments.swap(0, last);
130+
mod_path.segments.truncate(1);
131+
mod_path.kind = hir::PathKind::Plain;
132+
133+
let mut new_path = crate::ast_transform::path_to_ast(mod_path);
134+
135+
let type_args = p.segment().and_then(|s| s.type_arg_list());
136+
if let Some(type_args) = type_args {
137+
let last_segment = new_path.segment().unwrap();
138+
new_path = new_path.with_segment(last_segment.with_type_args(type_args));
139+
}
140+
141+
re.replace(p.syntax(), new_path.syntax());
142+
Some(())
143+
}
144+
76145
#[cfg(test)]
77146
mod tests {
78147
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -459,6 +528,118 @@ use std::fmt::Debug;
459528
460529
fn main() {
461530
Debug
531+
}
532+
",
533+
);
534+
}
535+
536+
#[test]
537+
fn replaces_all_affected_paths() {
538+
check_assist(
539+
replace_qualified_name_with_use,
540+
"
541+
fn main() {
542+
std::fmt::Debug<|>;
543+
let x: std::fmt::Debug = std::fmt::Debug;
544+
}
545+
",
546+
"
547+
use std::fmt::Debug;
548+
549+
fn main() {
550+
Debug;
551+
let x: Debug = Debug;
552+
}
553+
",
554+
);
555+
}
556+
557+
#[test]
558+
fn replaces_all_affected_paths_mod() {
559+
check_assist(
560+
replace_qualified_name_with_use,
561+
"
562+
mod m {
563+
fn f() {
564+
std::fmt::Debug<|>;
565+
let x: std::fmt::Debug = std::fmt::Debug;
566+
}
567+
fn g() {
568+
std::fmt::Debug;
569+
}
570+
}
571+
572+
fn f() {
573+
std::fmt::Debug;
574+
}
575+
",
576+
"
577+
mod m {
578+
use std::fmt::Debug;
579+
580+
fn f() {
581+
Debug;
582+
let x: Debug = Debug;
583+
}
584+
fn g() {
585+
Debug;
586+
}
587+
}
588+
589+
fn f() {
590+
std::fmt::Debug;
591+
}
592+
",
593+
);
594+
}
595+
596+
#[test]
597+
fn does_not_replace_in_submodules() {
598+
check_assist(
599+
replace_qualified_name_with_use,
600+
"
601+
fn main() {
602+
std::fmt::Debug<|>;
603+
}
604+
605+
mod sub {
606+
fn f() {
607+
std::fmt::Debug;
608+
}
609+
}
610+
",
611+
"
612+
use std::fmt::Debug;
613+
614+
fn main() {
615+
Debug;
616+
}
617+
618+
mod sub {
619+
fn f() {
620+
std::fmt::Debug;
621+
}
622+
}
623+
",
624+
);
625+
}
626+
627+
#[test]
628+
fn does_not_replace_in_use() {
629+
check_assist(
630+
replace_qualified_name_with_use,
631+
"
632+
use std::fmt::Display;
633+
634+
fn main() {
635+
std::fmt<|>;
636+
}
637+
",
638+
"
639+
use std::fmt::{self, Display};
640+
641+
fn main() {
642+
fmt;
462643
}
463644
",
464645
);

0 commit comments

Comments
 (0)