@@ -2,26 +2,30 @@ use std::ops::Range;
2
2
3
3
use hir:: { HasCrate , Module , ModuleDef } ;
4
4
use ide_db:: {
5
- FxHashSet , RootDatabase ,
5
+ FileId ,
6
+ FxHashSet ,
7
+ RootDatabase ,
6
8
assists:: AssistId ,
7
9
defs:: Definition ,
8
10
helpers:: mod_path_to_ast,
11
+ // this relies on ted
9
12
imports:: insert_use:: { ImportScope , InsertUseConfig , insert_use} ,
10
13
path_transform:: PathTransform ,
11
14
search:: FileReference ,
12
15
source_change:: SourceChangeBuilder ,
13
16
} ;
14
17
use itertools:: Itertools ;
15
18
use syntax:: {
16
- AstNode , Edition , SyntaxElement , SyntaxKind , SyntaxNode , T ,
19
+ AstNode , Edition , NodeOrToken , SyntaxElement , SyntaxKind , SyntaxNode , T ,
17
20
algo:: find_node_at_range,
18
21
ast:: {
19
- self , HasArgList , HasAttrs , HasGenericParams , HasName , HasVisibility ,
22
+ self , HasArgList , HasGenericParams , HasName , HasVisibility ,
20
23
edit:: { AstNodeEdit , IndentLevel } ,
21
24
make,
25
+ syntax_factory:: SyntaxFactory ,
22
26
} ,
23
27
match_ast,
24
- ted :: { self , Element } ,
28
+ syntax_editor :: { Element , Position , SyntaxEditor } ,
25
29
} ;
26
30
27
31
use crate :: { AssistContext , Assists } ;
@@ -85,9 +89,11 @@ pub(crate) fn extract_struct_from_function_signature(
85
89
"Extract struct from signature of a function" ,
86
90
target,
87
91
|builder| {
92
+ let make = SyntaxFactory :: with_mappings ( ) ;
93
+ let mut editor = builder. make_editor ( func. syntax ( ) ) ;
88
94
let n_new_lifetimes = field_list. fields ( ) . filter_map ( |f|f. ty ( ) ) . map ( |t|new_life_time_count ( & t) ) . sum ( ) ;
89
95
let edition = fn_hir. krate ( ctx. db ( ) ) . edition ( ctx. db ( ) ) ;
90
- let enum_module_def = ModuleDef :: from ( fn_hir) ;
96
+ let function_module_def = ModuleDef :: from ( fn_hir) ;
91
97
92
98
let usages = Definition :: Function ( fn_hir) . usages ( & ctx. sema ) . all ( ) ;
93
99
let mut visited_modules_set = FxHashSet :: default ( ) ;
@@ -102,46 +108,36 @@ pub(crate) fn extract_struct_from_function_signature(
102
108
def_file_references = Some ( references) ;
103
109
continue ;
104
110
}
105
- builder. edit_file ( file_id. file_id ( ctx. db ( ) ) ) ;
106
111
let processed = process_references (
107
112
ctx,
108
- builder,
109
113
& mut visited_modules_set,
110
- & enum_module_def ,
114
+ & function_module_def ,
111
115
references,
112
116
name. clone ( )
113
117
) ;
114
118
processed. into_iter ( ) . for_each ( |( path, import) | {
115
- apply_references ( ctx. config . insert_use , path , import, edition, used_params_range. clone ( ) , & field_list,
116
- name. clone ( ) ,
119
+ apply_references ( builder , ctx. config . insert_use , path , import, edition, used_params_range. clone ( ) , & field_list,
120
+ name. clone ( ) , file_id . file_id ( ctx . db ( ) ) ,
117
121
) ;
118
122
} ) ;
119
123
}
120
124
121
125
tracing:: info!( "extract_struct_from_function_signature: starting edit" ) ;
122
- builder. edit_file ( ctx. vfs_file_id ( ) ) ;
123
- // atl the make muts should generally before any edits happen
124
- let func_mut = builder. make_mut ( func. clone ( ) ) ;
125
126
// if in impl block then put struct before the impl block
126
127
let ( indent, syntax) = param_list. self_param ( ) . and_then ( |_|ctx. find_node_at_range :: < ast:: Impl > ( ) )
127
- . map ( |imp|( imp. indent_level ( ) , builder. make_syntax_mut ( imp. syntax ( ) . clone ( ) ) ) ) . unwrap_or ( ( func. indent_level ( ) , func_mut. syntax ( ) . clone ( ) ) ) ;
128
- builder. make_mut ( param_list. clone ( ) ) ;
129
- let used_param_list = used_param_list. into_iter ( ) . map ( |p| builder. make_mut ( p) ) . collect_vec ( ) ;
128
+ . map ( |imp|( imp. indent_level ( ) , imp. syntax ( ) . clone ( ) ) ) . unwrap_or ( ( func. indent_level ( ) , func. syntax ( ) . clone ( ) ) ) ;
130
129
tracing:: info!( "extract_struct_from_function_signature: editing main file" ) ;
131
- // this has to be after the edit_file (order matters)
132
- // func and param_list must be "mut" for the effect to work on used_param_list
133
130
if let Some ( references) = def_file_references {
134
131
let processed = process_references (
135
132
ctx,
136
- builder,
137
133
& mut visited_modules_set,
138
- & enum_module_def ,
134
+ & function_module_def ,
139
135
references,
140
136
name. clone ( )
141
137
) ;
142
138
processed. into_iter ( ) . for_each ( |( path, import) | {
143
- apply_references ( ctx. config . insert_use , path, import, edition, used_params_range. clone ( ) , & field_list,
144
- name. clone ( ) ,
139
+ apply_references ( builder , ctx. config . insert_use , path, import, edition, used_params_range. clone ( ) , & field_list,
140
+ name. clone ( ) , ctx . vfs_file_id ( )
145
141
) ;
146
142
} ) ;
147
143
}
@@ -173,23 +169,25 @@ pub(crate) fn extract_struct_from_function_signature(
173
169
}
174
170
}
175
171
} else {
176
- field_list. clone_for_update ( )
172
+ field_list
177
173
} ;
178
- field_list. fields ( ) . filter_map ( |f|f. ty ( ) ) . try_for_each ( |t|generate_new_lifetimes ( & t, & mut generics) ) ;
174
+ field_list. fields ( ) . filter_map ( |f|f. ty ( ) ) . try_for_each ( |t|generate_new_lifetimes ( & mut editor , & t, & mut generics) ) ;
179
175
tracing:: info!( "extract_struct_from_function_signature: collecting fields" ) ;
180
- let def = create_struct_def ( name. clone ( ) , & func_mut , & used_param_list, & field_list, generics) ;
176
+ let def = create_struct_def ( & make , name. clone ( ) , & func , & used_param_list, & field_list, generics) ;
181
177
tracing:: info!( "extract_struct_from_function_signature: creating struct" ) ;
182
178
let def = def. indent ( indent) ;
183
- ted :: insert_all (
184
- ted :: Position :: before ( syntax) ,
179
+ editor . insert_all (
180
+ Position :: before ( syntax) ,
185
181
vec ! [
186
182
def. syntax( ) . clone( ) . into( ) ,
187
183
make:: tokens:: whitespace( & format!( "\n \n {indent}" ) ) . into( ) ,
188
184
] ,
189
185
) ;
190
186
tracing:: info!( "extract_struct_from_function_signature: inserting struct {def}" ) ;
191
- update_function ( name, generic_params. map ( |g| g. clone_for_update ( ) ) , & used_param_list, n_new_lifetimes) . unwrap ( ) ;
187
+ update_function ( & mut editor , name, generic_params. map ( |g| g. clone_for_update ( ) ) , & used_param_list, n_new_lifetimes) . unwrap ( ) ;
192
188
tracing:: info!( "extract_struct_from_function_signature: updating function signature and parameter uses" ) ;
189
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
190
+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
193
191
} ,
194
192
)
195
193
}
@@ -215,6 +213,7 @@ fn extract_field_list(
215
213
}
216
214
217
215
fn update_function (
216
+ editor : & mut SyntaxEditor ,
218
217
name : ast:: Name ,
219
218
generics : Option < ast:: GenericParamList > ,
220
219
used_param_list : & [ ast:: Param ] ,
@@ -256,11 +255,13 @@ fn update_function(
256
255
257
256
// it is fine to unwrap() to because there is at least one parameter (if there is no parameters
258
257
// the code action will not show)
259
- let start_idx = used_param_list. first ( ) . unwrap ( ) . syntax ( ) . index ( ) ;
260
- let end_idx = used_param_list. last ( ) . unwrap ( ) . syntax ( ) . index ( ) ;
261
- let used_params_range = start_idx..end_idx + 1 ;
258
+ let start_idx = used_param_list. first ( ) . unwrap ( ) . syntax ( ) ;
259
+ let end_idx = used_param_list. last ( ) . unwrap ( ) . syntax ( ) ;
262
260
let new = vec ! [ param. syntax( ) . syntax_element( ) ] ;
263
- used_param_list. first ( ) . unwrap ( ) . syntax ( ) . parent ( ) ?. splice_children ( used_params_range, new) ;
261
+ editor. replace_all (
262
+ NodeOrToken :: Node ( start_idx. clone ( ) ) ..=NodeOrToken :: Node ( end_idx. clone ( ) ) ,
263
+ new,
264
+ ) ;
264
265
// no need update uses of parameters in function, because we destructure the struct
265
266
Some ( ( ) )
266
267
}
@@ -271,6 +272,7 @@ fn pat_to_name(pat: ast::Pat) -> Option<ast::Name> {
271
272
}
272
273
}
273
274
fn create_struct_def (
275
+ editor : & SyntaxFactory ,
274
276
name : ast:: Name ,
275
277
func : & ast:: Fn ,
276
278
param_ast : & [ ast:: Param ] ,
@@ -279,36 +281,20 @@ fn create_struct_def(
279
281
) -> ast:: Struct {
280
282
let fn_vis = func. visibility ( ) ;
281
283
282
- let insert_vis = |node : & ' _ SyntaxNode , vis : & ' _ SyntaxNode | {
283
- let vis = vis. clone_for_update ( ) ;
284
- ted:: insert ( ted:: Position :: before ( node) , vis) ;
285
- } ;
286
-
287
- // for fields without any existing visibility, use visibility of enum
288
- let field_list = {
289
- if let Some ( vis) = & fn_vis {
290
- field_list
291
- . fields ( )
292
- . filter ( |field| field. visibility ( ) . is_none ( ) )
293
- . filter_map ( |field| field. name ( ) )
294
- . for_each ( |it| insert_vis ( it. syntax ( ) , vis. syntax ( ) ) ) ;
295
- }
296
-
297
- field_list
298
- } ;
299
284
// if we do not expleictly copy over comments/attribures they just get lost
300
285
// TODO: what about comments/attributes in between parameters
301
- param_ast. iter ( ) . zip ( field_list. fields ( ) ) . for_each ( |( param, field) | {
302
- let elements = take_all_comments ( param. clone ( ) ) ;
303
- ted:: insert_all ( ted:: Position :: first_child_of ( field. syntax ( ) ) , elements) ;
304
- ted:: insert_all (
305
- ted:: Position :: first_child_of ( field. syntax ( ) ) ,
306
- param
307
- . attrs ( )
308
- . flat_map ( |it| [ it. syntax ( ) . clone ( ) . into ( ) , make:: tokens:: single_newline ( ) . into ( ) ] )
309
- . collect ( ) ,
310
- ) ;
311
- } ) ;
286
+ // param_ast.iter().zip(field_list.fields()).for_each(|(param, field)| {
287
+ // editor.attr_inner(meta)
288
+ // let elements = take_all_comments(param.clone());
289
+ // editor.insert_all(Position::first_child_of(field.syntax()), elements);
290
+ // editor.insert_all(
291
+ // Position::first_child_of(field.syntax()),
292
+ // param
293
+ // .attrs()
294
+ // .flat_map(|it| [it.syntax().clone().into(), make::tokens::single_newline().into()])
295
+ // .collect(),
296
+ // );
297
+ // });
312
298
let field_list = field_list. indent ( IndentLevel :: single ( ) ) ;
313
299
314
300
make:: struct_ ( fn_vis, name, generics, field_list. into ( ) ) . clone_for_update ( )
@@ -381,6 +367,7 @@ fn contains_impl_trait(ty: &ast::Type) -> bool {
381
367
ty. syntax ( ) . descendants ( ) . any ( |ty| ty. kind ( ) == ast:: ImplTraitType :: kind ( ) )
382
368
}
383
369
fn generate_new_lifetimes (
370
+ editor : & mut SyntaxEditor ,
384
371
ty : & ast:: Type ,
385
372
existing_type_param_list : & mut Option < ast:: GenericParamList > ,
386
373
) -> Option < ( ) > {
@@ -395,15 +382,15 @@ fn generate_new_lifetimes(
395
382
. get_or_insert ( make:: generic_param_list ( std:: iter:: empty ( ) ) . clone_for_update ( ) )
396
383
. add_generic_param ( make:: lifetime_param ( new_lt. clone ( ) ) . clone_for_update ( ) . into ( ) ) ;
397
384
398
- ted :: replace ( lt. syntax ( ) , new_lt. clone_for_update ( ) . syntax ( ) ) ;
385
+ // editor. replace(lt.syntax(), new_lt.clone_for_update().syntax());
399
386
} else if let Some ( r) = ast:: RefType :: cast ( token. clone ( ) )
400
387
&& r. lifetime ( ) . is_none ( )
401
388
{
402
389
let new_lt = generate_unique_lifetime_param_name ( existing_type_param_list) ?;
403
390
existing_type_param_list
404
391
. get_or_insert ( make:: generic_param_list ( std:: iter:: empty ( ) ) . clone_for_update ( ) )
405
392
. add_generic_param ( make:: lifetime_param ( new_lt. clone ( ) ) . clone_for_update ( ) . into ( ) ) ;
406
- ted :: insert ( ted :: Position :: after ( r. amp_token ( ) ?) , new_lt. clone_for_update ( ) . syntax ( ) ) ;
393
+ // editor. insert(Position::after(r.amp_token()?), new_lt.clone_for_update().syntax());
407
394
}
408
395
// TODO: nominal types that have only lifetimes
409
396
// struct Bar<'a, 'b> { f: &'a &'b i32 }
@@ -423,12 +410,12 @@ fn tag_generics_in_function_signature(
423
410
ast:: GenericParam :: LifetimeParam ( lt)
424
411
if matches ! ( token. kind( ) , T ![ lifetime_ident] ) =>
425
412
{
426
- if let Some ( lt) = lt. lifetime ( ) {
427
- if lt. text ( ) . as_str ( ) == token. text ( ) {
428
- * tag = true ;
429
- tagged_one = true ;
430
- break ;
431
- }
413
+ if let Some ( lt) = lt. lifetime ( )
414
+ && lt. text ( ) . as_str ( ) == token. text ( )
415
+ {
416
+ * tag = true ;
417
+ tagged_one = true ;
418
+ break ;
432
419
}
433
420
}
434
421
param if matches ! ( token. kind( ) , T ![ ident] ) => {
@@ -485,7 +472,6 @@ fn existing_definition(
485
472
486
473
fn process_references (
487
474
ctx : & AssistContext < ' _ > ,
488
- builder : & mut SourceChangeBuilder ,
489
475
visited_modules : & mut FxHashSet < Module > ,
490
476
function_module_def : & ModuleDef ,
491
477
refs : Vec < FileReference > ,
@@ -497,8 +483,6 @@ fn process_references(
497
483
refs. into_iter ( )
498
484
. flat_map ( |reference| {
499
485
let ( call, scope_node, module) = reference_to_node ( & ctx. sema , reference) ?;
500
- let scope_node = builder. make_syntax_mut ( scope_node) ;
501
- let call = builder. make_mut ( call) ;
502
486
if !visited_modules. contains ( & module) {
503
487
let mod_path = module. find_use_path (
504
488
ctx. sema . db ,
@@ -538,15 +522,20 @@ fn reference_to_node(
538
522
}
539
523
540
524
fn apply_references (
525
+ builder : & mut SourceChangeBuilder ,
541
526
insert_use_cfg : InsertUseConfig ,
542
527
call : CallExpr ,
543
528
import : Option < ( ImportScope , hir:: ModPath ) > ,
544
529
edition : Edition ,
545
530
used_params_range : Range < usize > ,
546
531
field_list : & ast:: RecordFieldList ,
547
532
name : ast:: Name ,
533
+ file_id : impl Into < FileId > ,
548
534
) -> Option < ( ) > {
535
+ let make = SyntaxFactory :: with_mappings ( ) ;
536
+ let mut editor = builder. make_editor ( call. syntax ( ) ) ;
549
537
if let Some ( ( scope, path) ) = import {
538
+ let scope = builder. make_import_scope_mut ( scope) ;
550
539
insert_use ( & scope, mod_path_to_ast ( & path, edition) , & insert_use_cfg) ;
551
540
}
552
541
@@ -570,18 +559,24 @@ fn apply_references(
570
559
} )
571
560
. collect :: < Option < Vec < _ > > > ( ) ?,
572
561
) ;
562
+ let first = call. arg_list ( ) ?. args ( ) . nth ( match call {
563
+ // for some reason the indices for parameters of method go in increments of 3s (but
564
+ // start at 4 to accommodate the self parameter)
565
+ CallExpr :: Method ( _) => used_params_range. start / 3 - 1 ,
566
+ CallExpr :: Normal ( _) => used_params_range. start - 1 ,
567
+ } ) ?;
568
+ let last = call. arg_list ( ) ?. args ( ) . nth ( match call {
569
+ // for some reason the indices for parameters of method go in increments of 3s (but
570
+ // start at 4 to accommodate the self parameter)
571
+ CallExpr :: Method ( _) => used_params_range. end / 3 - 1 ,
572
+ CallExpr :: Normal ( _) => used_params_range. end / 3 ,
573
+ } ) ?;
573
574
let record_expr = make:: record_expr ( path, fields) . clone_for_update ( ) ;
574
-
575
- // range for method definition used parames seems to be off
576
- call. arg_list ( ) ?. syntax ( ) . splice_children (
577
- match call {
578
- // but at call sites methods don't include the self argument as part of the "arg list" so
579
- // we have to decduct one parameters (for some reason length 3) from range
580
- CallExpr :: Method ( _) => ( used_params_range. start - 3 ) ..( used_params_range. end - 3 ) ,
581
- CallExpr :: Normal ( _) => used_params_range,
582
- } ,
575
+ editor. replace_all (
576
+ NodeOrToken :: Node ( first. syntax ( ) . clone ( ) ) ..=NodeOrToken :: Node ( last. syntax ( ) . clone ( ) ) ,
583
577
vec ! [ record_expr. syntax( ) . syntax_element( ) ] ,
584
578
) ;
579
+ builder. add_file_edits ( file_id, editor) ;
585
580
Some ( ( ) )
586
581
}
587
582
0 commit comments