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