@@ -3503,11 +3503,13 @@ impl<'ast> ast::visit::Visit<'ast> for ExpandFunctionCapture<'ast> {
3503
3503
}
3504
3504
}
3505
3505
3506
+ /// A set of variable names used in some gleam. Useful for passing to [NameGenerator::reserve_variable_names].
3506
3507
struct VariablesNames {
3507
3508
names : HashSet < EcoString > ,
3508
3509
}
3509
3510
3510
3511
impl VariablesNames {
3512
+ /// Creates a `VariableNames` by collecting all variables used in a list of statements.
3511
3513
fn from_statements ( statements : & [ TypedStatement ] ) -> Self {
3512
3514
let mut variables = Self {
3513
3515
names : HashSet :: new ( ) ,
@@ -3519,12 +3521,13 @@ impl VariablesNames {
3519
3521
variables
3520
3522
}
3521
3523
3522
- fn from_expr ( expr : & TypedExpr ) -> Self {
3524
+ /// Creates a `VariableNames` by collecting all variables used within an expression.
3525
+ fn from_expression ( expression : & TypedExpr ) -> Self {
3523
3526
let mut variables = Self {
3524
3527
names : HashSet :: new ( ) ,
3525
3528
} ;
3526
3529
3527
- variables. visit_typed_expr ( expr ) ;
3530
+ variables. visit_typed_expr ( expression ) ;
3528
3531
variables
3529
3532
}
3530
3533
}
@@ -8206,17 +8209,33 @@ impl<'ast> ast::visit::Visit<'ast> for RemoveUnreachableBranches<'ast> {
8206
8209
}
8207
8210
}
8208
8211
8209
- struct FunctionToWrap {
8210
- location : SrcSpan ,
8211
- arguments : Vec < Arc < Type > > ,
8212
- variables_names : VariablesNames ,
8213
- }
8214
-
8212
+ /// Code action to turn a function used as a reference into a one-statement anonymous function.
8213
+ ///
8214
+ /// For example, if the code action was used on `op` here:
8215
+ ///
8216
+ /// ```gleam
8217
+ /// list.map([1, 2, 3], op)
8218
+ /// ```
8219
+ ///
8220
+ /// it would become:
8221
+ ///
8222
+ /// ```gleam
8223
+ /// list.map([1, 2, 3], fn(int) {
8224
+ /// op(int)
8225
+ /// })
8226
+ /// ```
8215
8227
pub struct WrapInAnonymousFunction < ' a > {
8216
8228
module : & ' a Module ,
8217
8229
line_numbers : & ' a LineNumbers ,
8218
8230
params : & ' a CodeActionParams ,
8219
- targets : Vec < FunctionToWrap > ,
8231
+ functions : Vec < FunctionToWrap > ,
8232
+ }
8233
+
8234
+ /// Helper struct, a target for [WrapInAnonymousFunction].
8235
+ struct FunctionToWrap {
8236
+ location : SrcSpan ,
8237
+ arguments : Vec < Arc < Type > > ,
8238
+ variables_names : VariablesNames ,
8220
8239
}
8221
8240
8222
8241
impl < ' a > WrapInAnonymousFunction < ' a > {
@@ -8229,15 +8248,15 @@ impl<'a> WrapInAnonymousFunction<'a> {
8229
8248
module,
8230
8249
line_numbers,
8231
8250
params,
8232
- targets : vec ! [ ] ,
8251
+ functions : vec ! [ ] ,
8233
8252
}
8234
8253
}
8235
8254
8236
8255
pub fn code_actions ( mut self ) -> Vec < CodeAction > {
8237
8256
self . visit_typed_module ( & self . module . ast ) ;
8238
8257
8239
- let mut actions = Vec :: with_capacity ( self . targets . len ( ) ) ;
8240
- for target in self . targets {
8258
+ let mut actions = Vec :: with_capacity ( self . functions . len ( ) ) ;
8259
+ for target in self . functions {
8241
8260
let mut name_generator = NameGenerator :: new ( ) ;
8242
8261
name_generator. reserve_variable_names ( target. variables_names ) ;
8243
8262
let arguments = target
@@ -8262,9 +8281,9 @@ impl<'a> WrapInAnonymousFunction<'a> {
8262
8281
impl < ' ast > ast:: visit:: Visit < ' ast > for WrapInAnonymousFunction < ' ast > {
8263
8282
fn visit_typed_call_arg ( & mut self , arg : & ' ast TypedCallArg ) {
8264
8283
if let Type :: Fn { ref arguments, .. } = * arg. value . type_ ( ) {
8265
- let variables_names = VariablesNames :: from_expr ( & arg. value ) ;
8284
+ let variables_names = VariablesNames :: from_expression ( & arg. value ) ;
8266
8285
8267
- self . targets . push ( FunctionToWrap {
8286
+ self . functions . push ( FunctionToWrap {
8268
8287
location : arg. location ,
8269
8288
arguments : arguments. clone ( ) ,
8270
8289
variables_names,
@@ -8278,9 +8297,9 @@ impl<'ast> ast::visit::Visit<'ast> for WrapInAnonymousFunction<'ast> {
8278
8297
}
8279
8298
8280
8299
if let Type :: Fn { ref arguments, .. } = * assignment. value . type_ ( ) {
8281
- let variables_names = VariablesNames :: from_expr ( & assignment. value ) ;
8300
+ let variables_names = VariablesNames :: from_expression ( & assignment. value ) ;
8282
8301
8283
- self . targets . push ( FunctionToWrap {
8302
+ self . functions . push ( FunctionToWrap {
8284
8303
location : assignment. value . location ( ) ,
8285
8304
arguments : arguments. clone ( ) ,
8286
8305
variables_names,
@@ -8289,15 +8308,33 @@ impl<'ast> ast::visit::Visit<'ast> for WrapInAnonymousFunction<'ast> {
8289
8308
}
8290
8309
}
8291
8310
8311
+ /// Code action to unwrap trivial one-statement anonymous functions into just a reference to the function called
8312
+ ///
8313
+ /// For example, if the code action was used on the anonymous function here:
8314
+ ///
8315
+ /// ```gleam
8316
+ /// list.map([1, 2, 3], fn(int) {
8317
+ /// op(int)
8318
+ /// })
8319
+ /// ```
8320
+ ///
8321
+ /// it would become:
8322
+ ///
8323
+ /// ```gleam
8324
+ /// list.map([1, 2, 3], op)
8325
+ /// ```
8292
8326
pub struct UnwrapAnonymousFunction < ' a > {
8293
8327
module : & ' a Module ,
8294
8328
line_numbers : & ' a LineNumbers ,
8295
8329
params : & ' a CodeActionParams ,
8296
- targets : Vec < FnToUnwrap > ,
8330
+ functions : Vec < FunctionToUnwrap > ,
8297
8331
}
8298
8332
8299
- struct FnToUnwrap {
8300
- fn_location : SrcSpan ,
8333
+ /// Helper struct, a target for [UnwrapAnonymousFunction]
8334
+ struct FunctionToUnwrap {
8335
+ /// Location of the anonymous function to apply the action to
8336
+ anonymous_function_location : SrcSpan ,
8337
+ /// Location of the function being called inside the anonymous function. This will be all that's left after the action.
8301
8338
inner_function_location : SrcSpan ,
8302
8339
}
8303
8340
@@ -8311,27 +8348,27 @@ impl<'a> UnwrapAnonymousFunction<'a> {
8311
8348
module,
8312
8349
line_numbers,
8313
8350
params,
8314
- targets : vec ! [ ] ,
8351
+ functions : vec ! [ ] ,
8315
8352
}
8316
8353
}
8317
8354
8318
8355
pub fn code_actions ( mut self ) -> Vec < CodeAction > {
8319
8356
self . visit_typed_module ( & self . module . ast ) ;
8320
8357
8321
- let mut actions = Vec :: with_capacity ( self . targets . len ( ) ) ;
8322
- for target in self . targets {
8358
+ let mut actions = Vec :: with_capacity ( self . functions . len ( ) ) ;
8359
+ for target in self . functions {
8323
8360
let mut edits = TextEdits :: new ( self . line_numbers ) ;
8324
8361
8325
8362
edits. delete ( SrcSpan {
8326
- start : target. fn_location . start ,
8363
+ start : target. anonymous_function_location . start ,
8327
8364
end : target. inner_function_location . start ,
8328
8365
} ) ;
8329
8366
edits. delete ( SrcSpan {
8330
8367
start : target. inner_function_location . end ,
8331
- end : target. fn_location . end ,
8368
+ end : target. anonymous_function_location . end ,
8332
8369
} ) ;
8333
8370
8334
- CodeActionBuilder :: new ( "Unwrap anonymous function" )
8371
+ CodeActionBuilder :: new ( "Remove anonymous function wrapper " )
8335
8372
. kind ( CodeActionKind :: REFACTOR_REWRITE )
8336
8373
. changes ( self . params . text_document . uri . clone ( ) , edits. edits )
8337
8374
. push_to ( & mut actions) ;
@@ -8355,19 +8392,6 @@ impl<'ast> ast::visit::Visit<'ast> for UnwrapAnonymousFunction<'ast> {
8355
8392
_ => return ,
8356
8393
}
8357
8394
8358
- // We need the existing argument list for the fn to be a 1:1 match for the args we pass
8359
- // to the called function. We figure out what the call-arg list needs to look like here,
8360
- // and bail out if any incoming args are discarded.
8361
- let mut expected_argument_names = Vec :: with_capacity ( arguments. len ( ) ) ;
8362
- for a in arguments {
8363
- match & a. names {
8364
- ArgNames :: Named { name, .. } => expected_argument_names. push ( name) ,
8365
- ArgNames :: NamedLabelled { name, .. } => expected_argument_names. push ( name) ,
8366
- ArgNames :: Discard { .. } => return ,
8367
- ArgNames :: LabelledDiscard { .. } => return ,
8368
- }
8369
- }
8370
-
8371
8395
// We can only apply to anonymous functions containing a single function call
8372
8396
let [
8373
8397
TypedStatement :: Expression ( TypedExpr :: Call {
@@ -8380,20 +8404,38 @@ impl<'ast> ast::visit::Visit<'ast> for UnwrapAnonymousFunction<'ast> {
8380
8404
return ;
8381
8405
} ;
8382
8406
8383
- let mut call_argument_names = Vec :: with_capacity ( arguments. len ( ) ) ;
8407
+ // We need the existing argument list for the fn to be a 1:1 match for
8408
+ // the args we pass to the called function, so we need to collect the
8409
+ // names used in both lists and check they're equal.
8410
+
8411
+ let mut outer_argument_names = Vec :: with_capacity ( arguments. len ( ) ) ;
8412
+ for a in arguments {
8413
+ match & a. names {
8414
+ ArgNames :: Named { name, .. } => outer_argument_names. push ( name) ,
8415
+ ArgNames :: NamedLabelled { name, .. } => outer_argument_names. push ( name) ,
8416
+ // We can bail out early if any of these are discarded, since
8417
+ // they couldn't match the arguments used.
8418
+ ArgNames :: Discard { .. } => return ,
8419
+ ArgNames :: LabelledDiscard { .. } => return ,
8420
+ }
8421
+ }
8422
+
8423
+ let mut inner_argument_names = Vec :: with_capacity ( arguments. len ( ) ) ;
8384
8424
for a in call_arguments {
8385
8425
match & a. value {
8386
- TypedExpr :: Var { name, .. } => call_argument_names. push ( name) ,
8426
+ TypedExpr :: Var { name, .. } => inner_argument_names. push ( name) ,
8427
+ // We can bail out early if any of these aren't variables, since
8428
+ // they couldn't match the inputs.
8387
8429
_ => return ,
8388
8430
}
8389
8431
}
8390
8432
8391
- if call_argument_names != expected_argument_names {
8433
+ if inner_argument_names != outer_argument_names {
8392
8434
return ;
8393
8435
}
8394
8436
8395
- self . targets . push ( FnToUnwrap {
8396
- fn_location : * location,
8437
+ self . functions . push ( FunctionToUnwrap {
8438
+ anonymous_function_location : * location,
8397
8439
inner_function_location : called_function. location ( ) ,
8398
8440
} )
8399
8441
}
0 commit comments