1
1
use either:: Either ;
2
+ use hir:: FileRangeWrapper ;
2
3
use ide_db:: defs:: { Definition , NameRefClass } ;
4
+ use std:: ops:: RangeInclusive ;
3
5
use syntax:: {
4
- SyntaxKind , SyntaxNode , T ,
6
+ SyntaxElement , SyntaxKind , SyntaxNode , T , TextSize ,
5
7
ast:: { self , AstNode , HasAttrs , HasGenericParams , HasVisibility } ,
6
8
match_ast,
7
9
syntax_editor:: { Element , Position , SyntaxEditor } ,
@@ -80,8 +82,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
80
82
|edit| {
81
83
let names = generate_names ( tuple_fields. fields ( ) ) ;
82
84
edit_field_references ( ctx, edit, tuple_fields. fields ( ) , & names) ;
83
- edit_struct_references ( ctx, edit, strukt_def, & names) ;
84
85
let mut editor = edit. make_editor ( syntax) ;
86
+ edit_struct_references ( ctx, edit, strukt_def, & names) ;
85
87
edit_struct_def ( & mut editor, & strukt_or_variant, tuple_fields, names) ;
86
88
edit. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
87
89
} ,
@@ -142,27 +144,21 @@ fn edit_struct_references(
142
144
} ;
143
145
let usages = strukt_def. usages ( & ctx. sema ) . include_self_refs ( ) . all ( ) ;
144
146
145
- let edit_node = |edit : & mut SourceChangeBuilder , node : SyntaxNode | -> Option < ( ) > {
147
+ let edit_node = |node : SyntaxNode | -> Option < SyntaxNode > {
146
148
match_ast ! {
147
149
match node {
148
150
ast:: TupleStructPat ( tuple_struct_pat) => {
149
- let file_range = ctx. sema. original_range_opt( & node) ?;
150
- edit. edit_file( file_range. file_id. file_id( ctx. db( ) ) ) ;
151
- edit. replace(
152
- file_range. range,
153
- ast:: make:: record_pat_with_fields(
154
- tuple_struct_pat. path( ) ?,
155
- ast:: make:: record_pat_field_list( tuple_struct_pat. fields( ) . zip( names) . map(
156
- |( pat, name) | {
157
- ast:: make:: record_pat_field(
158
- ast:: make:: name_ref( & name. to_string( ) ) ,
159
- pat,
160
- )
161
- } ,
162
- ) , None ) ,
163
- )
164
- . to_string( ) ,
165
- ) ;
151
+ Some ( ast:: make:: record_pat_with_fields(
152
+ tuple_struct_pat. path( ) ?,
153
+ ast:: make:: record_pat_field_list( tuple_struct_pat. fields( ) . zip( names) . map(
154
+ |( pat, name) | {
155
+ ast:: make:: record_pat_field(
156
+ ast:: make:: name_ref( & name. to_string( ) ) ,
157
+ pat,
158
+ )
159
+ } ,
160
+ ) , None ) ,
161
+ ) . syntax( ) . clone_for_update( ) )
166
162
} ,
167
163
// for tuple struct creations like Foo(42)
168
164
ast:: CallExpr ( call_expr) => {
@@ -179,8 +175,7 @@ fn edit_struct_references(
179
175
180
176
let arg_list = call_expr. syntax( ) . descendants( ) . find_map( ast:: ArgList :: cast) ?;
181
177
182
- edit. replace(
183
- ctx. sema. original_range( & node) . range,
178
+ Some (
184
179
ast:: make:: record_expr(
185
180
path,
186
181
ast:: make:: record_expr_field_list( arg_list. args( ) . zip( names) . map(
@@ -191,25 +186,58 @@ fn edit_struct_references(
191
186
)
192
187
} ,
193
188
) ) ,
194
- )
195
- . to_string( ) ,
196
- ) ;
189
+ ) . syntax( ) . clone_for_update( )
190
+ )
197
191
} ,
198
192
_ => return None ,
199
193
}
200
194
}
201
- Some ( ( ) )
202
195
} ;
203
196
204
197
for ( file_id, refs) in usages {
205
- edit. edit_file ( file_id. file_id ( ctx. db ( ) ) ) ;
206
- for r in refs {
207
- for node in r. name . syntax ( ) . ancestors ( ) {
208
- if edit_node ( edit, node) . is_some ( ) {
209
- break ;
198
+ let source = ctx. sema . parse ( file_id) ;
199
+ let source = source. syntax ( ) ;
200
+
201
+ let mut editor = edit. make_editor ( source) ;
202
+ for r in refs. iter ( ) . rev ( ) {
203
+ if let Some ( ( old_node, new_node) ) = r
204
+ . name
205
+ . syntax ( )
206
+ . ancestors ( )
207
+ . find_map ( |node| Some ( ( node. clone ( ) , edit_node ( node. clone ( ) ) ?) ) )
208
+ {
209
+ if let Some ( old_node) = ctx. sema . original_syntax_node_rooted ( & old_node) {
210
+ editor. replace ( old_node, new_node) ;
211
+ } else {
212
+ let FileRangeWrapper { file_id : _, range } = ctx. sema . original_range ( & old_node) ;
213
+ let parent = source. covering_element ( range) ;
214
+ match parent {
215
+ SyntaxElement :: Token ( token) => {
216
+ editor. replace ( token, new_node. syntax_element ( ) ) ;
217
+ }
218
+ SyntaxElement :: Node ( parent_node) => {
219
+ // replace the part of macro
220
+ // ```
221
+ // foo!(a, Test::A(0));
222
+ // ^^^^^^^^^^^^^^^ // parent_node
223
+ // ^^^^^^^^^^ // replace_range
224
+ // ```
225
+ let start = parent_node
226
+ . children_with_tokens ( )
227
+ . find ( |t| t. text_range ( ) . contains ( range. start ( ) ) ) ;
228
+ let end = parent_node
229
+ . children_with_tokens ( )
230
+ . find ( |t| t. text_range ( ) . contains ( range. end ( ) - TextSize :: new ( 1 ) ) ) ;
231
+ if let ( Some ( start) , Some ( end) ) = ( start, end) {
232
+ let replace_range = RangeInclusive :: new ( start, end) ;
233
+ editor. replace_all ( replace_range, vec ! [ new_node. into( ) ] ) ;
234
+ }
235
+ }
236
+ }
210
237
}
211
238
}
212
239
}
240
+ edit. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , editor) ;
213
241
}
214
242
}
215
243
@@ -227,12 +255,17 @@ fn edit_field_references(
227
255
let def = Definition :: Field ( field) ;
228
256
let usages = def. usages ( & ctx. sema ) . all ( ) ;
229
257
for ( file_id, refs) in usages {
230
- edit. edit_file ( file_id. file_id ( ctx. db ( ) ) ) ;
258
+ let source = ctx. sema . parse ( file_id) ;
259
+ let source = source. syntax ( ) ;
260
+ let mut editor = edit. make_editor ( source) ;
231
261
for r in refs {
232
- if let Some ( name_ref) = r. name . as_name_ref ( ) {
233
- edit. replace ( ctx. sema . original_range ( name_ref. syntax ( ) ) . range , name. text ( ) ) ;
262
+ if let Some ( name_ref) = r. name . as_name_ref ( )
263
+ && let Some ( original) = ctx. sema . original_ast_node ( name_ref. clone ( ) )
264
+ {
265
+ editor. replace ( original. syntax ( ) , name. syntax ( ) ) ;
234
266
}
235
267
}
268
+ edit. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , editor) ;
236
269
}
237
270
}
238
271
}
@@ -242,7 +275,7 @@ fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Nam
242
275
. enumerate ( )
243
276
. map ( |( i, _) | {
244
277
let idx = i + 1 ;
245
- ast:: make:: name ( & format ! ( "field{idx}" ) )
278
+ ast:: make:: name ( & format ! ( "field{idx}" ) ) . clone_for_update ( )
246
279
} )
247
280
. collect ( )
248
281
}
0 commit comments