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) ]
77146mod tests {
78147 use crate :: tests:: { check_assist, check_assist_not_applicable} ;
@@ -459,6 +528,118 @@ use std::fmt::Debug;
459528
460529fn 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