@@ -8411,3 +8411,190 @@ impl<'ast> ast::visit::Visit<'ast> for AddOmittedLabels<'ast> {
8411
8411
self . arguments_and_omitted_labels = Some ( omitted_labels) ;
8412
8412
}
8413
8413
}
8414
+
8415
+ /// Code action to extract selected code into a separate function.
8416
+ pub struct ExtractFunction < ' a > {
8417
+ module : & ' a Module ,
8418
+ params : & ' a CodeActionParams ,
8419
+ edits : TextEdits < ' a > ,
8420
+ extract : Extract < ' a > ,
8421
+ function_end_position : Option < u32 > ,
8422
+ }
8423
+
8424
+ #[ derive( Debug ) ]
8425
+ enum Extract < ' a > {
8426
+ None ,
8427
+ Expression ( & ' a TypedExpr ) ,
8428
+ Statements ( Vec < & ' a TypedStatement > ) ,
8429
+ }
8430
+
8431
+ impl < ' a > ExtractFunction < ' a > {
8432
+ pub fn new (
8433
+ module : & ' a Module ,
8434
+ line_numbers : & ' a LineNumbers ,
8435
+ params : & ' a CodeActionParams ,
8436
+ ) -> Self {
8437
+ Self {
8438
+ module,
8439
+ params,
8440
+ edits : TextEdits :: new ( line_numbers) ,
8441
+ extract : Extract :: None ,
8442
+ function_end_position : None ,
8443
+ }
8444
+ }
8445
+
8446
+ pub fn code_actions ( mut self ) -> Vec < CodeAction > {
8447
+ if self . params . range . start == self . params . range . end {
8448
+ return Vec :: new ( ) ;
8449
+ }
8450
+
8451
+ self . visit_typed_module ( & self . module . ast ) ;
8452
+
8453
+ let Some ( end) = self . function_end_position else {
8454
+ return Vec :: new ( ) ;
8455
+ } ;
8456
+
8457
+ match std:: mem:: replace ( & mut self . extract , Extract :: None ) {
8458
+ Extract :: None => return Vec :: new ( ) ,
8459
+ Extract :: Expression ( expression) => self . extract_expression ( expression, end) ,
8460
+ Extract :: Statements ( statements) => self . extract_statements ( statements, end) ,
8461
+ }
8462
+
8463
+ let mut action = Vec :: with_capacity ( 1 ) ;
8464
+ CodeActionBuilder :: new ( "Extract function" )
8465
+ . kind ( CodeActionKind :: REFACTOR_EXTRACT )
8466
+ . changes ( self . params . text_document . uri . clone ( ) , self . edits . edits )
8467
+ . preferred ( false )
8468
+ . push_to ( & mut action) ;
8469
+ action
8470
+ }
8471
+
8472
+ fn extract_expression ( & mut self , expression : & TypedExpr , function_end : u32 ) {
8473
+ let referenced_variables = referenced_variables ( expression) ;
8474
+
8475
+ let expression_code = code_at ( self . module , expression. location ( ) ) ;
8476
+
8477
+ let arguments = referenced_variables. iter ( ) . map ( |( name, _) | name) . join ( ", " ) ;
8478
+ let call = format ! ( "function({arguments})" ) ;
8479
+ self . edits . replace ( expression. location ( ) , call) ;
8480
+
8481
+ let mut printer = Printer :: new ( & self . module . ast . names ) ;
8482
+
8483
+ let parameters = referenced_variables
8484
+ . iter ( )
8485
+ . map ( |( name, type_) | eco_format ! ( "{name}: {}" , printer. print_type( type_) ) )
8486
+ . join ( ", " ) ;
8487
+ let return_type = printer. print_type ( & expression. type_ ( ) ) ;
8488
+
8489
+ let function = format ! (
8490
+ "\n \n fn function({parameters}) -> {return_type} {{
8491
+ {expression_code}
8492
+ }}"
8493
+ ) ;
8494
+
8495
+ self . edits . insert ( function_end, function) ;
8496
+ }
8497
+
8498
+ fn extract_statements ( & mut self , statements : Vec < & TypedStatement > , function_end : u32 ) {
8499
+ todo ! ( "Implement for statements" )
8500
+ }
8501
+ }
8502
+
8503
+ impl < ' ast > ast:: visit:: Visit < ' ast > for ExtractFunction < ' ast > {
8504
+ fn visit_typed_function ( & mut self , function : & ' ast ast:: TypedFunction ) {
8505
+ let range = self . edits . src_span_to_lsp_range ( function. full_location ( ) ) ;
8506
+
8507
+ if within ( self . params . range , range) {
8508
+ self . function_end_position = Some ( function. end_position ) ;
8509
+
8510
+ ast:: visit:: visit_typed_function ( self , function) ;
8511
+ }
8512
+ }
8513
+
8514
+ fn visit_typed_expr ( & mut self , expression : & ' ast TypedExpr ) {
8515
+ match & self . extract {
8516
+ Extract :: None => {
8517
+ let range = self . edits . src_span_to_lsp_range ( expression. location ( ) ) ;
8518
+
8519
+ if within ( range, self . params . range ) {
8520
+ self . extract = Extract :: Expression ( expression) ;
8521
+ return ;
8522
+ }
8523
+ }
8524
+ Extract :: Expression ( _) | Extract :: Statements ( _) => { }
8525
+ }
8526
+ ast:: visit:: visit_typed_expr ( self , expression) ;
8527
+ }
8528
+
8529
+ fn visit_typed_statement ( & mut self , statement : & ' ast TypedStatement ) {
8530
+ let range = self . edits . src_span_to_lsp_range ( statement. location ( ) ) ;
8531
+ if within ( range, self . params . range ) {
8532
+ match & mut self . extract {
8533
+ Extract :: None => {
8534
+ self . extract = Extract :: Statements ( vec ! [ statement] ) ;
8535
+ }
8536
+ Extract :: Expression ( expression) => {
8537
+ if expression. location ( ) . contains_span ( statement. location ( ) ) {
8538
+ return ;
8539
+ }
8540
+
8541
+ self . extract = Extract :: Statements ( vec ! [ statement] ) ;
8542
+ }
8543
+ Extract :: Statements ( statements) => {
8544
+ statements. push ( statement) ;
8545
+ }
8546
+ }
8547
+ } else {
8548
+ ast:: visit:: visit_typed_statement ( self , statement) ;
8549
+ }
8550
+ }
8551
+ }
8552
+
8553
+ fn referenced_variables ( expression : & TypedExpr ) -> Vec < ( EcoString , Arc < Type > ) > {
8554
+ let mut references = ReferencedVariables {
8555
+ variables : Vec :: new ( ) ,
8556
+ defined_variables : HashSet :: new ( ) ,
8557
+ } ;
8558
+ references. visit_typed_expr ( expression) ;
8559
+ references. variables
8560
+ }
8561
+
8562
+ struct ReferencedVariables {
8563
+ variables : Vec < ( EcoString , Arc < Type > ) > ,
8564
+ defined_variables : HashSet < EcoString > ,
8565
+ }
8566
+
8567
+ impl ReferencedVariables {
8568
+ fn register ( & mut self , name : & EcoString , type_ : & Arc < Type > ) {
8569
+ if self . defined_variables . contains ( name) {
8570
+ return ;
8571
+ }
8572
+
8573
+ if !self
8574
+ . variables
8575
+ . iter ( )
8576
+ . any ( |( variable_name, _) | variable_name == name)
8577
+ {
8578
+ self . variables . push ( ( name. clone ( ) , type_. clone ( ) ) )
8579
+ }
8580
+ }
8581
+ }
8582
+
8583
+ impl < ' ast > ast:: visit:: Visit < ' ast > for ReferencedVariables {
8584
+ fn visit_typed_expr_var (
8585
+ & mut self ,
8586
+ _location : & ' ast SrcSpan ,
8587
+ constructor : & ' ast ValueConstructor ,
8588
+ name : & ' ast EcoString ,
8589
+ ) {
8590
+ match & constructor. variant {
8591
+ type_:: ValueConstructorVariant :: LocalVariable { .. } => {
8592
+ self . register ( name, & constructor. type_ ) ;
8593
+ }
8594
+ type_:: ValueConstructorVariant :: ModuleConstant { .. }
8595
+ | type_:: ValueConstructorVariant :: LocalConstant { .. }
8596
+ | type_:: ValueConstructorVariant :: ModuleFn { .. }
8597
+ | type_:: ValueConstructorVariant :: Record { .. } => { }
8598
+ }
8599
+ }
8600
+ }
0 commit comments