1
1
use hir:: { HasVisibility , sym} ;
2
- use ide_db:: text_edit:: TextRange ;
3
2
use ide_db:: {
4
3
FxHashMap , FxHashSet ,
5
4
assists:: AssistId ,
@@ -8,7 +7,9 @@ use ide_db::{
8
7
search:: { FileReference , SearchScope } ,
9
8
} ;
10
9
use itertools:: Itertools ;
11
- use syntax:: { AstNode , Edition , SmolStr , SyntaxNode , ToSmolStr , ast, ted} ;
10
+ use syntax:: ast:: syntax_factory:: SyntaxFactory ;
11
+ use syntax:: syntax_editor:: SyntaxEditor ;
12
+ use syntax:: { AstNode , Edition , SmolStr , SyntaxNode , ToSmolStr , ast} ;
12
13
13
14
use crate :: {
14
15
assist_context:: { AssistContext , Assists , SourceChangeBuilder } ,
@@ -62,13 +63,10 @@ fn destructure_struct_binding_impl(
62
63
data : & StructEditData ,
63
64
) {
64
65
let field_names = generate_field_names ( ctx, data) ;
65
- let assignment_edit = build_assignment_edit ( ctx, builder, data, & field_names) ;
66
- let usage_edits = build_usage_edits ( ctx, builder, data, & field_names. into_iter ( ) . collect ( ) ) ;
67
-
68
- assignment_edit. apply ( ) ;
69
- for edit in usage_edits {
70
- edit. apply ( builder) ;
71
- }
66
+ let mut editor = builder. make_editor ( data. ident_pat . syntax ( ) ) ;
67
+ destructure_pat ( ctx, & mut editor, data, & field_names) ;
68
+ update_usages ( ctx, & mut editor, data, & field_names. into_iter ( ) . collect ( ) ) ;
69
+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
72
70
}
73
71
74
72
struct StructEditData {
@@ -173,64 +171,57 @@ fn get_names_in_scope(
173
171
Some ( names)
174
172
}
175
173
176
- fn build_assignment_edit (
174
+ fn destructure_pat (
177
175
_ctx : & AssistContext < ' _ > ,
178
- builder : & mut SourceChangeBuilder ,
176
+ editor : & mut SyntaxEditor ,
179
177
data : & StructEditData ,
180
178
field_names : & [ ( SmolStr , SmolStr ) ] ,
181
- ) -> AssignmentEdit {
182
- let ident_pat = builder . make_mut ( data. ident_pat . clone ( ) ) ;
179
+ ) {
180
+ let ident_pat = & data. ident_pat ;
183
181
184
182
let struct_path = mod_path_to_ast ( & data. struct_def_path , data. edition ) ;
185
183
let is_ref = ident_pat. ref_token ( ) . is_some ( ) ;
186
184
let is_mut = ident_pat. mut_token ( ) . is_some ( ) ;
187
185
186
+ let make = SyntaxFactory :: with_mappings ( ) ;
188
187
let new_pat = match data. kind {
189
188
hir:: StructKind :: Tuple => {
190
189
let ident_pats = field_names. iter ( ) . map ( |( _, new_name) | {
191
- let name = ast :: make:: name ( new_name) ;
192
- ast:: Pat :: from ( ast :: make:: ident_pat ( is_ref, is_mut, name) )
190
+ let name = make. name ( new_name) ;
191
+ ast:: Pat :: from ( make. ident_pat ( is_ref, is_mut, name) )
193
192
} ) ;
194
- ast:: Pat :: TupleStructPat ( ast :: make:: tuple_struct_pat ( struct_path, ident_pats) )
193
+ ast:: Pat :: TupleStructPat ( make. tuple_struct_pat ( struct_path, ident_pats) )
195
194
}
196
195
hir:: StructKind :: Record => {
197
196
let fields = field_names. iter ( ) . map ( |( old_name, new_name) | {
198
197
// Use shorthand syntax if possible
199
198
if old_name == new_name && !is_mut {
200
- ast :: make:: record_pat_field_shorthand ( ast :: make:: name_ref ( old_name) )
199
+ make. record_pat_field_shorthand ( make. name_ref ( old_name) )
201
200
} else {
202
- ast:: make:: record_pat_field (
203
- ast:: make:: name_ref ( old_name) ,
204
- ast:: Pat :: IdentPat ( ast:: make:: ident_pat (
205
- is_ref,
206
- is_mut,
207
- ast:: make:: name ( new_name) ,
208
- ) ) ,
201
+ make. record_pat_field (
202
+ make. name_ref ( old_name) ,
203
+ ast:: Pat :: IdentPat ( make. ident_pat ( is_ref, is_mut, make. name ( new_name) ) ) ,
209
204
)
210
205
}
211
206
} ) ;
207
+ let field_list = make
208
+ . record_pat_field_list ( fields, data. has_private_members . then_some ( make. rest_pat ( ) ) ) ;
212
209
213
- let field_list = ast:: make:: record_pat_field_list (
214
- fields,
215
- data. has_private_members . then_some ( ast:: make:: rest_pat ( ) ) ,
216
- ) ;
217
- ast:: Pat :: RecordPat ( ast:: make:: record_pat_with_fields ( struct_path, field_list) )
210
+ ast:: Pat :: RecordPat ( make. record_pat_with_fields ( struct_path, field_list) )
218
211
}
219
- hir:: StructKind :: Unit => ast :: make:: path_pat ( struct_path) ,
212
+ hir:: StructKind :: Unit => make. path_pat ( struct_path) ,
220
213
} ;
221
214
222
215
// If the binding is nested inside a record, we need to wrap the new
223
216
// destructured pattern in a non-shorthand record field
224
- let new_pat = if data. is_nested {
225
- let record_pat_field =
226
- ast:: make:: record_pat_field ( ast:: make:: name_ref ( & ident_pat. to_string ( ) ) , new_pat)
227
- . clone_for_update ( ) ;
228
- NewPat :: RecordPatField ( record_pat_field)
217
+ let destructured_pat = if data. is_nested {
218
+ make. record_pat_field ( make. name_ref ( & ident_pat. to_string ( ) ) , new_pat) . syntax ( ) . clone ( )
229
219
} else {
230
- NewPat :: Pat ( new_pat. clone_for_update ( ) )
220
+ new_pat. syntax ( ) . clone ( )
231
221
} ;
232
222
233
- AssignmentEdit { old_pat : ident_pat, new_pat }
223
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
224
+ editor. replace ( data. ident_pat . syntax ( ) , destructured_pat) ;
234
225
}
235
226
236
227
fn generate_field_names ( ctx : & AssistContext < ' _ > , data : & StructEditData ) -> Vec < ( SmolStr , SmolStr ) > {
@@ -267,85 +258,52 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> Sm
267
258
name
268
259
}
269
260
270
- struct AssignmentEdit {
271
- old_pat : ast:: IdentPat ,
272
- new_pat : NewPat ,
273
- }
274
-
275
- enum NewPat {
276
- Pat ( ast:: Pat ) ,
277
- RecordPatField ( ast:: RecordPatField ) ,
278
- }
279
-
280
- impl AssignmentEdit {
281
- fn apply ( self ) {
282
- match self . new_pat {
283
- NewPat :: Pat ( pat) => ted:: replace ( self . old_pat . syntax ( ) , pat. syntax ( ) ) ,
284
- NewPat :: RecordPatField ( record_pat_field) => {
285
- ted:: replace ( self . old_pat . syntax ( ) , record_pat_field. syntax ( ) )
286
- }
287
- }
288
- }
289
- }
290
-
291
- fn build_usage_edits (
261
+ fn update_usages (
292
262
ctx : & AssistContext < ' _ > ,
293
- builder : & mut SourceChangeBuilder ,
263
+ editor : & mut SyntaxEditor ,
294
264
data : & StructEditData ,
295
265
field_names : & FxHashMap < SmolStr , SmolStr > ,
296
- ) -> Vec < StructUsageEdit > {
297
- data. usages
266
+ ) {
267
+ let make = SyntaxFactory :: with_mappings ( ) ;
268
+ let edits = data
269
+ . usages
298
270
. iter ( )
299
- . filter_map ( |r| build_usage_edit ( ctx, builder, data, r, field_names) )
300
- . collect_vec ( )
271
+ . filter_map ( |r| build_usage_edit ( ctx, & make, data, r, field_names) )
272
+ . collect_vec ( ) ;
273
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
274
+ for ( old, new) in edits {
275
+ editor. replace ( old, new) ;
276
+ }
301
277
}
302
278
303
279
fn build_usage_edit (
304
280
ctx : & AssistContext < ' _ > ,
305
- builder : & mut SourceChangeBuilder ,
281
+ make : & SyntaxFactory ,
306
282
data : & StructEditData ,
307
283
usage : & FileReference ,
308
284
field_names : & FxHashMap < SmolStr , SmolStr > ,
309
- ) -> Option < StructUsageEdit > {
285
+ ) -> Option < ( SyntaxNode , SyntaxNode ) > {
310
286
match usage. name . syntax ( ) . ancestors ( ) . find_map ( ast:: FieldExpr :: cast) {
311
287
Some ( field_expr) => Some ( {
312
288
let field_name: SmolStr = field_expr. name_ref ( ) ?. to_string ( ) . into ( ) ;
313
289
let new_field_name = field_names. get ( & field_name) ?;
314
- let new_expr = ast :: make:: expr_path ( ast:: make:: ext:: ident_path ( new_field_name) ) ;
290
+ let new_expr = make. expr_path ( ast:: make:: ext:: ident_path ( new_field_name) ) ;
315
291
316
292
// If struct binding is a reference, we might need to deref field usages
317
293
if data. is_ref {
318
294
let ( replace_expr, ref_data) = determine_ref_and_parens ( ctx, & field_expr) ;
319
- StructUsageEdit :: IndexField (
320
- builder . make_mut ( replace_expr ) ,
321
- ref_data. wrap_expr ( new_expr) . clone_for_update ( ) ,
295
+ (
296
+ replace_expr . syntax ( ) . clone_for_update ( ) ,
297
+ ref_data. wrap_expr ( new_expr) . syntax ( ) . clone_for_update ( ) ,
322
298
)
323
299
} else {
324
- StructUsageEdit :: IndexField (
325
- builder. make_mut ( field_expr) . into ( ) ,
326
- new_expr. clone_for_update ( ) ,
327
- )
300
+ ( field_expr. syntax ( ) . clone ( ) , new_expr. syntax ( ) . clone ( ) )
328
301
}
329
302
} ) ,
330
- None => Some ( StructUsageEdit :: Path ( usage. range ) ) ,
331
- }
332
- }
333
-
334
- enum StructUsageEdit {
335
- Path ( TextRange ) ,
336
- IndexField ( ast:: Expr , ast:: Expr ) ,
337
- }
338
-
339
- impl StructUsageEdit {
340
- fn apply ( self , edit : & mut SourceChangeBuilder ) {
341
- match self {
342
- StructUsageEdit :: Path ( target_expr) => {
343
- edit. replace ( target_expr, "todo!()" ) ;
344
- }
345
- StructUsageEdit :: IndexField ( target_expr, replace_with) => {
346
- ted:: replace ( target_expr. syntax ( ) , replace_with. syntax ( ) )
347
- }
348
- }
303
+ None => Some ( (
304
+ usage. name . syntax ( ) . as_node ( ) . unwrap ( ) . clone ( ) ,
305
+ make. expr_macro ( ast:: make:: ext:: ident_path ( "todo" ) , make. arg_list ( [ ] ) ) . syntax ( ) . clone ( ) ,
306
+ ) ) ,
349
307
}
350
308
}
351
309
0 commit comments