···41744174 }
41754175}
4176417641774177+/// A set of variable names used in some gleam. Useful for passing to [NameGenerator::reserve_variable_names].
41774178struct VariablesNames {
41784179 names: HashSet<EcoString>,
41794180}
4180418141814182impl VariablesNames {
41834183+ /// Creates a `VariableNames` by collecting all variables used in a list of statements.
41824184 fn from_statements(statements: &[TypedStatement]) -> Self {
41834185 let mut variables = Self {
41844186 names: HashSet::new(),
···41874189 for statement in statements {
41884190 variables.visit_typed_statement(statement);
41894191 }
41924192+ variables
41934193+ }
41944194+41954195+ /// Creates a `VariableNames` by collecting all variables used within an expression.
41964196+ fn from_expression(expression: &TypedExpr) -> Self {
41974197+ let mut variables = Self {
41984198+ names: HashSet::new(),
41994199+ };
42004200+42014201+ variables.visit_typed_expr(expression);
41904202 variables
41914203 }
41924204}
···1137511387 }
1137611388 }
1137711389}
1139011390+1139111391+/// Code action to turn a function used as a reference into a one-statement anonymous function.
1139211392+///
1139311393+/// For example, if the code action was used on `op` here:
1139411394+///
1139511395+/// ```gleam
1139611396+/// list.map([1, 2, 3], op)
1139711397+/// ```
1139811398+///
1139911399+/// it would become:
1140011400+///
1140111401+/// ```gleam
1140211402+/// list.map([1, 2, 3], fn(int) {
1140311403+/// op(int)
1140411404+/// })
1140511405+/// ```
1140611406+pub struct WrapInAnonymousFunction<'a> {
1140711407+ module: &'a Module,
1140811408+ line_numbers: &'a LineNumbers,
1140911409+ params: &'a CodeActionParams,
1141011410+ functions: Vec<FunctionToWrap>,
1141111411+}
1141211412+1141311413+/// Helper struct, a target for [WrapInAnonymousFunction].
1141411414+struct FunctionToWrap {
1141511415+ location: SrcSpan,
1141611416+ arguments: Vec<Arc<Type>>,
1141711417+ variables_names: VariablesNames,
1141811418+}
1141911419+1142011420+impl<'a> WrapInAnonymousFunction<'a> {
1142111421+ pub fn new(
1142211422+ module: &'a Module,
1142311423+ line_numbers: &'a LineNumbers,
1142411424+ params: &'a CodeActionParams,
1142511425+ ) -> Self {
1142611426+ Self {
1142711427+ module,
1142811428+ line_numbers,
1142911429+ params,
1143011430+ functions: vec![],
1143111431+ }
1143211432+ }
1143311433+1143411434+ pub fn code_actions(mut self) -> Vec<CodeAction> {
1143511435+ self.visit_typed_module(&self.module.ast);
1143611436+1143711437+ let mut actions = Vec::with_capacity(self.functions.len());
1143811438+ for target in self.functions {
1143911439+ let mut name_generator = NameGenerator::new();
1144011440+ name_generator.reserve_variable_names(target.variables_names);
1144111441+ let arguments = target
1144211442+ .arguments
1144311443+ .iter()
1144411444+ .map(|t| name_generator.generate_name_from_type(t))
1144511445+ .join(", ");
1144611446+1144711447+ let mut edits = TextEdits::new(self.line_numbers);
1144811448+ edits.insert(target.location.start, format!("fn({arguments}) {{ "));
1144911449+ edits.insert(target.location.end, format!("({arguments}) }}"));
1145011450+1145111451+ CodeActionBuilder::new("Wrap in anonymous function")
1145211452+ .kind(CodeActionKind::REFACTOR_REWRITE)
1145311453+ .changes(self.params.text_document.uri.clone(), edits.edits)
1145411454+ .push_to(&mut actions);
1145511455+ }
1145611456+ actions
1145711457+ }
1145811458+}
1145911459+1146011460+impl<'ast> ast::visit::Visit<'ast> for WrapInAnonymousFunction<'ast> {
1146111461+ fn visit_typed_expr(&mut self, expression: &'ast TypedExpr) {
1146211462+ let expression_range = src_span_to_lsp_range(expression.location(), self.line_numbers);
1146311463+ if !overlaps(self.params.range, expression_range) {
1146411464+ return;
1146511465+ }
1146611466+1146711467+ let is_excluded = if let TypedExpr::Fn { kind, .. } = expression {
1146811468+ kind.is_anonymous() || kind.is_capture()
1146911469+ } else {
1147011470+ false
1147111471+ };
1147211472+1147311473+ if let Type::Fn { arguments, .. } = &*expression.type_()
1147411474+ && !is_excluded
1147511475+ {
1147611476+ self.functions.push(FunctionToWrap {
1147711477+ location: expression.location(),
1147811478+ arguments: arguments.clone(),
1147911479+ variables_names: VariablesNames::from_expression(expression),
1148011480+ });
1148111481+ };
1148211482+1148311483+ ast::visit::visit_typed_expr(self, expression);
1148411484+ }
1148511485+1148611486+ /// We don't want to apply to functions that are being explicitly called
1148711487+ /// already, so we need to intercept visits to function calls and bounce
1148811488+ /// them out again so they don't end up in our impl for visit_typed_expr.
1148911489+ /// Otherwise this is the same as [].
1149011490+ fn visit_typed_expr_call(
1149111491+ &mut self,
1149211492+ _location: &'ast SrcSpan,
1149311493+ _type: &'ast Arc<Type>,
1149411494+ fun: &'ast TypedExpr,
1149511495+ arguments: &'ast [TypedCallArg],
1149611496+ _argument_parentheses: &'ast Option<SrcSpan>,
1149711497+ ) {
1149811498+ // We only need to do this interception for explicit calls, so if any
1149911499+ // of our arguments are explicit we re-enter the visitor as usual.
1150011500+ if arguments.iter().any(|a| a.is_implicit()) {
1150111501+ self.visit_typed_expr(fun);
1150211502+ } else {
1150311503+ // We still want to visit other nodes nested in the function being
1150411504+ // called so we bounce the call back out.
1150511505+ ast::visit::visit_typed_expr(self, fun);
1150611506+ }
1150711507+1150811508+ for argument in arguments {
1150911509+ self.visit_typed_call_arg(argument);
1151011510+ }
1151111511+ }
1151211512+}