Skip to content

Add 'Extract tuple into struct' code action#2719

Open
krishnapermi wants to merge 2 commits into
swiftlang:mainfrom
krishnapermi:extract-tuple-into-struct
Open

Add 'Extract tuple into struct' code action#2719
krishnapermi wants to merge 2 commits into
swiftlang:mainfrom
krishnapermi:extract-tuple-into-struct

Conversation

@krishnapermi

Copy link
Copy Markdown
Contributor

Implements the "Extract tuple into struct" code action requested in #2511.

When the cursor is on a named tuple type — one where every element has a label — a new code action appears in the lightbulb menu. It generates a struct with matching stored properties immediately before the enclosing declaration and replaces the tuple type annotation with the new struct name.

Before:

func getUser() -> (name: String, age: Int, email: String) {
    return ("Alice", 30, "alice@example.com")
}

After:

struct GetUserResult {
    let name: String
    let age: Int
    let email: String
}

func getUser() -> GetUserResult {
    return ("Alice", 30, "alice@example.com")
}

The struct name is inferred from context:

  • Function return type → <FunctionName>Result (e.g. getUserGetUserResult)
  • Variable annotation → capitalised variable name
  • Type alias → the alias name itself
  • Fallback → concatenated capitalised element labels

Indentation is matched to the surrounding source using BasicFormat.inferIndentation.

Scope: This is the syntactic-only step discussed in #2511 — only the local type annotation is rewritten. Renaming call sites that construct or destructure the tuple is left to a follow-up rename refactoring (the pattern @ahoppen described as an acceptable first step).

What doesn't trigger it: unlabelled tuples, single-element tuples, and tuples with _ wildcard labels, since none of those produce clean property names.

Resolves #2511.

This source file implements a syntactic code action that extracts a named tuple type into a named struct in Swift. It includes methods to find the tuple type, determine the insertion point for the new struct, and derive a suitable struct name based on the context.

@ahoppen ahoppen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments. Could you also add test cases for this?

/// return ("Alice", 30, "alice@example.com")
/// }
/// ```
struct ExtractTupleIntoStruct: SyntaxCodeActionProvider {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you implement this in terms of SyntaxRefactoringCodeActionProvider?


let structName = deriveStructName(for: tupleType)

// Match the indentation style of the surrounding source.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think this comment provides significant value as it just describes what the code below already clearly states. Please remove such comments.

// trailing blank line and indentation to restore spacing before the
// original declaration.
let insertPos = enclosingDecl.positionAfterSkippingLeadingTrivia
let structText = "struct \(structName) {\n\(fields)\n\(baseIndent)}\n\n\(baseIndent)"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would read nicer as a multi-line string literal

/// immediately before it, either at the top level of a function body or as a
/// nested member of a type.
private func findEnclosingInsertionPoint(of node: Syntax) -> DeclSyntax? {
var current: Syntax? = node

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can’t you use findParentOfSelf here instead of walking the tree manually?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Extract tuple into struct code action

2 participants