@@ -9,6 +9,7 @@ use ide_db::{
9
9
base_db:: { FileId , FileRange } ,
10
10
defs:: { Definition , NameClass , NameRefClass } ,
11
11
rename:: { bail, format_err, source_edit_from_references, IdentifierKind } ,
12
+ source_change:: SourceChangeBuilder ,
12
13
RootDatabase ,
13
14
} ;
14
15
use itertools:: Itertools ;
@@ -90,24 +91,60 @@ pub(crate) fn rename(
90
91
let syntax = source_file. syntax ( ) ;
91
92
92
93
let defs = find_definitions ( & sema, syntax, position) ?;
94
+ let alias_fallback = alias_fallback ( syntax, position, new_name) ;
95
+
96
+ let ops: RenameResult < Vec < SourceChange > > = match alias_fallback {
97
+ Some ( _) => defs
98
+ // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can
99
+ // properly find "direct" usages/references.
100
+ . map ( |( .., def) | {
101
+ match IdentifierKind :: classify ( new_name) ? {
102
+ IdentifierKind :: Ident => ( ) ,
103
+ IdentifierKind :: Lifetime => {
104
+ bail ! ( "Cannot alias reference to a lifetime identifier" )
105
+ }
106
+ IdentifierKind :: Underscore => bail ! ( "Cannot alias reference to `_`" ) ,
107
+ } ;
93
108
94
- let ops: RenameResult < Vec < SourceChange > > = defs
95
- . map ( |( .., def) | {
96
- if let Definition :: Local ( local) = def {
97
- if let Some ( self_param) = local. as_self_param ( sema. db ) {
98
- cov_mark:: hit!( rename_self_to_param) ;
99
- return rename_self_to_param ( & sema, local, self_param, new_name) ;
100
- }
101
- if new_name == "self" {
102
- cov_mark:: hit!( rename_to_self) ;
103
- return rename_to_self ( & sema, local) ;
109
+ let mut usages = def. usages ( & sema) . all ( ) ;
110
+
111
+ // FIXME: hack - removes the usage that triggered this rename operation.
112
+ match usages. references . get_mut ( & position. file_id ) . and_then ( |refs| {
113
+ refs. iter ( )
114
+ . position ( |ref_| ref_. range . contains_inclusive ( position. offset ) )
115
+ . map ( |idx| refs. remove ( idx) )
116
+ } ) {
117
+ Some ( _) => ( ) ,
118
+ None => never ! ( ) ,
119
+ } ;
120
+
121
+ let mut source_change = SourceChange :: default ( ) ;
122
+ source_change. extend ( usages. iter ( ) . map ( |( & file_id, refs) | {
123
+ ( file_id, source_edit_from_references ( refs, def, new_name) )
124
+ } ) ) ;
125
+
126
+ Ok ( source_change)
127
+ } )
128
+ . collect ( ) ,
129
+ None => defs
130
+ . map ( |( .., def) | {
131
+ if let Definition :: Local ( local) = def {
132
+ if let Some ( self_param) = local. as_self_param ( sema. db ) {
133
+ cov_mark:: hit!( rename_self_to_param) ;
134
+ return rename_self_to_param ( & sema, local, self_param, new_name) ;
135
+ }
136
+ if new_name == "self" {
137
+ cov_mark:: hit!( rename_to_self) ;
138
+ return rename_to_self ( & sema, local) ;
139
+ }
104
140
}
105
- }
106
- def . rename ( & sema , new_name )
107
- } )
108
- . collect ( ) ;
141
+ def . rename ( & sema , new_name , rename_external )
142
+ } )
143
+ . collect ( ) ,
144
+ } ;
109
145
110
146
ops?. into_iter ( )
147
+ . chain ( alias_fallback)
111
148
. reduce ( |acc, elem| acc. merge ( elem) )
112
149
. ok_or_else ( || format_err ! ( "No references found at position" ) )
113
150
}
@@ -130,6 +167,38 @@ pub(crate) fn will_rename_file(
130
167
Some ( change)
131
168
}
132
169
170
+ // FIXME: Should support `extern crate`.
171
+ fn alias_fallback (
172
+ syntax : & SyntaxNode ,
173
+ FilePosition { file_id, offset } : FilePosition ,
174
+ new_name : & str ,
175
+ ) -> Option < SourceChange > {
176
+ let use_tree = syntax
177
+ . token_at_offset ( offset)
178
+ . flat_map ( |syntax| syntax. parent_ancestors ( ) )
179
+ . find_map ( ast:: UseTree :: cast) ?;
180
+
181
+ let last_path_segment = use_tree. path ( ) ?. segments ( ) . last ( ) ?. name_ref ( ) ?;
182
+ if !last_path_segment. syntax ( ) . text_range ( ) . contains_inclusive ( offset) {
183
+ return None ;
184
+ } ;
185
+
186
+ let mut builder = SourceChangeBuilder :: new ( file_id) ;
187
+
188
+ match use_tree. rename ( ) {
189
+ Some ( rename) => {
190
+ let offset = rename. syntax ( ) . text_range ( ) ;
191
+ builder. replace ( offset, format ! ( "as {new_name}" ) ) ;
192
+ }
193
+ None => {
194
+ let offset = use_tree. syntax ( ) . text_range ( ) . end ( ) ;
195
+ builder. insert ( offset, format ! ( " as {new_name}" ) ) ;
196
+ }
197
+ }
198
+
199
+ Some ( builder. finish ( ) )
200
+ }
201
+
133
202
fn find_definitions (
134
203
sema : & Semantics < ' _ , RootDatabase > ,
135
204
syntax : & SyntaxNode ,
@@ -2686,4 +2755,27 @@ fn test() {
2686
2755
"# ,
2687
2756
) ;
2688
2757
}
2758
+
2759
+ #[ test]
2760
+ fn rename_path_inside_use_tree ( ) {
2761
+ check (
2762
+ "Baz" ,
2763
+ r#"
2764
+ mod foo { pub struct Foo; }
2765
+ mod bar { use super::Foo; }
2766
+
2767
+ use foo::Foo$0;
2768
+
2769
+ fn main() { let _: Foo; }
2770
+ "# ,
2771
+ r#"
2772
+ mod foo { pub struct Foo; }
2773
+ mod bar { use super::Baz; }
2774
+
2775
+ use foo::Foo as Baz;
2776
+
2777
+ fn main() { let _: Baz; }
2778
+ "# ,
2779
+ )
2780
+ }
2689
2781
}
0 commit comments