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
52 changes: 52 additions & 0 deletions Sources/SwiftLanguageService/CodeActions/MoveMember.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

@_spi(SourceKitLSP) import LanguageServerProtocol
import SourceKitLSP
import SwiftSyntax

extension CodeActionKind {
static let refactorMove = CodeActionKind(rawValue: "refactor.move")
}

struct MoveMember: SyntaxCodeActionProvider {

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

guard
let member =
scope.innermostNodeContainingRange?
.findParentOfSelf(
ofType: MemberBlockItemSyntax.self,
stoppingIf: { $0.is(SourceFileSyntax.self) }
)
else {
return []
}

return [
CodeAction(
title: "Move to another type",
kind: .refactorMove,
command: Command(
title: "Move to another type",
command: "swift.moveMember",
arguments: [
.string(scope.snapshot.uri.stringValue),
.int(member.position.utf8Offset),
.int(member.endPosition.utf8Offset),
]
)
)
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ let allSyntaxCodeActions: [any SyntaxCodeActionProvider.Type] = {
MigrateToNewIfLetSyntax.self,
OpaqueParameterToGeneric.self,
RemoveSeparatorsFromIntegerLiteral.self,
MoveMember.self,
]
#if !NO_SWIFTPM_DEPENDENCY
result.append(PackageManifestEdits.self)
Expand Down
39 changes: 39 additions & 0 deletions Tests/SourceKitLSPTests/CodeActionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,45 @@ final class CodeActionTests: SourceKitLSPTestCase {
[]
}
}

func testMoveMemberCodeAction() async throws {
let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport)
let uri = DocumentURI(for: .swift)
let positions = testClient.openDocument(
"""
struct MyStruct {
1️⃣func myFunction() {}
}
""",
uri: uri
)

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

// Make sure we get a "Move to another type" action.
let moveMemberAction = result?.codeActions?.first { action in
return action.title == "Move to another type"
}
XCTAssertNotNil(moveMemberAction)
}

func testMoveMemberCodeActionNotOfferedForTopLevelCode() async throws {
try await assertCodeActions(
"""
1️⃣func topLevelFunction() {}
""",
markers: ["1️⃣"],
exhaustive: false
) { _, _ in
[]
}
}

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