Skip to content

Add Convert closure to function code action #2519

Description

@Tabonx

Description

Convert a closure to a named nested function, turning captures into parameters.

Before:

func example() {
    let x = 5
    let add = { (y: Int) in x + y }
    print(add(3))
}

After:

func example() {
    let x = 5
    func add(y: Int, x: Int) -> Int { x + y }
    print(add(y: 3, x: x))
}

See also: rust-analyzer convert_closure_to_fn

Activity

  1. added theissue type on Feb 28, 2026
  2. rintaro commented on Mar 10, 2026

    @rintaro
    Member

    I'm not sure we often see this pattern

    let closure = { ... }
    use(closure)

    But obviously we often see

    use { ... }

    which can be rewritten

    func operation(...) { ... }
    
    use(operation) // where 'operation' is a parameter name in `use(_:)`

    The implementation should check the exact type of the closure and the desired function name (variable name or parameter name).

  3. BoseSj commented on Sep 4, 2026

    @BoseSj

    Hello, I’d love to take a crack at this as my first contribution to sourcekit-lsp. I have gone through the rust-analyser, and also the swift example, I got the idea here.

    Would it be alright if I start working on this ?
    @ahoppen Can you please let me know where should I start looking into for reference, or anything of sorts that can guide me ?

  4. ahoppen commented on Sep 4, 2026

    @ahoppen
    Member

    Great to hear that you’re interested, @BoseSj.

    The most similar code action is ConvertComputedPropertyToStored, which you could use as reference: Both need to infer the type of a variable (or parameters in the case of this refactoring) using cursor info. You should be able to follow that as a blueprint.

  5. BoseSj commented on Sep 15, 2026

    @BoseSj

    Hello, its been some time. Among other stuffs, I have dived into swift-syntax apis and existing similar refactoring actions for reference.
    As of now, my flow in following:

    • Detecting Functionc all expressions
    • Detecting trailing Closure expression
    • Get parameter names and types for ClosureParameterList, eg. ... (num: Int, decinum: Float) in ...
    • Get parameter names and types for ClosureShorthandParameterList, eg. ... num, decinum in ...

    As I have been experimenting before actually implementing the action. I have not been able to get the type for ClosureShorthandParameterListSyntax, any idea on that ? @ahoppen

  6. BoseSj commented on Sep 22, 2026

    @BoseSj

    So as of now I have been able to do the following kind of transformation

    For ClosureParameterClauseSyntax

    Original

    dosome { (num: Int, decinum: Float) -> Void in
      return "Hello: \(num)"
    }
    

    Generated

    func <#name#>(num: Int, decinum: Float) -> Void  {
      return "Hello: \(num)"
    }
    

    For ClosureShorthandParameterListSyntax

    Original

    dosome { num, decinum in
      let val = "Hello: \(num)"
      return val
    }
    

    Generated

    func <#name#>(num: <#Type#>, decinum : <#Type#>) -> <#returnType#> {
      let val = "Hello: \(num)"
      return val
    }
    

    @Tabonx are these looking closer to expectation ?

  7. BoseSj commented on Oct 1, 2026

    @BoseSj

    @ahoppen a little help there for the ClosureShorthandParameterListSyntax case,, I am a little stuck there.

  8. ahoppen commented on Oct 3, 2026

    @ahoppen
    Member

    @BoseSj thanks for tackling this and sorry for the delayed reply. In case the closure’s parameter list doesn’t specify any types, you should be able to run a cursor info request to retrieve it. For example, look at how we resole the type of a variable here:

    guard let symbolInfo = try await symbolInfo(data.position).only, let typeName = symbolInfo.typeName, typeName != "_"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    code actionCode action provided by LSP

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions