Skip to content

Commit ca47033

Browse files
authored
Merge pull request #1008 from ahoppen/ahoppen/skd-array-dictionary-from-literal
Support creation of SKDRequest(Dictionary|Array) from literals
2 parents a6fe484 + 3991fae commit ca47033

12 files changed

+245
-211
lines changed

Sources/SourceKitD/SKDRequestArray.swift

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ import Musl
2020
import CRT
2121
#endif
2222

23+
extension SourceKitD {
24+
/// Create a `SKDRequestArray` from the given array.
25+
public func array(_ array: [SKDValue]) -> SKDRequestArray {
26+
let result = SKDRequestArray(sourcekitd: self)
27+
for element in array {
28+
result.append(element)
29+
}
30+
return result
31+
}
32+
}
33+
2334
public final class SKDRequestArray {
2435
public let array: sourcekitd_object_t?
2536
public let sourcekitd: SourceKitD
@@ -33,15 +44,32 @@ public final class SKDRequestArray {
3344
sourcekitd.api.request_release(array)
3445
}
3546

36-
public func append(_ value: String) {
37-
sourcekitd.api.request_array_set_string(array, -1, value)
38-
}
39-
40-
public func append(_ value: SKDRequestDictionary) {
41-
sourcekitd.api.request_array_set_value(array, -1, value.dict)
47+
public func append(_ newValue: SKDValue) {
48+
switch newValue {
49+
case let newValue as String:
50+
sourcekitd.api.request_array_set_string(array, -1, newValue)
51+
case let newValue as Int:
52+
sourcekitd.api.request_array_set_int64(array, -1, Int64(newValue))
53+
case let newValue as sourcekitd_uid_t:
54+
sourcekitd.api.request_array_set_uid(array, -1, newValue)
55+
case let newValue as SKDRequestDictionary:
56+
sourcekitd.api.request_array_set_value(array, -1, newValue.dict)
57+
case let newValue as SKDRequestArray:
58+
sourcekitd.api.request_array_set_value(array, -1, newValue.array)
59+
case let newValue as Array<SKDValue>:
60+
self.append(sourcekitd.array(newValue))
61+
case let newValue as Dictionary<sourcekitd_uid_t, SKDValue>:
62+
self.append(sourcekitd.dictionary(newValue))
63+
case let newValue as Optional<SKDValue>:
64+
if let newValue {
65+
self.append(newValue)
66+
}
67+
default:
68+
preconditionFailure("Unknown type conforming to SKDValueProtocol")
69+
}
4270
}
4371

44-
public static func += (array: SKDRequestArray, other: some Sequence<SKDRequestDictionary>) {
72+
public static func += (array: SKDRequestArray, other: some Sequence<SKDValue>) {
4573
for item in other {
4674
array.append(item)
4775
}

Sources/SourceKitD/SKDRequestDictionary.swift

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@ import Musl
2121
import CRT
2222
#endif
2323

24+
/// Values that can be stored in a `SKDRequestDictionary`.
25+
///
26+
/// - Warning: `SKDRequestDictionary.subscript` and `SKDRequestArray.append`
27+
/// switch exhaustively over this protocol.
28+
/// Do not add new conformances without adding a new case in the subscript and
29+
/// `append` function.
30+
public protocol SKDValue {}
31+
32+
extension String: SKDValue {}
33+
extension Int: SKDValue {}
34+
extension sourcekitd_uid_t: SKDValue {}
35+
extension SKDRequestDictionary: SKDValue {}
36+
extension SKDRequestArray: SKDValue {}
37+
extension Array<SKDValue>: SKDValue {}
38+
extension Dictionary<sourcekitd_uid_t, SKDValue>: SKDValue {}
39+
extension Optional: SKDValue where Wrapped: SKDValue {}
40+
41+
extension SourceKitD {
42+
/// Create a `SKDRequestDictionary` from the given dictionary.
43+
public func dictionary(_ dict: [sourcekitd_uid_t: SKDValue]) -> SKDRequestDictionary {
44+
let result = SKDRequestDictionary(sourcekitd: self)
45+
for (key, value) in dict {
46+
result.set(key, to: value)
47+
}
48+
return result
49+
}
50+
}
51+
2452
public final class SKDRequestDictionary {
2553
public let dict: sourcekitd_object_t?
2654
public let sourcekitd: SourceKitD
@@ -34,34 +62,30 @@ public final class SKDRequestDictionary {
3462
sourcekitd.api.request_release(dict)
3563
}
3664

37-
public subscript(key: sourcekitd_uid_t?) -> String {
38-
get { fatalError("request is set-only") }
39-
set { sourcekitd.api.request_dictionary_set_string(dict, key, newValue) }
40-
}
41-
public subscript(key: sourcekitd_uid_t?) -> Int {
42-
get { fatalError("request is set-only") }
43-
set { sourcekitd.api.request_dictionary_set_int64(dict, key, Int64(newValue)) }
44-
}
45-
public subscript(key: sourcekitd_uid_t?) -> sourcekitd_uid_t? {
46-
get { fatalError("request is set-only") }
47-
set { sourcekitd.api.request_dictionary_set_uid(dict, key, newValue) }
48-
}
49-
public subscript(key: sourcekitd_uid_t?) -> SKDRequestDictionary {
50-
get { fatalError("request is set-only") }
51-
set { sourcekitd.api.request_dictionary_set_value(dict, key, newValue.dict) }
52-
}
53-
public subscript<S>(key: sourcekitd_uid_t?) -> S where S: Sequence, S.Element == String {
54-
get { fatalError("request is set-only") }
55-
set {
56-
let array = SKDRequestArray(sourcekitd: sourcekitd)
57-
newValue.forEach { array.append($0) }
58-
sourcekitd.api.request_dictionary_set_value(dict, key, array.array)
65+
public func set(_ key: sourcekitd_uid_t, to newValue: SKDValue) {
66+
switch newValue {
67+
case let newValue as String:
68+
sourcekitd.api.request_dictionary_set_string(dict, key, newValue)
69+
case let newValue as Int:
70+
sourcekitd.api.request_dictionary_set_int64(dict, key, Int64(newValue))
71+
case let newValue as sourcekitd_uid_t:
72+
sourcekitd.api.request_dictionary_set_uid(dict, key, newValue)
73+
case let newValue as SKDRequestDictionary:
74+
sourcekitd.api.request_dictionary_set_value(dict, key, newValue.dict)
75+
case let newValue as SKDRequestArray:
76+
sourcekitd.api.request_dictionary_set_value(dict, key, newValue.array)
77+
case let newValue as Array<SKDValue>:
78+
self.set(key, to: sourcekitd.array(newValue))
79+
case let newValue as Dictionary<sourcekitd_uid_t, SKDValue>:
80+
self.set(key, to: sourcekitd.dictionary(newValue))
81+
case let newValue as Optional<SKDValue>:
82+
if let newValue {
83+
self.set(key, to: newValue)
84+
}
85+
default:
86+
preconditionFailure("Unknown type conforming to SKDValueProtocol")
5987
}
6088
}
61-
public subscript(key: sourcekitd_uid_t?) -> SKDRequestArray {
62-
get { fatalError("request is set-only") }
63-
set { sourcekitd.api.request_dictionary_set_value(dict, key, newValue.array) }
64-
}
6589
}
6690

6791
extension SKDRequestDictionary: CustomStringConvertible {

Sources/SourceKitLSP/Rename.swift

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -389,28 +389,28 @@ extension SwiftLanguageServer {
389389
oldName: String,
390390
in snapshot: DocumentSnapshot
391391
) async throws -> [SyntacticRenameName] {
392-
let locations = SKDRequestArray(sourcekitd: sourcekitd)
393-
locations += renameLocations.map { renameLocation in
394-
let skRenameLocation = SKDRequestDictionary(sourcekitd: sourcekitd)
395-
skRenameLocation[keys.line] = renameLocation.line
396-
skRenameLocation[keys.column] = renameLocation.utf8Column
397-
skRenameLocation[keys.nameType] = renameLocation.usage.uid(keys: keys)
398-
return skRenameLocation
399-
}
400-
let renameLocation = SKDRequestDictionary(sourcekitd: sourcekitd)
401-
renameLocation[keys.locations] = locations
402-
renameLocation[keys.name] = oldName
403-
404-
let renameLocations = SKDRequestArray(sourcekitd: sourcekitd)
405-
renameLocations.append(renameLocation)
406-
407-
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
408-
skreq[keys.request] = requests.find_syntactic_rename_ranges
409-
skreq[keys.sourcefile] = snapshot.uri.pseudoPath
410-
// find-syntactic-rename-ranges is a syntactic sourcekitd request that doesn't use the in-memory file snapshot.
411-
// We need to send the source text again.
412-
skreq[keys.sourcetext] = snapshot.text
413-
skreq[keys.renamelocations] = renameLocations
392+
let locations = sourcekitd.array(
393+
renameLocations.map { renameLocation in
394+
sourcekitd.dictionary([
395+
keys.line: renameLocation.line,
396+
keys.column: renameLocation.utf8Column,
397+
keys.nameType: renameLocation.usage.uid(keys: keys),
398+
])
399+
}
400+
)
401+
let renameLocation = sourcekitd.dictionary([
402+
keys.locations: locations,
403+
keys.name: oldName,
404+
])
405+
406+
let skreq = sourcekitd.dictionary([
407+
keys.request: requests.find_syntactic_rename_ranges,
408+
keys.sourcefile: snapshot.uri.pseudoPath,
409+
// find-syntactic-rename-ranges is a syntactic sourcekitd request that doesn't use the in-memory file snapshot.
410+
// We need to send the source text again.
411+
keys.sourcetext: snapshot.text,
412+
keys.renamelocations: [renameLocation],
413+
])
414414

415415
let syntacticRenameRangesResponse = try await sourcekitd.send(skreq, fileContents: snapshot.text)
416416
guard let categorizedRanges: SKDResponseArray = syntacticRenameRangesResponse[keys.categorizedranges] else {

Sources/SourceKitLSP/Swift/CodeCompletionSession.swift

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,15 @@ class CodeCompletionSession {
191191
throw ResponseError(code: .invalidRequest, message: "open must use the original snapshot")
192192
}
193193

194-
let req = SKDRequestDictionary(sourcekitd: sourcekitd)
195-
req[keys.request] = sourcekitd.requests.codecomplete_open
196-
req[keys.offset] = utf8StartOffset
197-
req[keys.name] = uri.pseudoPath
198-
req[keys.sourcefile] = uri.pseudoPath
199-
req[keys.sourcetext] = snapshot.text
200-
req[keys.codecomplete_options] = optionsDictionary(filterText: filterText, options: options)
201-
if let compileCommand = compileCommand {
202-
req[keys.compilerargs] = compileCommand.compilerArgs
203-
}
194+
let req = sourcekitd.dictionary([
195+
keys.request: sourcekitd.requests.codecomplete_open,
196+
keys.offset: utf8StartOffset,
197+
keys.name: uri.pseudoPath,
198+
keys.sourcefile: uri.pseudoPath,
199+
keys.sourcetext: snapshot.text,
200+
keys.codecomplete_options: optionsDictionary(filterText: filterText, options: options),
201+
keys.compilerargs: compileCommand?.compilerArgs as [SKDValue]?,
202+
])
204203

205204
let dict = try await sourcekitd.send(req, fileContents: snapshot.text)
206205
self.state = .open
@@ -229,11 +228,12 @@ class CodeCompletionSession {
229228
// FIXME: Assertion for prefix of snapshot matching what we started with.
230229

231230
logger.info("Updating code completion session: \(self, privacy: .private) filter=\(filterText)")
232-
let req = SKDRequestDictionary(sourcekitd: sourcekitd)
233-
req[keys.request] = sourcekitd.requests.codecomplete_update
234-
req[keys.offset] = utf8StartOffset
235-
req[keys.name] = uri.pseudoPath
236-
req[keys.codecomplete_options] = optionsDictionary(filterText: filterText, options: options)
231+
let req = sourcekitd.dictionary([
232+
keys.request: sourcekitd.requests.codecomplete_update,
233+
keys.offset: utf8StartOffset,
234+
keys.name: uri.pseudoPath,
235+
keys.codecomplete_options: optionsDictionary(filterText: filterText, options: options),
236+
])
237237

238238
let dict = try await sourcekitd.send(req, fileContents: snapshot.text)
239239
guard let completions: SKDResponseArray = dict[keys.results] else {
@@ -253,19 +253,18 @@ class CodeCompletionSession {
253253
filterText: String,
254254
options: SKCompletionOptions
255255
) -> SKDRequestDictionary {
256-
let dict = SKDRequestDictionary(sourcekitd: sourcekitd)
257-
// Sorting and priority options.
258-
dict[keys.codecomplete_hideunderscores] = 0
259-
dict[keys.codecomplete_hidelowpriority] = 0
260-
dict[keys.codecomplete_hidebyname] = 0
261-
dict[keys.codecomplete_addinneroperators] = 0
262-
dict[keys.codecomplete_callpatternheuristics] = 0
263-
dict[keys.codecomplete_showtopnonliteralresults] = 0
264-
// Filtering options.
265-
dict[keys.codecomplete_filtertext] = filterText
266-
if let maxResults = options.maxResults {
267-
dict[keys.codecomplete_requestlimit] = maxResults
268-
}
256+
let dict = sourcekitd.dictionary([
257+
// Sorting and priority options.
258+
keys.codecomplete_hideunderscores: 0,
259+
keys.codecomplete_hidelowpriority: 0,
260+
keys.codecomplete_hidebyname: 0,
261+
keys.codecomplete_addinneroperators: 0,
262+
keys.codecomplete_callpatternheuristics: 0,
263+
keys.codecomplete_showtopnonliteralresults: 0,
264+
// Filtering options.
265+
keys.codecomplete_filtertext: filterText,
266+
keys.codecomplete_requestlimit: options.maxResults,
267+
])
269268
return dict
270269
}
271270

@@ -275,10 +274,11 @@ class CodeCompletionSession {
275274
// Already closed, nothing to do.
276275
break
277276
case .open:
278-
let req = SKDRequestDictionary(sourcekitd: sourcekitd)
279-
req[keys.request] = sourcekitd.requests.codecomplete_close
280-
req[keys.offset] = self.utf8StartOffset
281-
req[keys.name] = self.snapshot.uri.pseudoPath
277+
let req = sourcekitd.dictionary([
278+
keys.request: sourcekitd.requests.codecomplete_close,
279+
keys.offset: utf8StartOffset,
280+
keys.name: snapshot.uri.pseudoPath,
281+
])
282282
logger.info("Closing code completion session: \(self, privacy: .private)")
283283
_ = try? await sourcekitd.send(req, fileContents: nil)
284284
self.state = .closed

Sources/SourceKitLSP/Swift/CursorInfo.swift

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,14 @@ extension SwiftLanguageServer {
9292

9393
let keys = self.keys
9494

95-
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
96-
skreq[keys.request] = requests.cursorinfo
97-
skreq[keys.cancelOnSubsequentRequest] = 0
98-
skreq[keys.offset] = offsetRange.lowerBound
99-
if offsetRange.upperBound != offsetRange.lowerBound {
100-
skreq[keys.length] = offsetRange.count
101-
}
102-
skreq[keys.sourcefile] = snapshot.uri.pseudoPath
103-
104-
// FIXME: SourceKit should probably cache this for us.
105-
if let compileCommand = await self.buildSettings(for: uri) {
106-
skreq[keys.compilerargs] = compileCommand.compilerArgs
107-
}
95+
let skreq = sourcekitd.dictionary([
96+
keys.request: requests.cursorinfo,
97+
keys.cancelOnSubsequentRequest: 0,
98+
keys.offset: offsetRange.lowerBound,
99+
keys.length: offsetRange.upperBound != offsetRange.lowerBound ? offsetRange.count : nil,
100+
keys.sourcefile: snapshot.uri.pseudoPath,
101+
keys.compilerargs: await self.buildSettings(for: uri)?.compilerArgs as [SKDValue]?,
102+
])
108103

109104
appendAdditionalParameters?(skreq)
110105

Sources/SourceKitLSP/Swift/OpenInterface.swift

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,14 @@ extension SwiftLanguageServer {
7070
interfaceURI: DocumentURI
7171
) async throws -> InterfaceInfo {
7272
let keys = self.keys
73-
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
74-
skreq[keys.request] = requests.editor_open_interface
75-
skreq[keys.modulename] = name
76-
if request.groupNames.count > 0 {
77-
skreq[keys.groupname] = request.groupNames
78-
}
79-
skreq[keys.name] = interfaceURI.pseudoPath
80-
skreq[keys.synthesizedextensions] = 1
81-
if let compileCommand = await self.buildSettings(for: uri) {
82-
skreq[keys.compilerargs] = compileCommand.compilerArgs
83-
}
73+
let skreq = sourcekitd.dictionary([
74+
keys.request: requests.editor_open_interface,
75+
keys.modulename: name,
76+
keys.groupname: request.groupNames.isEmpty ? nil : request.groupNames as [SKDValue],
77+
keys.name: interfaceURI.pseudoPath,
78+
keys.synthesizedextensions: 1,
79+
keys.compilerargs: await self.buildSettings(for: uri)?.compilerArgs as [SKDValue]?,
80+
])
8481

8582
let dict = try await self.sourcekitd.send(skreq, fileContents: nil)
8683
return InterfaceInfo(contents: dict[keys.sourcetext] ?? "")
@@ -97,11 +94,11 @@ extension SwiftLanguageServer {
9794
return InterfaceDetails(uri: uri, position: nil)
9895
}
9996
let keys = self.keys
100-
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
101-
102-
skreq[keys.request] = requests.find_usr
103-
skreq[keys.sourcefile] = uri.pseudoPath
104-
skreq[keys.usr] = symbol
97+
let skreq = sourcekitd.dictionary([
98+
keys.request: requests.find_usr,
99+
keys.sourcefile: uri.pseudoPath,
100+
keys.usr: symbol,
101+
])
105102

106103
let dict = try await self.sourcekitd.send(skreq, fileContents: snapshot.text)
107104
if let offset: Int = dict[keys.offset],

Sources/SourceKitLSP/Swift/RelatedIdentifiers.swift

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,14 @@ extension SwiftLanguageServer {
6666
throw ResponseError.unknown("invalid position \(position)")
6767
}
6868

69-
let skreq = SKDRequestDictionary(sourcekitd: self.sourcekitd)
70-
skreq[keys.request] = self.requests.relatedidents
71-
skreq[keys.cancelOnSubsequentRequest] = 0
72-
skreq[keys.offset] = offset
73-
skreq[keys.sourcefile] = snapshot.uri.pseudoPath
74-
skreq[keys.includeNonEditableBaseNames] = includeNonEditableBaseNames ? 1 : 0
75-
76-
// FIXME: SourceKit should probably cache this for us.
77-
if let compileCommand = await self.buildSettings(for: snapshot.uri) {
78-
skreq[keys.compilerargs] = compileCommand.compilerArgs
79-
}
69+
let skreq = sourcekitd.dictionary([
70+
keys.request: requests.relatedidents,
71+
keys.cancelOnSubsequentRequest: 0,
72+
keys.offset: offset,
73+
keys.sourcefile: snapshot.uri.pseudoPath,
74+
keys.includeNonEditableBaseNames: includeNonEditableBaseNames ? 1 : 0,
75+
keys.compilerargs: await self.buildSettings(for: snapshot.uri)?.compilerArgs as [SKDValue]?,
76+
])
8077

8178
let dict = try await self.sourcekitd.send(skreq, fileContents: snapshot.text)
8279

0 commit comments

Comments
 (0)