@@ -14,9 +14,10 @@ use ide_db::{
14
14
use itertools:: Itertools ;
15
15
use syntax:: {
16
16
AstNode , Edition , SyntaxElement , SyntaxKind , SyntaxNode , T ,
17
+ algo:: find_node_at_range,
17
18
ast:: {
18
- self , CallExpr , HasArgList , HasAttrs , HasGenericArgs , HasGenericParams , HasName ,
19
- HasVisibility , RecordExprField ,
19
+ self , HasArgList , HasAttrs , HasGenericParams , HasName , HasVisibility , MethodCallExpr ,
20
+ RecordExprField ,
20
21
edit:: { AstNodeEdit , IndentLevel } ,
21
22
make,
22
23
} ,
@@ -111,10 +112,9 @@ pub(crate) fn extract_struct_from_function_signature(
111
112
references,
112
113
name. clone ( )
113
114
) ;
114
- processed. into_iter ( ) . for_each ( |( path, node , import) | {
115
- apply_references ( ctx. config . insert_use , path, node , import, edition, used_params_range. clone ( ) , & field_list,
115
+ processed. into_iter ( ) . for_each ( |( path, import) | {
116
+ apply_references ( ctx. config . insert_use , path , import, edition, used_params_range. clone ( ) , & field_list,
116
117
name. clone ( ) ,
117
- // new_lifetime_count
118
118
) ;
119
119
} ) ;
120
120
}
@@ -136,10 +136,9 @@ pub(crate) fn extract_struct_from_function_signature(
136
136
references,
137
137
name. clone ( )
138
138
) ;
139
- processed. into_iter ( ) . for_each ( |( path, node , import) | {
140
- apply_references ( ctx. config . insert_use , path, node , import, edition, used_params_range. clone ( ) , & field_list,
139
+ processed. into_iter ( ) . for_each ( |( path, import) | {
140
+ apply_references ( ctx. config . insert_use , path, import, edition, used_params_range. clone ( ) , & field_list,
141
141
name. clone ( ) ,
142
- // new_lifetime_count
143
142
) ;
144
143
} ) ;
145
144
}
@@ -178,12 +177,13 @@ pub(crate) fn extract_struct_from_function_signature(
178
177
let def = create_struct_def ( name. clone ( ) , & fn_ast_mut, & used_param_list, & field_list, generics) ;
179
178
tracing:: info!( "extract_struct_from_function_signature: creating struct" ) ;
180
179
181
- let indent = fn_ast_mut. indent_level ( ) ;
180
+ // if in impl block then put struct before the impl block
181
+ let ( indent, syntax) = param_list. self_param ( ) . and_then ( |_|ctx. find_node_at_range :: < ast:: Impl > ( ) ) . map ( |impl_|builder. make_mut ( impl_) ) . map ( |impl_|( impl_. indent_level ( ) , impl_. syntax ( ) . clone ( ) ) ) . unwrap_or ( ( fn_ast. indent_level ( ) , fn_ast_mut. syntax ( ) . clone ( ) ) ) ;
182
182
let def = def. indent ( indent) ;
183
183
184
184
185
185
ted:: insert_all (
186
- ted:: Position :: before ( fn_ast_mut . syntax ( ) ) ,
186
+ ted:: Position :: before ( syntax) ,
187
187
vec ! [
188
188
def. syntax( ) . clone( ) . into( ) ,
189
189
make:: tokens:: whitespace( & format!( "\n \n {indent}" ) ) . into( ) ,
@@ -477,14 +477,15 @@ fn process_references(
477
477
function_module_def : & ModuleDef ,
478
478
refs : Vec < FileReference > ,
479
479
name : ast:: Name ,
480
- ) -> Vec < ( ast :: PathSegment , SyntaxNode , Option < ( ImportScope , hir:: ModPath ) > ) > {
480
+ ) -> Vec < ( CallExpr , Option < ( ImportScope , hir:: ModPath ) > ) > {
481
481
// we have to recollect here eagerly as we are about to edit the tree we need to calculate the changes
482
482
// and corresponding nodes up front
483
483
let name = make:: name_ref ( name. text_non_mutable ( ) ) ;
484
484
refs. into_iter ( )
485
485
. flat_map ( |reference| {
486
- let ( segment , scope_node, module) = reference_to_node ( & ctx. sema , reference) ?;
486
+ let ( call , scope_node, module) = reference_to_node ( & ctx. sema , reference) ?;
487
487
let scope_node = builder. make_syntax_mut ( scope_node) ;
488
+ let call = builder. make_mut ( call) ;
488
489
if !visited_modules. contains ( & module) {
489
490
let mod_path = module. find_use_path (
490
491
ctx. sema . db ,
@@ -497,81 +498,57 @@ fn process_references(
497
498
mod_path. push_segment ( hir:: Name :: new_root ( name. text_non_mutable ( ) ) . clone ( ) ) ;
498
499
let scope = ImportScope :: find_insert_use_container ( & scope_node, & ctx. sema ) ?;
499
500
visited_modules. insert ( module) ;
500
- return Some ( ( segment , scope_node , Some ( ( scope, mod_path) ) ) ) ;
501
+ return Some ( ( call , Some ( ( scope, mod_path) ) ) ) ;
501
502
}
502
503
}
503
- Some ( ( segment , scope_node , None ) )
504
+ Some ( ( call , None ) )
504
505
} )
505
506
. collect ( )
506
507
}
507
508
fn reference_to_node (
508
509
sema : & hir:: Semantics < ' _ , RootDatabase > ,
509
510
reference : FileReference ,
510
- ) -> Option < ( ast:: PathSegment , SyntaxNode , hir:: Module ) > {
511
- // filter out the reference in macro (seems to be probalamtic with lifetimes/generics arguments)
512
- let segment =
513
- reference. name . as_name_ref ( ) ?. syntax ( ) . parent ( ) . and_then ( ast:: PathSegment :: cast) ?;
511
+ ) -> Option < ( CallExpr , SyntaxNode , hir:: Module ) > {
512
+ // find neareat method call/call to the reference because different amount of parents between
513
+ // name and full call depending on if its method call or normal call
514
+ let node =
515
+ find_node_at_range :: < CallExpr > ( reference. name . as_name_ref ( ) ?. syntax ( ) , reference. range ) ?;
514
516
515
517
// let segment_range = segment.syntax().text_range();
516
518
// if segment_range != reference.range {
517
519
// return None;
518
520
// }
519
521
520
- let parent = segment. parent_path ( ) . syntax ( ) . parent ( ) ?;
521
- let expr_or_pat = match_ast ! {
522
- match parent {
523
- ast:: PathExpr ( _it) => parent. parent( ) ?,
524
- ast:: RecordExpr ( _it) => parent,
525
- ast:: TupleStructPat ( _it) => parent,
526
- ast:: RecordPat ( _it) => parent,
527
- _ => return None ,
528
- }
529
- } ;
530
- let module = sema. scope ( & expr_or_pat) ?. module ( ) ;
522
+ let module = sema. scope ( & node. syntax ( ) ) ?. module ( ) ;
531
523
532
- Some ( ( segment . clone_for_update ( ) , expr_or_pat , module) )
524
+ Some ( ( node . clone ( ) , node . syntax ( ) . clone ( ) , module) )
533
525
}
534
526
535
527
fn apply_references (
536
528
insert_use_cfg : InsertUseConfig ,
537
- segment : ast:: PathSegment ,
538
- node : SyntaxNode ,
529
+ call : CallExpr ,
539
530
import : Option < ( ImportScope , hir:: ModPath ) > ,
540
531
edition : Edition ,
541
532
used_params_range : Range < usize > ,
542
533
field_list : & ast:: RecordFieldList ,
543
534
name : ast:: Name ,
544
- // new_lifetime_count: usize,
545
535
) -> Option < ( ) > {
546
536
if let Some ( ( scope, path) ) = import {
547
537
insert_use ( & scope, mod_path_to_ast ( & path, edition) , & insert_use_cfg) ;
548
538
}
549
- // TODO: figure out lifetimes in referecnecs
550
- // becauuse we have to convert from segment being non turbofish, also only need
551
- // generics/lifetimes that are used in struct possibly not all the no ones for the original call
552
- // if no specified lifetimes/generics we just give empty one
553
- // if new_lifetime_count > 0 {
554
- // (0..new_lifetime_count).for_each(|_| {
555
- // segment
556
- // .get_or_create_generic_arg_list()
557
- // .add_generic_arg(make::lifetime_arg(make::lifetime("'_")).clone_for_update().into())
558
- // });
559
- // }
560
539
561
540
// current idea: the lifetimes can be inferred from the call
562
- if let Some ( generics) = segment. generic_arg_list ( ) {
563
- ted:: remove ( generics. syntax ( ) ) ;
564
- }
565
- ted:: replace ( segment. name_ref ( ) ?. syntax ( ) , name. clone_for_update ( ) . syntax ( ) ) ;
566
- // deep clone to prevent cycle
567
- let path = make:: path_from_segments ( std:: iter:: once ( segment. clone_subtree ( ) ) , false ) ;
568
- // TODO: do I need to to method call to
569
- let call = CallExpr :: cast ( node) ?;
541
+ let path = make:: path_from_text ( name. text_non_mutable ( ) ) ;
570
542
let fields = make:: record_expr_field_list (
571
543
call. arg_list ( ) ?
572
544
. args ( )
573
- . skip ( used_params_range. start - 1 )
574
- . take ( used_params_range. end - used_params_range. start )
545
+ . skip ( match call {
546
+ // for some reason the indices for parameters of method go in increments of 3s (but
547
+ // start at 4 to accommodate the self parameter)
548
+ CallExpr :: Method ( _) => used_params_range. start / 3 - 1 ,
549
+ CallExpr :: Normal ( _) => used_params_range. start - 1 ,
550
+ } )
551
+ // the zip implicitly makes that it will only take the amount of parameters required
575
552
. zip ( field_list. fields ( ) )
576
553
. map ( |e| {
577
554
e. 1 . name ( ) . map ( |name| -> RecordExprField {
@@ -582,11 +559,57 @@ fn apply_references(
582
559
) ;
583
560
let record_expr = make:: record_expr ( path, fields) . clone_for_update ( ) ;
584
561
585
- call. arg_list ( ) ?
586
- . syntax ( )
587
- . splice_children ( used_params_range, vec ! [ record_expr. syntax( ) . syntax_element( ) ] ) ;
562
+ // range for method definition used parames seems to be off
563
+ call. arg_list ( ) ?. syntax ( ) . splice_children (
564
+ match call {
565
+ // but at call sites methods don't include the self argument as part of the "arg list" so
566
+ // we have to decduct one parameters (for some reason length 3) from range
567
+ CallExpr :: Method ( _) => ( used_params_range. start - 3 ) ..( used_params_range. end - 3 ) ,
568
+ CallExpr :: Normal ( _) => used_params_range,
569
+ } ,
570
+ vec ! [ record_expr. syntax( ) . syntax_element( ) ] ,
571
+ ) ;
588
572
Some ( ( ) )
589
573
}
574
+
575
+ #[ derive( Debug , Clone ) ]
576
+ enum CallExpr {
577
+ Normal ( ast:: CallExpr ) ,
578
+ Method ( ast:: MethodCallExpr ) ,
579
+ }
580
+ impl AstNode for CallExpr {
581
+ fn can_cast ( kind : SyntaxKind ) -> bool
582
+ where
583
+ Self : Sized ,
584
+ {
585
+ kind == ast:: MethodCallExpr :: kind ( ) && kind == ast:: CallExpr :: kind ( )
586
+ }
587
+
588
+ fn cast ( syntax : SyntaxNode ) -> Option < Self >
589
+ where
590
+ Self : Sized ,
591
+ {
592
+ ast:: CallExpr :: cast ( syntax. clone ( ) )
593
+ . map ( CallExpr :: Normal )
594
+ . or ( MethodCallExpr :: cast ( syntax) . map ( CallExpr :: Method ) )
595
+ }
596
+
597
+ fn syntax ( & self ) -> & SyntaxNode {
598
+ match self {
599
+ CallExpr :: Normal ( call_expr) => call_expr. syntax ( ) ,
600
+ CallExpr :: Method ( method_call_expr) => method_call_expr. syntax ( ) ,
601
+ }
602
+ }
603
+ }
604
+ impl HasArgList for CallExpr {
605
+ fn arg_list ( & self ) -> Option < ast:: ArgList > {
606
+ match self {
607
+ CallExpr :: Normal ( call_expr) => call_expr. arg_list ( ) ,
608
+ CallExpr :: Method ( method_call_expr) => method_call_expr. arg_list ( ) ,
609
+ }
610
+ }
611
+ }
612
+
590
613
#[ cfg( test) ]
591
614
mod tests {
592
615
use super :: * ;
@@ -802,4 +825,32 @@ fn foo<'a>(FooStruct { bar, .. }: FooStruct<'a, '_>, baz: i32) {
802
825
r"fn foo($0i: impl ToString) { }" ,
803
826
) ;
804
827
}
828
+ #[ test]
829
+ fn test_extract_function_signature_in_method ( ) {
830
+ check_assist (
831
+ extract_struct_from_function_signature,
832
+ r#"
833
+ struct Foo
834
+ impl Foo {
835
+ fn foo(&self, $0j: i32, i: i32$0, z:i32) { }
836
+ }
837
+
838
+ fn bar() {
839
+ Foo.foo(1, 2, 3)
840
+ }
841
+ "# ,
842
+ r#"
843
+ struct Foo
844
+ struct FooStruct{ j: i32, i: i32 }
845
+
846
+ impl Foo {
847
+ fn foo(&self, FooStruct { j, i, .. }: FooStruct, z:i32) { }
848
+ }
849
+
850
+ fn bar() {
851
+ Foo.foo(FooStruct { j: 1, i: 2 }, 3)
852
+ }
853
+ "# ,
854
+ ) ;
855
+ }
805
856
}
0 commit comments