Skip to content

Commit 6d94f19

Browse files
committed
Convert between computed properties and zero-parameters functions
1 parent b016a0d commit 6d94f19

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

Sources/SourceKitLSP/Swift/CodeActions/SyntaxCodeActions.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import SwiftRefactor
1717
let allSyntaxCodeActions: [SyntaxCodeActionProvider.Type] = [
1818
AddDocumentation.self,
1919
AddSeparatorsToIntegerLiteral.self,
20+
ConvertComputedPropertyToZeroParameterFunction.self
2021
ConvertIntegerLiteral.self,
2122
ConvertJSONToCodableStruct.self,
23+
ConvertZeroParameterFunctionToComputedProperty.self,
2224
FormatRawStringLiteral.self,
2325
MigrateToNewIfLetSyntax.self,
2426
OpaqueParameterToGeneric.self,

Sources/SourceKitLSP/Swift/CodeActions/SyntaxRefactoringCodeActionProvider.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,28 @@ extension RemoveSeparatorsFromIntegerLiteral: SyntaxRefactoringCodeActionProvide
121121
}
122122
}
123123

124+
extension ConvertZeroParameterFunctionToComputedProperty: SyntaxRefactoringCodeActionProvider {
125+
static var title: String { "Convert to computed property" }
126+
127+
static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? {
128+
return scope.innermostNodeContainingRange?.findParentOfSelf(
129+
ofType: FunctionDeclSyntax.self,
130+
stoppingIf: { $0.is(CodeBlockSyntax.self) || $0.is(MemberBlockSyntax.self) }
131+
)
132+
}
133+
}
134+
135+
extension ConvertComputedPropertyToZeroParameterFunction: SyntaxRefactoringCodeActionProvider {
136+
static var title: String { "Convert to zero parameter function" }
137+
138+
static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? {
139+
return scope.innermostNodeContainingRange?.findParentOfSelf(
140+
ofType: VariableDeclSyntax.self,
141+
stoppingIf: { $0.is(CodeBlockSyntax.self) || $0.is(MemberBlockSyntax.self) }
142+
)
143+
}
144+
}
145+
124146
extension SyntaxProtocol {
125147
/// Finds the innermost parent of the given type while not walking outside of nodes that satisfy `stoppingIf`.
126148
func findParentOfSelf<ParentType: SyntaxProtocol>(

Tests/SourceKitLSPTests/CodeActionTests.swift

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,122 @@ final class CodeActionTests: XCTestCase {
10061006
}
10071007
}
10081008

1009+
func testConvertFunctionZeroParameterToComputedProperty() async throws {
1010+
let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport)
1011+
let uri = DocumentURI(for: .swift)
1012+
1013+
let positions = testClient.openDocument(
1014+
"""
1015+
1️⃣func someFunction() -> String2️⃣ { return "" }3️⃣
1016+
""",
1017+
uri: uri
1018+
)
1019+
1020+
let request = CodeActionRequest(
1021+
range: positions["1️⃣"]..<positions["2️⃣"],
1022+
context: .init(),
1023+
textDocument: TextDocumentIdentifier(uri)
1024+
)
1025+
let result = try await testClient.send(request)
1026+
1027+
guard case .codeActions(let codeActions) = result else {
1028+
XCTFail("Expected code actions")
1029+
return
1030+
}
1031+
1032+
let expectedCodeAction = CodeAction(
1033+
title: "Convert to computed property",
1034+
kind: .refactorInline,
1035+
diagnostics: nil,
1036+
edit: WorkspaceEdit(
1037+
changes: [
1038+
uri: [
1039+
TextEdit(
1040+
range: positions["1️⃣"]..<positions["3️⃣"],
1041+
newText: """
1042+
var someFunction: String { return "" }
1043+
"""
1044+
)
1045+
]
1046+
]
1047+
),
1048+
command: nil
1049+
)
1050+
1051+
XCTAssertTrue(codeActions.contains(expectedCodeAction))
1052+
}
1053+
1054+
func testConvertZeroParameterFunctionToComputedPropertyIsNotShownFromTheBody() async throws {
1055+
try await assertCodeActions(
1056+
##"""
1057+
func someFunction() -> String 1️⃣{
1058+
2️⃣return ""
1059+
}3️⃣
1060+
"""##,
1061+
exhaustive: false
1062+
) { uri, positions in
1063+
[]
1064+
}
1065+
}
1066+
1067+
func testConvertComputedPropertyToZeroParameterFunction() async throws {
1068+
let testClient = try await TestSourceKitLSPClient(capabilities: clientCapabilitiesWithCodeActionSupport)
1069+
let uri = DocumentURI(for: .swift)
1070+
1071+
let positions = testClient.openDocument(
1072+
"""
1073+
1️⃣var someFunction: String2️⃣ { return "" }3️⃣
1074+
""",
1075+
uri: uri
1076+
)
1077+
1078+
let request = CodeActionRequest(
1079+
range: positions["1️⃣"]..<positions["2️⃣"],
1080+
context: .init(),
1081+
textDocument: TextDocumentIdentifier(uri)
1082+
)
1083+
let result = try await testClient.send(request)
1084+
1085+
guard case .codeActions(let codeActions) = result else {
1086+
XCTFail("Expected code actions")
1087+
return
1088+
}
1089+
1090+
let expectedCodeAction = CodeAction(
1091+
title: "Convert to zero parameter function",
1092+
kind: .refactorInline,
1093+
diagnostics: nil,
1094+
edit: WorkspaceEdit(
1095+
changes: [
1096+
uri: [
1097+
TextEdit(
1098+
range: positions["1️⃣"]..<positions["3️⃣"],
1099+
newText: """
1100+
func someFunction() -> String { return "" }
1101+
"""
1102+
)
1103+
]
1104+
]
1105+
),
1106+
command: nil
1107+
)
1108+
1109+
XCTAssertTrue(codeActions.contains(expectedCodeAction))
1110+
}
1111+
1112+
func testConvertComputedPropertyToZeroParameterFunctionIsNotShownFromTheBody() async throws {
1113+
try await assertCodeActions(
1114+
##"""
1115+
var someFunction: String 1️⃣{
1116+
2️⃣return ""
1117+
}3️⃣
1118+
"""##,
1119+
exhaustive: false
1120+
) { uri, positions in
1121+
[]
1122+
}
1123+
}
1124+
10091125
/// Retrieves the code action at a set of markers and asserts that it matches a list of expected code actions.
10101126
///
10111127
/// - Parameters:

0 commit comments

Comments
 (0)