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 ,
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 } ,
7
12
} ;
8
13
9
14
use crate :: { AssistContext , AssistId , Assists , assist_context:: SourceChangeBuilder } ;
@@ -71,66 +76,63 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
71
76
Either :: Right ( v) => Either :: Right ( ctx. sema . to_def ( v) ?) ,
72
77
} ;
73
78
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 ( ) ) ;
75
80
acc. add (
76
81
AssistId :: refactor_rewrite ( "convert_tuple_struct_to_named_struct" ) ,
77
82
"Convert to named struct" ,
78
83
target,
79
84
|edit| {
80
85
let names = generate_names ( tuple_fields. fields ( ) ) ;
81
86
edit_field_references ( ctx, edit, tuple_fields. fields ( ) , & names) ;
87
+ let mut editor = edit. make_editor ( syntax) ;
82
88
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) ;
84
91
} ,
85
92
)
86
93
}
87
94
88
95
fn edit_struct_def (
89
- ctx : & AssistContext < ' _ > ,
90
- edit : & mut SourceChangeBuilder ,
96
+ editor : & mut SyntaxEditor ,
91
97
strukt : & Either < ast:: Struct , ast:: Variant > ,
92
98
tuple_fields : ast:: TupleFieldList ,
93
99
names : Vec < ast:: Name > ,
94
100
) {
95
101
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 ( ) ) ,
99
106
f. attrs ( ) . map ( |attr| attr. syntax ( ) . clone_subtree ( ) . clone_for_update ( ) . into ( ) ) . collect ( ) ,
100
107
) ;
101
- Some ( field )
108
+ ast :: RecordField :: cast ( field_editor . finish ( ) . new_root ( ) . clone ( ) )
102
109
} ) ;
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 ( ) ) ;
107
113
108
114
if let Either :: Left ( strukt) = strukt {
109
115
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 ( ) ) ;
116
120
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 ( ) ) ;
118
122
}
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) ;
123
125
} 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 ( ) ) ;
125
127
}
126
128
if let Some ( t) = strukt. semicolon_token ( ) {
127
- edit . delete ( t. text_range ( ) ) ;
129
+ editor . delete ( t) ;
128
130
}
129
131
} 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 ( ) ) ;
131
133
}
132
134
133
- edit . replace ( tuple_fields_text_range , record_fields. to_string ( ) ) ;
135
+ editor . replace ( tuple_fields . syntax ( ) , record_fields. syntax ( ) ) ;
134
136
}
135
137
136
138
fn edit_struct_references (
@@ -145,27 +147,22 @@ fn edit_struct_references(
145
147
} ;
146
148
let usages = strukt_def. usages ( & ctx. sema ) . include_self_refs ( ) . all ( ) ;
147
149
148
- let edit_node = |edit : & mut SourceChangeBuilder , node : SyntaxNode | -> Option < ( ) > {
150
+ let edit_node = |node : SyntaxNode | -> Option < SyntaxNode > {
151
+ let make = SyntaxFactory :: without_mappings ( ) ;
149
152
match_ast ! {
150
153
match node {
151
154
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( ) )
169
166
} ,
170
167
// for tuple struct creations like Foo(42)
171
168
ast:: CallExpr ( call_expr) => {
@@ -181,10 +178,8 @@ fn edit_struct_references(
181
178
}
182
179
183
180
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(
188
183
path,
189
184
ast:: make:: record_expr_field_list( arg_list. args( ) . zip( names) . map(
190
185
|( expr, name) | {
@@ -194,25 +189,58 @@ fn edit_struct_references(
194
189
)
195
190
} ,
196
191
) ) ,
197
- )
198
- . to_string( ) ,
199
- ) ;
192
+ ) . syntax( ) . clone( )
193
+ )
200
194
} ,
201
195
_ => return None ,
202
196
}
203
197
}
204
- Some ( ( ) )
205
198
} ;
206
199
207
200
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
+ }
213
240
}
214
241
}
215
242
}
243
+ edit. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , editor) ;
216
244
}
217
245
}
218
246
@@ -230,22 +258,28 @@ fn edit_field_references(
230
258
let def = Definition :: Field ( field) ;
231
259
let usages = def. usages ( & ctx. sema ) . all ( ) ;
232
260
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) ;
234
264
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 ( ) ) ;
237
269
}
238
270
}
271
+ edit. add_file_edits ( file_id. file_id ( ctx. db ( ) ) , editor) ;
239
272
}
240
273
}
241
274
}
242
275
243
276
fn generate_names ( fields : impl Iterator < Item = ast:: TupleField > ) -> Vec < ast:: Name > {
277
+ let make = SyntaxFactory :: without_mappings ( ) ;
244
278
fields
245
279
. enumerate ( )
246
280
. map ( |( i, _) | {
247
281
let idx = i + 1 ;
248
- ast :: make:: name ( & format ! ( "field{idx}" ) )
282
+ make. name ( & format ! ( "field{idx}" ) )
249
283
} )
250
284
. collect ( )
251
285
}
@@ -1013,8 +1047,7 @@ where
1013
1047
pub struct $0Foo(#[my_custom_attr] u32);
1014
1048
"# ,
1015
1049
r#"
1016
- pub struct Foo { #[my_custom_attr]
1017
- field1: u32 }
1050
+ pub struct Foo { #[my_custom_attr]field1: u32 }
1018
1051
"# ,
1019
1052
) ;
1020
1053
}
0 commit comments