Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let allSyntaxCodeActions: [any SyntaxCodeActionProvider.Type] = {
ConvertZeroParameterFunctionToComputedProperty.self,
FormatRawStringLiteral.self,
MigrateToNewIfLetSyntax.self,
MoveMembersToExtension.self,
OpaqueParameterToGeneric.self,
RemoveSeparatorsFromIntegerLiteral.self,
]
Expand All @@ -37,5 +38,6 @@ let allSyntaxCodeActions: [any SyntaxCodeActionProvider.Type] = {
}()

let supersededSourcekitdRefactoringActions: Set<String> = [
"source.refactoring.kind.simplify.long.number.literal" // Superseded by AddSeparatorsToIntegerLiteral
"source.refactoring.kind.move.members.to.extension", // Superseded by MoveMembersToExtension
"source.refactoring.kind.simplify.long.number.literal", // Superseded by AddSeparatorsToIntegerLiteral
]
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,37 @@ extension ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringCodeA
}
}

extension MoveMembersToExtension: SyntaxRefactoringCodeActionProvider {
static func codeActions(in scope: SyntaxCodeActionScope) -> [CodeAction] {

guard let node = nodeToRefactor(in: scope) else {
return []
}

guard let sourceEdits = try? Self.textRefactor(syntax: node, in: Context(range: scope.range)) else {
return []
}

guard let workspaceEdit = sourceEdits.asWorkspaceEdit(snapshot: scope.snapshot) else {
return []
}

return [
CodeAction(
title: Self.title,
kind: .refactorExtract,
edit: workspaceEdit
)
]
}

package static var title: String { "Move to extension" }

static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? {
return scope.file
}
}

extension ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringCodeActionProvider {
package static var title: String { "Convert to zero parameter function" }

Expand Down
64 changes: 64 additions & 0 deletions Tests/SourceKitLSPTests/CodeActionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,70 @@ final class CodeActionTests: SourceKitLSPTestCase {
)
}

func testMoveMembersToExtension() async throws {
let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport)
let uri = DocumentURI(for: .swift)

let positions = testClient.openDocument(
"""
1️⃣class Foo {
2️⃣func foo() {
print("Hello world!")
}3️⃣

func bar() {
print("Hello world!")
}
}4️⃣
""",
uri: uri
)

let request = CodeActionRequest(
range: positions["2️⃣"]..<positions["3️⃣"],
context: .init(),
textDocument: TextDocumentIdentifier(uri)
)
let result = try await testClient.send(request)

guard case .codeActions(let codeActions) = result else {
XCTFail("Expected code actions")
return
}

let expectedCodeAction = CodeAction(
title: "Move to extension",
kind: .refactorExtract,
diagnostics: nil,
edit: WorkspaceEdit(
changes: [
uri: [
TextEdit(
range: positions["1️⃣"]..<positions["4️⃣"],
newText: """
class Foo {

func bar() {
print("Hello world!")
}
}

extension Foo {
func foo() {
print("Hello world!")
}
}
"""
)
]
]
),
command: nil
)

XCTAssertTrue(codeActions.contains(expectedCodeAction))
}

func testConvertFunctionZeroParameterToComputedProperty() async throws {
let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport)
let uri = DocumentURI(for: .swift)
Expand Down