11use either:: Either ;
2+ use hir:: FileRangeWrapper ;
23use ide_db:: defs:: { Definition , NameRefClass } ;
4+ use std:: ops:: RangeInclusive ;
35use syntax:: {
4- SyntaxKind , SyntaxNode ,
5- ast:: { self , AstNode , HasAttrs , HasGenericParams , HasVisibility } ,
6- match_ast, ted,
6+ SyntaxElement , SyntaxKind , SyntaxNode , T , TextSize ,
7+ ast:: {
8+ self , AstNode , HasAttrs , HasGenericParams , HasVisibility , syntax_factory:: SyntaxFactory ,
9+ } ,
10+ match_ast,
11+ syntax_editor:: { Element , Position , SyntaxEditor } ,
712} ;
813
914use crate :: { AssistContext , AssistId , Assists , assist_context:: SourceChangeBuilder } ;
@@ -71,66 +76,63 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
7176 Either :: Right ( v) => Either :: Right ( ctx. sema . to_def ( v) ?) ,
7277 } ;
7378 let target = strukt_or_variant. as_ref ( ) . either ( |s| s. syntax ( ) , |v| v. syntax ( ) ) . text_range ( ) ;
74-
79+ let syntax = strukt_or_variant . as_ref ( ) . either ( |s| s . syntax ( ) , |v| v . syntax ( ) ) ;
7580 acc. add (
7681 AssistId :: refactor_rewrite ( "convert_tuple_struct_to_named_struct" ) ,
7782 "Convert to named struct" ,
7883 target,
7984 |edit| {
8085 let names = generate_names ( tuple_fields. fields ( ) ) ;
8186 edit_field_references ( ctx, edit, tuple_fields. fields ( ) , & names) ;
87+ let mut editor = edit. make_editor ( syntax) ;
8288 edit_struct_references ( ctx, edit, strukt_def, & names) ;
83- edit_struct_def ( ctx, edit, & strukt_or_variant, tuple_fields, names) ;
89+ edit_struct_def ( & mut editor, & strukt_or_variant, tuple_fields, names) ;
90+ edit. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
8491 } ,
8592 )
8693}
8794
8895fn edit_struct_def (
89- ctx : & AssistContext < ' _ > ,
90- edit : & mut SourceChangeBuilder ,
96+ editor : & mut SyntaxEditor ,
9197 strukt : & Either < ast:: Struct , ast:: Variant > ,
9298 tuple_fields : ast:: TupleFieldList ,
9399 names : Vec < ast:: Name > ,
94100) {
95101 let record_fields = tuple_fields. fields ( ) . zip ( names) . filter_map ( |( f, name) | {
96- let field = ast:: make:: record_field ( f. visibility ( ) , name, f. ty ( ) ?) . clone_for_update ( ) ;
97- ted:: insert_all (
98- ted:: Position :: first_child_of ( field. syntax ( ) ) ,
102+ let field = ast:: make:: record_field ( f. visibility ( ) , name, f. ty ( ) ?) ;
103+ let mut field_editor = SyntaxEditor :: new ( field. syntax ( ) . clone ( ) ) ;
104+ field_editor. insert_all (
105+ Position :: first_child_of ( field. syntax ( ) ) ,
99106 f. attrs ( ) . map ( |attr| attr. syntax ( ) . clone_subtree ( ) . clone_for_update ( ) . into ( ) ) . collect ( ) ,
100107 ) ;
101- Some ( field )
108+ ast :: RecordField :: cast ( field_editor . finish ( ) . new_root ( ) . clone ( ) )
102109 } ) ;
103- let record_fields = ast:: make:: record_field_list ( record_fields) ;
104- let tuple_fields_text_range = tuple_fields. syntax ( ) . text_range ( ) ;
105-
106- edit. edit_file ( ctx. vfs_file_id ( ) ) ;
110+ let make = SyntaxFactory :: without_mappings ( ) ;
111+ let record_fields = make. record_field_list ( record_fields) ;
112+ let tuple_fields_before = Position :: before ( tuple_fields. syntax ( ) ) ;
107113
108114 if let Either :: Left ( strukt) = strukt {
109115 if let Some ( w) = strukt. where_clause ( ) {
110- edit. delete ( w. syntax ( ) . text_range ( ) ) ;
111- edit. insert (
112- tuple_fields_text_range. start ( ) ,
113- ast:: make:: tokens:: single_newline ( ) . text ( ) ,
114- ) ;
115- edit. insert ( tuple_fields_text_range. start ( ) , w. syntax ( ) . text ( ) ) ;
116+ editor. delete ( w. syntax ( ) ) ;
117+ let mut insert_element = Vec :: new ( ) ;
118+ insert_element. push ( ast:: make:: tokens:: single_newline ( ) . syntax_element ( ) ) ;
119+ insert_element. push ( w. syntax ( ) . clone_for_update ( ) . syntax_element ( ) ) ;
116120 if w. syntax ( ) . last_token ( ) . is_none_or ( |t| t. kind ( ) != SyntaxKind :: COMMA ) {
117- edit . insert ( tuple_fields_text_range . start ( ) , "," ) ;
121+ insert_element . push ( ast :: make :: token ( T ! [ , ] ) . into ( ) ) ;
118122 }
119- edit. insert (
120- tuple_fields_text_range. start ( ) ,
121- ast:: make:: tokens:: single_newline ( ) . text ( ) ,
122- ) ;
123+ insert_element. push ( ast:: make:: tokens:: single_newline ( ) . syntax_element ( ) ) ;
124+ editor. insert_all ( tuple_fields_before, insert_element) ;
123125 } else {
124- edit . insert ( tuple_fields_text_range . start ( ) , ast:: make:: tokens:: single_space ( ) . text ( ) ) ;
126+ editor . insert ( tuple_fields_before , ast:: make:: tokens:: single_space ( ) ) ;
125127 }
126128 if let Some ( t) = strukt. semicolon_token ( ) {
127- edit . delete ( t. text_range ( ) ) ;
129+ editor . delete ( t) ;
128130 }
129131 } else {
130- edit . insert ( tuple_fields_text_range . start ( ) , ast:: make:: tokens:: single_space ( ) . text ( ) ) ;
132+ editor . insert ( tuple_fields_before , ast:: make:: tokens:: single_space ( ) ) ;
131133 }
132134
133- edit . replace ( tuple_fields_text_range , record_fields. to_string ( ) ) ;
135+ editor . replace ( tuple_fields . syntax ( ) , record_fields. syntax ( ) ) ;
134136}
135137
136138fn edit_struct_references (
@@ -145,27 +147,22 @@ fn edit_struct_references(
145147 } ;
146148 let usages = strukt_def. usages ( & ctx. sema ) . include_self_refs ( ) . all ( ) ;
147149
148- let edit_node = |edit : & mut SourceChangeBuilder , node : SyntaxNode | -> Option < ( ) > {
150+ let edit_node = |node : SyntaxNode | -> Option < SyntaxNode > {
151+ let make = SyntaxFactory :: without_mappings ( ) ;
149152 match_ast ! {
150153 match node {
151154 ast:: TupleStructPat ( tuple_struct_pat) => {
152- let file_range = ctx. sema. original_range_opt( & node) ?;
153- edit. edit_file( file_range. file_id. file_id( ctx. db( ) ) ) ;
154- edit. replace(
155- file_range. range,
156- ast:: make:: record_pat_with_fields(
157- tuple_struct_pat. path( ) ?,
158- ast:: make:: record_pat_field_list( tuple_struct_pat. fields( ) . zip( names) . map(
159- |( pat, name) | {
160- ast:: make:: record_pat_field(
161- ast:: make:: name_ref( & name. to_string( ) ) ,
162- pat,
163- )
164- } ,
165- ) , None ) ,
166- )
167- . to_string( ) ,
168- ) ;
155+ Some ( make. record_pat_with_fields(
156+ tuple_struct_pat. path( ) ?,
157+ ast:: make:: record_pat_field_list( tuple_struct_pat. fields( ) . zip( names) . map(
158+ |( pat, name) | {
159+ ast:: make:: record_pat_field(
160+ ast:: make:: name_ref( & name. to_string( ) ) ,
161+ pat,
162+ )
163+ } ,
164+ ) , None ) ,
165+ ) . syntax( ) . clone( ) )
169166 } ,
170167 // for tuple struct creations like Foo(42)
171168 ast:: CallExpr ( call_expr) => {
@@ -181,10 +178,8 @@ fn edit_struct_references(
181178 }
182179
183180 let arg_list = call_expr. syntax( ) . descendants( ) . find_map( ast:: ArgList :: cast) ?;
184-
185- edit. replace(
186- ctx. sema. original_range( & node) . range,
187- ast:: make:: record_expr(
181+ Some (
182+ make. record_expr(
188183 path,
189184 ast:: make:: record_expr_field_list( arg_list. args( ) . zip( names) . map(
190185 |( expr, name) | {
@@ -194,25 +189,58 @@ fn edit_struct_references(
194189 )
195190 } ,
196191 ) ) ,
197- )
198- . to_string( ) ,
199- ) ;
192+ ) . syntax( ) . clone( )
193+ )
200194 } ,
201195 _ => return None ,
202196 }
203197 }
204- Some ( ( ) )
205198 } ;
206199
207200 for ( file_id, refs) in usages {
208- edit. edit_file ( file_id. file_id ( ctx. db ( ) ) ) ;
209- for r in refs {
210- for node in r. name . syntax ( ) . ancestors ( ) {
211- if edit_node ( edit, node) . is_some ( ) {
212- break ;
201+ let source = ctx. sema . parse ( file_id) ;
202+ let source = source. syntax ( ) ;
203+
204+ let mut editor = edit. make_editor ( source) ;
205+ for r in refs. iter ( ) . rev ( ) {
206+ if let Some ( ( old_node, new_node) ) = r
207+ . name
208+ . syntax ( )
209+ . ancestors ( )
210+ . find_map ( |node| Some ( ( node. clone ( ) , edit_node ( node. clone ( ) ) ?) ) )
211+ {
212+ if let Some ( old_node) = ctx. sema . original_syntax_node_rooted ( & old_node) {
213+ editor. replace ( old_node, new_node) ;
214+ } else {
215+ let FileRangeWrapper { file_id : _, range } = ctx. sema . original_range ( & old_node) ;
216+ let parent = source. covering_element ( range) ;
217+ match parent {
218+ SyntaxElement :: Token ( token) => {
219+ editor. replace ( token, new_node. syntax_element ( ) ) ;
220+ }
221+ SyntaxElement :: Node ( parent_node) => {
222+ // replace the part of macro
223+ // ```
224+ // foo!(a, Test::A(0));
225+ // ^^^^^^^^^^^^^^^ // parent_node
226+ // ^^^^^^^^^^ // replace_range
227+ // ```
228+ let start = parent_node
229+ . children_with_tokens ( )
230+ . find ( |t| t. text_range ( ) . contains ( range. start ( ) ) ) ;
231+ let end = parent_node
232+ . children_with_tokens ( )
233+ . find ( |t| t. text_range ( ) . contains ( range. end ( ) - TextSize :: new ( 1 ) ) ) ;
234+ if let ( Some ( start) , Some ( end) ) = ( start, end) {
235+ let replace_range = RangeInclusive :: new ( start, end) ;
236+ editor. replace_all ( replace_range, vec ! [ new_node. into( ) ] ) ;
237+ }
238+ }
239+ }
213240 }
214241 }
215242 }
243+ edit. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , editor) ;
216244 }
217245}
218246
@@ -230,22 +258,28 @@ fn edit_field_references(
230258 let def = Definition :: Field ( field) ;
231259 let usages = def. usages ( & ctx. sema ) . all ( ) ;
232260 for ( file_id, refs) in usages {
233- edit. edit_file ( file_id. file_id ( ctx. db ( ) ) ) ;
261+ let source = ctx. sema . parse ( file_id) ;
262+ let source = source. syntax ( ) ;
263+ let mut editor = edit. make_editor ( source) ;
234264 for r in refs {
235- if let Some ( name_ref) = r. name . as_name_ref ( ) {
236- edit. replace ( ctx. sema . original_range ( name_ref. syntax ( ) ) . range , name. text ( ) ) ;
265+ if let Some ( name_ref) = r. name . as_name_ref ( )
266+ && let Some ( original) = ctx. sema . original_ast_node ( name_ref. clone ( ) )
267+ {
268+ editor. replace ( original. syntax ( ) , name. syntax ( ) ) ;
237269 }
238270 }
271+ edit. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , editor) ;
239272 }
240273 }
241274}
242275
243276fn generate_names ( fields : impl Iterator < Item = ast:: TupleField > ) -> Vec < ast:: Name > {
277+ let make = SyntaxFactory :: without_mappings ( ) ;
244278 fields
245279 . enumerate ( )
246280 . map ( |( i, _) | {
247281 let idx = i + 1 ;
248- ast :: make:: name ( & format ! ( "field{idx}" ) )
282+ make. name ( & format ! ( "field{idx}" ) )
249283 } )
250284 . collect ( )
251285}
@@ -1013,8 +1047,7 @@ where
10131047pub struct $0Foo(#[my_custom_attr] u32);
10141048"# ,
10151049 r#"
1016- pub struct Foo { #[my_custom_attr]
1017- field1: u32 }
1050+ pub struct Foo { #[my_custom_attr]field1: u32 }
10181051"# ,
10191052 ) ;
10201053 }
0 commit comments