@@ -6,7 +6,7 @@ use ide_db::{
66 FxHashSet , RootDatabase ,
77 defs:: Definition ,
88 helpers:: mod_path_to_ast,
9- imports:: insert_use:: { ImportScope , InsertUseConfig , insert_use } ,
9+ imports:: insert_use:: { ImportScope , InsertUseConfig , insert_use_with_editor } ,
1010 path_transform:: PathTransform ,
1111 search:: FileReference ,
1212} ;
@@ -16,12 +16,14 @@ use syntax::{
1616 SyntaxKind :: * ,
1717 SyntaxNode , T ,
1818 ast:: {
19- self , AstNode , HasAttrs , HasGenericParams , HasName , HasVisibility , edit:: AstNodeEdit , make,
19+ self , AstNode , HasAttrs , HasGenericParams , HasName , HasVisibility , edit:: AstNodeEdit ,
20+ syntax_factory:: SyntaxFactory ,
2021 } ,
21- match_ast, ted,
22+ match_ast,
23+ syntax_editor:: { Position , SyntaxEditor } ,
2224} ;
2325
24- use crate :: { AssistContext , AssistId , Assists , assist_context :: SourceChangeBuilder } ;
26+ use crate :: { AssistContext , AssistId , Assists } ;
2527
2628// Assist: extract_struct_from_enum_variant
2729//
@@ -58,6 +60,8 @@ pub(crate) fn extract_struct_from_enum_variant(
5860 "Extract struct from enum variant" ,
5961 target,
6062 |builder| {
63+ let make = SyntaxFactory :: with_mappings ( ) ;
64+ let mut editor = builder. make_editor ( variant. syntax ( ) ) ;
6165 let edition = enum_hir. krate ( ctx. db ( ) ) . edition ( ctx. db ( ) ) ;
6266 let variant_hir_name = variant_hir. name ( ctx. db ( ) ) ;
6367 let enum_module_def = ModuleDef :: from ( enum_hir) ;
@@ -73,40 +77,56 @@ pub(crate) fn extract_struct_from_enum_variant(
7377 def_file_references = Some ( references) ;
7478 continue ;
7579 }
76- builder. edit_file ( file_id. file_id ( ctx. db ( ) ) ) ;
7780 let processed = process_references (
7881 ctx,
79- builder,
8082 & mut visited_modules_set,
8183 & enum_module_def,
8284 & variant_hir_name,
8385 references,
8486 ) ;
87+ if processed. is_empty ( ) {
88+ continue ;
89+ }
90+ let mut file_editor = builder. make_editor ( processed[ 0 ] . 0 . syntax ( ) ) ;
8591 processed. into_iter ( ) . for_each ( |( path, node, import) | {
86- apply_references ( ctx. config . insert_use , path, node, import, edition)
92+ apply_references (
93+ ctx. config . insert_use ,
94+ path,
95+ node,
96+ import,
97+ edition,
98+ & mut file_editor,
99+ & make,
100+ )
87101 } ) ;
102+ file_editor. add_mappings ( make. take ( ) ) ;
103+ builder. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , file_editor) ;
88104 }
89- builder. edit_file ( ctx. vfs_file_id ( ) ) ;
90105
91- let variant = builder. make_mut ( variant. clone ( ) ) ;
92106 if let Some ( references) = def_file_references {
93107 let processed = process_references (
94108 ctx,
95- builder,
96109 & mut visited_modules_set,
97110 & enum_module_def,
98111 & variant_hir_name,
99112 references,
100113 ) ;
101114 processed. into_iter ( ) . for_each ( |( path, node, import) | {
102- apply_references ( ctx. config . insert_use , path, node, import, edition)
115+ apply_references (
116+ ctx. config . insert_use ,
117+ path,
118+ node,
119+ import,
120+ edition,
121+ & mut editor,
122+ & make,
123+ )
103124 } ) ;
104125 }
105126
106- let generic_params = enum_ast
107- . generic_param_list ( )
108- . and_then ( |known_generics| extract_generic_params ( & known_generics, & field_list) ) ;
109- let generics = generic_params. as_ref ( ) . map ( |generics| generics. clone_for_update ( ) ) ;
127+ let generic_params = enum_ast. generic_param_list ( ) . and_then ( |known_generics| {
128+ extract_generic_params ( & make, & known_generics, & field_list)
129+ } ) ;
110130
111131 // resolve GenericArg in field_list to actual type
112132 let field_list = if let Some ( ( target_scope, source_scope) ) =
@@ -124,25 +144,45 @@ pub(crate) fn extract_struct_from_enum_variant(
124144 }
125145 }
126146 } else {
127- field_list. clone_for_update ( )
147+ field_list. clone ( )
128148 } ;
129149
130- let def =
131- create_struct_def ( variant_name. clone ( ) , & variant, & field_list, generics, & enum_ast) ;
150+ let ( comments_for_struct, comments_to_delete) =
151+ collect_variant_comments ( & make, variant. syntax ( ) ) ;
152+ for element in & comments_to_delete {
153+ editor. delete ( element. clone ( ) ) ;
154+ }
155+
156+ let def = create_struct_def (
157+ & make,
158+ variant_name. clone ( ) ,
159+ & field_list,
160+ generic_params. clone ( ) ,
161+ & enum_ast,
162+ ) ;
132163
133164 let enum_ast = variant. parent_enum ( ) ;
134165 let indent = enum_ast. indent_level ( ) ;
135166 let def = def. indent ( indent) ;
136167
137- ted:: insert_all (
138- ted:: Position :: before ( enum_ast. syntax ( ) ) ,
139- vec ! [
140- def. syntax( ) . clone( ) . into( ) ,
141- make:: tokens:: whitespace( & format!( "\n \n {indent}" ) ) . into( ) ,
142- ] ,
168+ let mut insert_items: Vec < SyntaxElement > = Vec :: new ( ) ;
169+ for attr in enum_ast. attrs ( ) {
170+ insert_items. push ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
171+ insert_items. push ( make. whitespace ( "\n " ) . into ( ) ) ;
172+ }
173+ insert_items. extend ( comments_for_struct) ;
174+ insert_items. push ( def. syntax ( ) . clone ( ) . into ( ) ) ;
175+ insert_items. push ( make. whitespace ( & format ! ( "\n \n {indent}" ) ) . into ( ) ) ;
176+ editor. insert_all_with_whitespace (
177+ Position :: before ( enum_ast. syntax ( ) ) ,
178+ insert_items,
179+ & make,
143180 ) ;
144181
145- update_variant ( & variant, generic_params. map ( |g| g. clone_for_update ( ) ) ) ;
182+ update_variant ( & make, & mut editor, & variant, generic_params) ;
183+
184+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
185+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
146186 } ,
147187 )
148188}
@@ -184,6 +224,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En
184224}
185225
186226fn extract_generic_params (
227+ make : & SyntaxFactory ,
187228 known_generics : & ast:: GenericParamList ,
188229 field_list : & Either < ast:: RecordFieldList , ast:: TupleFieldList > ,
189230) -> Option < ast:: GenericParamList > {
@@ -201,7 +242,7 @@ fn extract_generic_params(
201242 } ;
202243
203244 let generics = generics. into_iter ( ) . filter_map ( |( param, tag) | tag. then_some ( param) ) ;
204- tagged_one. then ( || make:: generic_param_list ( generics) )
245+ tagged_one. then ( || make. generic_param_list ( generics) )
205246}
206247
207248fn tag_generics_in_variant ( ty : & ast:: Type , generics : & mut [ ( ast:: GenericParam , bool ) ] ) -> bool {
@@ -250,118 +291,114 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b
250291}
251292
252293fn create_struct_def (
294+ make : & SyntaxFactory ,
253295 name : ast:: Name ,
254- variant : & ast:: Variant ,
255296 field_list : & Either < ast:: RecordFieldList , ast:: TupleFieldList > ,
256297 generics : Option < ast:: GenericParamList > ,
257298 enum_ : & ast:: Enum ,
258299) -> ast:: Struct {
259300 let enum_vis = enum_. visibility ( ) ;
260301
261- let insert_vis = |node : & ' _ SyntaxNode , vis : & ' _ SyntaxNode | {
262- let vis = vis. clone_for_update ( ) ;
263- ted:: insert ( ted:: Position :: before ( node) , vis) ;
264- } ;
265-
266302 // for fields without any existing visibility, use visibility of enum
267303 let field_list: ast:: FieldList = match field_list {
268304 Either :: Left ( field_list) => {
269305 if let Some ( vis) = & enum_vis {
270- field_list
271- . fields ( )
272- . filter ( |field| field. visibility ( ) . is_none ( ) )
273- . filter_map ( |field| field. name ( ) )
274- . for_each ( |it| insert_vis ( it. syntax ( ) , vis. syntax ( ) ) ) ;
306+ let new_fields = field_list. fields ( ) . map ( |field| {
307+ if field. visibility ( ) . is_none ( )
308+ && let Some ( name) = field. name ( )
309+ && let Some ( ty) = field. ty ( )
310+ {
311+ make. record_field ( Some ( vis. clone ( ) ) , name, ty)
312+ } else {
313+ field
314+ }
315+ } ) ;
316+ make. record_field_list ( new_fields) . into ( )
317+ } else {
318+ field_list. clone ( ) . into ( )
275319 }
276-
277- field_list. clone ( ) . into ( )
278320 }
279321 Either :: Right ( field_list) => {
280322 if let Some ( vis) = & enum_vis {
281- field_list
282- . fields ( )
283- . filter ( |field| field. visibility ( ) . is_none ( ) )
284- . filter_map ( |field| field. ty ( ) )
285- . for_each ( |it| insert_vis ( it. syntax ( ) , vis. syntax ( ) ) ) ;
323+ let new_fields = field_list. fields ( ) . map ( |field| {
324+ if field. visibility ( ) . is_none ( )
325+ && let Some ( ty) = field. ty ( )
326+ {
327+ make. tuple_field ( Some ( vis. clone ( ) ) , ty)
328+ } else {
329+ field
330+ }
331+ } ) ;
332+ make. tuple_field_list ( new_fields) . into ( )
333+ } else {
334+ field_list. clone ( ) . into ( )
286335 }
287-
288- field_list. clone ( ) . into ( )
289336 }
290337 } ;
291338
292- let strukt = make:: struct_ ( enum_vis, name, generics, field_list) . clone_for_update ( ) ;
293-
294- // take comments from variant
295- ted:: insert_all (
296- ted:: Position :: first_child_of ( strukt. syntax ( ) ) ,
297- take_all_comments ( variant. syntax ( ) ) ,
298- ) ;
299-
300- // copy attributes from enum
301- ted:: insert_all (
302- ted:: Position :: first_child_of ( strukt. syntax ( ) ) ,
303- enum_
304- . attrs ( )
305- . flat_map ( |it| {
306- vec ! [ it. syntax( ) . clone_for_update( ) . into( ) , make:: tokens:: single_newline( ) . into( ) ]
307- } )
308- . collect ( ) ,
309- ) ;
310-
311- strukt
339+ make. struct_ ( enum_vis, name, generics, field_list)
312340}
313341
314- fn update_variant ( variant : & ast:: Variant , generics : Option < ast:: GenericParamList > ) -> Option < ( ) > {
342+ fn update_variant (
343+ make : & SyntaxFactory ,
344+ editor : & mut SyntaxEditor ,
345+ variant : & ast:: Variant ,
346+ generics : Option < ast:: GenericParamList > ,
347+ ) -> Option < ( ) > {
315348 let name = variant. name ( ) ?;
316349 let generic_args = generics
317350 . filter ( |generics| generics. generic_params ( ) . count ( ) > 0 )
318351 . map ( |generics| generics. to_generic_args ( ) ) ;
319352 // FIXME: replace with a `ast::make` constructor
320353 let ty = match generic_args {
321- Some ( generic_args) => make:: ty ( & format ! ( "{name}{generic_args}" ) ) ,
322- None => make:: ty ( & name. text ( ) ) ,
354+ Some ( generic_args) => make. ty ( & format ! ( "{name}{generic_args}" ) ) ,
355+ None => make. ty ( & name. text ( ) ) ,
323356 } ;
324357
325358 // change from a record to a tuple field list
326- let tuple_field = make:: tuple_field ( None , ty) ;
327- let field_list = make:: tuple_field_list ( iter:: once ( tuple_field) ) . clone_for_update ( ) ;
328- ted :: replace ( variant. field_list ( ) ?. syntax ( ) , field_list. syntax ( ) ) ;
359+ let tuple_field = make. tuple_field ( None , ty) ;
360+ let field_list = make. tuple_field_list ( iter:: once ( tuple_field) ) ;
361+ editor . replace ( variant. field_list ( ) ?. syntax ( ) , field_list. syntax ( ) ) ;
329362
330363 // remove any ws after the name
331364 if let Some ( ws) = name
332365 . syntax ( )
333366 . siblings_with_tokens ( syntax:: Direction :: Next )
334367 . find_map ( |tok| tok. into_token ( ) . filter ( |tok| tok. kind ( ) == WHITESPACE ) )
335368 {
336- ted :: remove ( SyntaxElement :: Token ( ws ) ) ;
369+ editor . delete ( ws ) ;
337370 }
338371
339372 Some ( ( ) )
340373}
341374
342- // Note: this also detaches whitespace after comments,
343- // since `SyntaxNode::splice_children` (and by extension `ted::insert_all_raw`)
344- // detaches nodes. If we only took the comments, we'd leave behind the old whitespace.
345- fn take_all_comments ( node : & SyntaxNode ) -> Vec < SyntaxElement > {
346- let mut remove_next_ws = false ;
347- node. children_with_tokens ( )
348- . filter_map ( move |child| match child. kind ( ) {
375+ fn collect_variant_comments (
376+ make : & SyntaxFactory ,
377+ node : & SyntaxNode ,
378+ ) -> ( Vec < SyntaxElement > , Vec < SyntaxElement > ) {
379+ let mut to_insert: Vec < SyntaxElement > = Vec :: new ( ) ;
380+ let mut to_delete: Vec < SyntaxElement > = Vec :: new ( ) ;
381+ let mut after_comment = false ;
382+
383+ for child in node. children_with_tokens ( ) {
384+ match child. kind ( ) {
349385 COMMENT => {
350- remove_next_ws = true ;
351- child. detach ( ) ;
352- Some ( child)
386+ after_comment = true ;
387+ to_insert . push ( child. clone ( ) ) ;
388+ to_delete . push ( child) ;
353389 }
354- WHITESPACE if remove_next_ws => {
355- remove_next_ws = false ;
356- child . detach ( ) ;
357- Some ( make :: tokens :: single_newline ( ) . into ( ) )
390+ WHITESPACE if after_comment => {
391+ after_comment = false ;
392+ to_insert . push ( make . whitespace ( " \n " ) . into ( ) ) ;
393+ to_delete . push ( child ) ;
358394 }
359395 _ => {
360- remove_next_ws = false ;
361- None
396+ after_comment = false ;
362397 }
363- } )
364- . collect ( )
398+ }
399+ }
400+
401+ ( to_insert, to_delete)
365402}
366403
367404fn apply_references (
@@ -370,20 +407,27 @@ fn apply_references(
370407 node : SyntaxNode ,
371408 import : Option < ( ImportScope , hir:: ModPath ) > ,
372409 edition : Edition ,
410+ editor : & mut SyntaxEditor ,
411+ make : & SyntaxFactory ,
373412) {
374413 if let Some ( ( scope, path) ) = import {
375- insert_use ( & scope, mod_path_to_ast ( & path, edition) , & insert_use_cfg) ;
414+ insert_use_with_editor (
415+ & scope,
416+ mod_path_to_ast ( & path, edition) ,
417+ & insert_use_cfg,
418+ editor,
419+ make,
420+ ) ;
376421 }
377422 // deep clone to prevent cycle
378- let path = make:: path_from_segments ( iter:: once ( segment. clone_subtree ( ) ) , false ) ;
379- ted :: insert_raw ( ted :: Position :: before ( segment. syntax ( ) ) , path . clone_for_update ( ) . syntax ( ) ) ;
380- ted :: insert_raw ( ted :: Position :: before ( segment. syntax ( ) ) , make :: token ( T ! [ '(' ] ) ) ;
381- ted :: insert_raw ( ted :: Position :: after ( & node) , make:: token ( T ! [ ')' ] ) ) ;
423+ let path = make. path_from_segments ( iter:: once ( segment. clone ( ) ) , false ) ;
424+ editor . insert ( Position :: before ( segment. syntax ( ) ) , make . token ( T ! [ '(' ] ) ) ;
425+ editor . insert ( Position :: before ( segment. syntax ( ) ) , path . syntax ( ) ) ;
426+ editor . insert ( Position :: after ( & node) , make. token ( T ! [ ')' ] ) ) ;
382427}
383428
384429fn process_references (
385430 ctx : & AssistContext < ' _ > ,
386- builder : & mut SourceChangeBuilder ,
387431 visited_modules : & mut FxHashSet < Module > ,
388432 enum_module_def : & ModuleDef ,
389433 variant_hir_name : & Name ,
@@ -394,8 +438,6 @@ fn process_references(
394438 refs. into_iter ( )
395439 . flat_map ( |reference| {
396440 let ( segment, scope_node, module) = reference_to_node ( & ctx. sema , reference) ?;
397- let segment = builder. make_mut ( segment) ;
398- let scope_node = builder. make_syntax_mut ( scope_node) ;
399441 if !visited_modules. contains ( & module) {
400442 let cfg =
401443 ctx. config . find_path_config ( ctx. sema . is_nightly ( module. krate ( ctx. sema . db ) ) ) ;
0 commit comments