diff --git a/Sources/SourceKitLSP/Swift/CodeActions/SyntaxCodeActions.swift b/Sources/SourceKitLSP/Swift/CodeActions/SyntaxCodeActions.swift index acdea5dd8..328012c3f 100644 --- a/Sources/SourceKitLSP/Swift/CodeActions/SyntaxCodeActions.swift +++ b/Sources/SourceKitLSP/Swift/CodeActions/SyntaxCodeActions.swift @@ -17,8 +17,10 @@ import SwiftRefactor let allSyntaxCodeActions: [SyntaxCodeActionProvider.Type] = [ AddDocumentation.self, AddSeparatorsToIntegerLiteral.self, + ConvertComputedPropertyToZeroParameterFunction.self ConvertIntegerLiteral.self, ConvertJSONToCodableStruct.self, + ConvertZeroParameterFunctionToComputedProperty.self, FormatRawStringLiteral.self, MigrateToNewIfLetSyntax.self, OpaqueParameterToGeneric.self, diff --git a/Sources/SourceKitLSP/Swift/CodeActions/SyntaxRefactoringCodeActionProvider.swift b/Sources/SourceKitLSP/Swift/CodeActions/SyntaxRefactoringCodeActionProvider.swift index 7a48c91b1..468f8b28e 100644 --- a/Sources/SourceKitLSP/Swift/CodeActions/SyntaxRefactoringCodeActionProvider.swift +++ b/Sources/SourceKitLSP/Swift/CodeActions/SyntaxRefactoringCodeActionProvider.swift @@ -121,6 +121,28 @@ extension RemoveSeparatorsFromIntegerLiteral: SyntaxRefactoringCodeActionProvide } } +extension ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringCodeActionProvider { + static var title: String { "Convert to computed property" } + + static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? { + return scope.innermostNodeContainingRange?.findParentOfSelf( + ofType: FunctionDeclSyntax.self, + stoppingIf: { $0.is(CodeBlockSyntax.self) || $0.is(MemberBlockSyntax.self) } + ) + } +} + +extension ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringCodeActionProvider { + static var title: String { "Convert to zero parameter function" } + + static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? { + return scope.innermostNodeContainingRange?.findParentOfSelf( + ofType: VariableDeclSyntax.self, + stoppingIf: { $0.is(CodeBlockSyntax.self) || $0.is(MemberBlockSyntax.self) } + ) + } +} + extension SyntaxProtocol { /// Finds the innermost parent of the given type while not walking outside of nodes that satisfy `stoppingIf`. func findParentOfSelf( diff --git a/Tests/SourceKitLSPTests/CodeActionTests.swift b/Tests/SourceKitLSPTests/CodeActionTests.swift index 9e9c26ae9..56867e8d4 100644 --- a/Tests/SourceKitLSPTests/CodeActionTests.swift +++ b/Tests/SourceKitLSPTests/CodeActionTests.swift @@ -1006,6 +1006,122 @@ final class CodeActionTests: XCTestCase { } } + func testConvertFunctionZeroParameterToComputedProperty() async throws { + let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport) + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( + """ + 1️⃣func someFunction() -> String2️⃣ { return "" }3️⃣ + """, + uri: uri + ) + + let request = CodeActionRequest( + range: positions["1️⃣"].. String 1️⃣{ + 2️⃣return "" + }3️⃣ + """##, + exhaustive: false + ) { uri, positions in + [] + } + } + + func testConvertComputedPropertyToZeroParameterFunction() async throws { + let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport) + let uri = DocumentURI(for: .swift) + + let positions = testClient.openDocument( + """ + 1️⃣var someFunction: String2️⃣ { return "" }3️⃣ + """, + uri: uri + ) + + let request = CodeActionRequest( + range: positions["1️⃣"].. String { return "" } + """ + ) + ] + ] + ), + command: nil + ) + + XCTAssertTrue(codeActions.contains(expectedCodeAction)) + } + + func testConvertComputedPropertyToZeroParameterFunctionIsNotShownFromTheBody() async throws { + try await assertCodeActions( + ##""" + var someFunction: String 1️⃣{ + 2️⃣return "" + }3️⃣ + """##, + exhaustive: false + ) { uri, positions in + [] + } + } + /// Retrieves the code action at a set of markers and asserts that it matches a list of expected code actions. /// /// - Parameters: