11use hir:: { HasVisibility , sym} ;
2- use ide_db:: text_edit:: TextRange ;
32use ide_db:: {
43 FxHashMap , FxHashSet ,
54 assists:: AssistId ,
@@ -8,7 +7,9 @@ use ide_db::{
87 search:: { FileReference , SearchScope } ,
98} ;
109use 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} ;
1213
1314use crate :: {
1415 assist_context:: { AssistContext , Assists , SourceChangeBuilder } ,
@@ -62,13 +63,10 @@ fn destructure_struct_binding_impl(
6263 data : & StructEditData ,
6364) {
6465 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) ;
7270}
7371
7472struct StructEditData {
@@ -173,64 +171,57 @@ fn get_names_in_scope(
173171 Some ( names)
174172}
175173
176- fn build_assignment_edit (
174+ fn destructure_pat (
177175 _ctx : & AssistContext < ' _ > ,
178- builder : & mut SourceChangeBuilder ,
176+ editor : & mut SyntaxEditor ,
179177 data : & StructEditData ,
180178 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 ;
183181
184182 let struct_path = mod_path_to_ast ( & data. struct_def_path , data. edition ) ;
185183 let is_ref = ident_pat. ref_token ( ) . is_some ( ) ;
186184 let is_mut = ident_pat. mut_token ( ) . is_some ( ) ;
187185
186+ let make = SyntaxFactory :: with_mappings ( ) ;
188187 let new_pat = match data. kind {
189188 hir:: StructKind :: Tuple => {
190189 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) )
193192 } ) ;
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) )
195194 }
196195 hir:: StructKind :: Record => {
197196 let fields = field_names. iter ( ) . map ( |( old_name, new_name) | {
198197 // Use shorthand syntax if possible
199198 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) )
201200 } 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) ) ) ,
209204 )
210205 }
211206 } ) ;
207+ let field_list = make
208+ . record_pat_field_list ( fields, data. has_private_members . then_some ( make. rest_pat ( ) ) ) ;
212209
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) )
218211 }
219- hir:: StructKind :: Unit => ast :: make:: path_pat ( struct_path) ,
212+ hir:: StructKind :: Unit => make. path_pat ( struct_path) ,
220213 } ;
221214
222215 // If the binding is nested inside a record, we need to wrap the new
223216 // 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 ( )
229219 } else {
230- NewPat :: Pat ( new_pat. clone_for_update ( ) )
220+ new_pat. syntax ( ) . clone ( )
231221 } ;
232222
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) ;
234225}
235226
236227fn 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
267258 name
268259}
269260
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 (
292262 ctx : & AssistContext < ' _ > ,
293- builder : & mut SourceChangeBuilder ,
263+ editor : & mut SyntaxEditor ,
294264 data : & StructEditData ,
295265 field_names : & FxHashMap < SmolStr , SmolStr > ,
296- ) -> Vec < StructUsageEdit > {
297- data. usages
266+ ) {
267+ let make = SyntaxFactory :: with_mappings ( ) ;
268+ let edits = data
269+ . usages
298270 . 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+ }
301277}
302278
303279fn build_usage_edit (
304280 ctx : & AssistContext < ' _ > ,
305- builder : & mut SourceChangeBuilder ,
281+ make : & SyntaxFactory ,
306282 data : & StructEditData ,
307283 usage : & FileReference ,
308284 field_names : & FxHashMap < SmolStr , SmolStr > ,
309- ) -> Option < StructUsageEdit > {
285+ ) -> Option < ( SyntaxNode , SyntaxNode ) > {
310286 match usage. name . syntax ( ) . ancestors ( ) . find_map ( ast:: FieldExpr :: cast) {
311287 Some ( field_expr) => Some ( {
312288 let field_name: SmolStr = field_expr. name_ref ( ) ?. to_string ( ) . into ( ) ;
313289 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) ) ;
315291
316292 // If struct binding is a reference, we might need to deref field usages
317293 if data. is_ref {
318294 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 ( ) ,
322298 )
323299 } 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 ( ) )
328301 }
329302 } ) ,
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+ ) ) ,
349307 }
350308}
351309
0 commit comments