Skip to content

Commit fefd85c

Browse files
authored
Merge pull request swiftlang#144 from rmaz/separateregistries
Pass in MessageRegistry instead of using a singleton
2 parents 2b463d1 + 93982b2 commit fefd85c

File tree

8 files changed

+44
-49
lines changed

8 files changed

+44
-49
lines changed

Sources/LanguageServerProtocol/MessageRegistry.swift

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,19 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
import Dispatch
1413

1514
public final class MessageRegistry {
1615

17-
private(set)
18-
var methodToRequest: [String: _RequestType.Type] = {
19-
Dictionary(uniqueKeysWithValues: builtinRequests.map { ($0.method, $0) })
20-
}()
16+
public static let lspProtocol: MessageRegistry =
17+
MessageRegistry(requests: builtinRequests, notifications: builtinNotifications)
2118

22-
private(set)
23-
var methodToNotification: [String: NotificationType.Type] = {
24-
Dictionary(uniqueKeysWithValues: builtinNotifications.map { ($0.method, $0) })
25-
}()
19+
private let methodToRequest: [String: _RequestType.Type]
20+
private let methodToNotification: [String: NotificationType.Type]
2621

27-
/// The global message registry.
28-
public static let shared: MessageRegistry = .init()
22+
public init(requests: [_RequestType.Type], notifications: [NotificationType.Type]) {
23+
self.methodToRequest = Dictionary(uniqueKeysWithValues: requests.map { ($0.method, $0) })
24+
self.methodToNotification = Dictionary(uniqueKeysWithValues: notifications.map { ($0.method, $0) })
25+
}
2926

3027
/// Returns the type of the message named `method`, or nil if it is unknown.
3128
public func requestType(for method: String) -> _RequestType.Type? {
@@ -37,15 +34,4 @@ public final class MessageRegistry {
3734
return methodToNotification[method]
3835
}
3936

40-
/// Adds a new message type to the registry. **For test messages only; not thread-safe!**.
41-
public func _register(_ type: _RequestType.Type) {
42-
precondition(methodToRequest[type.method] == nil)
43-
methodToRequest[type.method] = type
44-
}
45-
46-
/// Adds a new message type to the registry. **For test messages only; not thread-safe!**.
47-
public func _register(_ type: NotificationType.Type) {
48-
precondition(methodToNotification[type.method] == nil)
49-
methodToNotification[type.method] = type
50-
}
5137
}

Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public final class JSONRPCConection {
2525
let sendQueue: DispatchQueue = DispatchQueue(label: "jsonrpc-send-queue", qos: .userInitiated)
2626
let receiveIO: DispatchIO
2727
let sendIO: DispatchIO
28+
let messageRegistry: MessageRegistry
2829

2930
enum State {
3031
case created, running, closed
@@ -50,9 +51,10 @@ public final class JSONRPCConection {
5051

5152
var closeHandler: () -> Void
5253

53-
public init(inFD: Int32, outFD: Int32, closeHandler: @escaping () -> Void = {}) {
54+
public init(protocol messageRegistry: MessageRegistry, inFD: Int32, outFD: Int32, closeHandler: @escaping () -> Void = {}) {
5455
state = .created
5556
self.closeHandler = closeHandler
57+
self.messageRegistry = messageRegistry
5658

5759
receiveIO = DispatchIO(type: .stream, fileDescriptor: inFD, queue: queue) { (error: Int32) in
5860
if error != 0 {
@@ -137,6 +139,9 @@ public final class JSONRPCConection {
137139

138140
let decoder = JSONDecoder()
139141

142+
// Set message registry to use for model decoding.
143+
decoder.userInfo[.messageRegistryKey] = messageRegistry
144+
140145
// Setup callback for response type.
141146
decoder.userInfo[.responseTypeCallbackKey] = { id in
142147
guard let outstanding = self.outstandingRequests[id] else {

Sources/LanguageServerProtocolJSONRPC/MessageCoding.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public enum JSONRPCMessage {
2222

2323
extension CodingUserInfoKey {
2424
public static let responseTypeCallbackKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "lsp.jsonrpc.responseTypeCallback")!
25+
public static let messageRegistryKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "lsp.jsonrpc.messageRegistry")!
2526
}
2627

2728
extension JSONRPCMessage: Codable {
@@ -39,6 +40,9 @@ extension JSONRPCMessage: Codable {
3940

4041
public init(from decoder: Decoder) throws {
4142

43+
guard let messageRegistry = decoder.userInfo[.messageRegistryKey] as? MessageRegistry else {
44+
fatalError("missing or invalid messageRegistryKey on decoder")
45+
}
4246
let container = try decoder.container(keyedBy: CodingKeys.self)
4347
let jsonrpc = try container.decodeIfPresent(String.self, forKey: .jsonrpc)
4448
let id = try container.decodeIfPresent(RequestID.self, forKey: .id)
@@ -58,7 +62,7 @@ extension JSONRPCMessage: Codable {
5862
case (nil, let method?, _, nil):
5963
msgKind = .notification
6064

61-
guard let messageType = MessageRegistry.shared.notificationType(for: method) else {
65+
guard let messageType = messageRegistry.notificationType(for: method) else {
6266
throw MessageDecodingError.methodNotFound(method)
6367
}
6468

@@ -69,7 +73,7 @@ extension JSONRPCMessage: Codable {
6973
case (let id?, let method?, _, nil):
7074
msgKind = .request
7175

72-
guard let messageType = MessageRegistry.shared.requestType(for: method) else {
76+
guard let messageType = messageRegistry.requestType(for: method) else {
7377
throw MessageDecodingError.methodNotFound(method)
7478
}
7579

Sources/SKTestSupport/TestJSONRPCConnection.swift

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import XCTest
1919
// Workaround ambiguity with Foundation.
2020
public typealias Notification = LanguageServerProtocol.Notification
2121

22-
public let initRequestsOnce: Void = registerTestRequests()
23-
2422
public struct TestJSONRPCConnection {
2523
public let clientToServer: Pipe = Pipe()
2624
public let serverToClient: Pipe = Pipe()
@@ -30,19 +28,19 @@ public struct TestJSONRPCConnection {
3028
public let serverConnection: JSONRPCConection
3129

3230
public init() {
33-
_ = initRequestsOnce
34-
3531
// FIXME: DispatchIO doesn't like when the Pipes close behind its back even after the tests
3632
// finish. Until we fix the lifetime, leak.
3733
_ = Unmanaged.passRetained(clientToServer)
3834
_ = Unmanaged.passRetained(serverToClient)
3935

4036
clientConnection = JSONRPCConection(
37+
protocol: testMessageRegistry,
4138
inFD: serverToClient.fileHandleForReading.fileDescriptor,
4239
outFD: clientToServer.fileHandleForWriting.fileDescriptor
4340
)
4441

4542
serverConnection = JSONRPCConection(
43+
protocol: testMessageRegistry,
4644
inFD: clientToServer.fileHandleForReading.fileDescriptor,
4745
outFD: serverToClient.fileHandleForWriting.fileDescriptor
4846
)
@@ -67,8 +65,6 @@ public struct TestLocalConnection {
6765
public let serverConnection: LocalConnection = .init()
6866

6967
public init() {
70-
_ = initRequestsOnce
71-
7268
client = TestClient(server: serverConnection)
7369
server = TestServer(client: clientConnection)
7470

@@ -230,16 +226,9 @@ public final class TestServer: LanguageServer {
230226

231227
// MARK: Test requests.
232228

233-
public func registerTestRequests() {
234-
[
235-
EchoRequest.self,
236-
EchoError.self,
237-
].forEach { MessageRegistry.shared._register($0) }
238-
239-
[
240-
EchoNotification.self,
241-
].forEach { MessageRegistry.shared._register($0) }
242-
}
229+
private let testMessageRegistry = MessageRegistry(
230+
requests: [EchoRequest.self, EchoError.self],
231+
notifications: [EchoNotification.self])
243232

244233
extension String: ResponseType {}
245234

Sources/SKTestSupport/TestServer.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public struct TestSourceKitServer {
4545
public let server: SourceKitServer?
4646

4747
public init(connectionKind: ConnectionKind = .local) {
48-
_ = initRequestsOnce
4948

5049
switch connectionKind {
5150
case .local:
@@ -71,10 +70,12 @@ public struct TestSourceKitServer {
7170
_ = Unmanaged.passRetained(serverToClient)
7271

7372
let clientConnection = JSONRPCConection(
73+
protocol: MessageRegistry.lspProtocol,
7474
inFD: serverToClient.fileHandleForReading.fileDescriptor,
7575
outFD: clientToServer.fileHandleForWriting.fileDescriptor
7676
)
7777
let serverConnection = JSONRPCConection(
78+
protocol: MessageRegistry.lspProtocol,
7879
inFD: clientToServer.fileHandleForReading.fileDescriptor,
7980
outFD: serverToClient.fileHandleForWriting.fileDescriptor
8081
)

Sources/SourceKit/clangd/ClangLanguageServer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ func makeJSONRPCClangServer(client: MessageHandler, toolchain: Toolchain, buildS
134134
let serverToClient: Pipe = Pipe()
135135

136136
let connection = JSONRPCConection(
137+
protocol: MessageRegistry.lspProtocol,
137138
inFD: serverToClient.fileHandleForReading.fileDescriptor,
138139
outFD: clientToServer.fileHandleForWriting.fileDescriptor
139140
)

Sources/sourcekit-lsp/main.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ func parseArguments() throws -> BuildSetup {
5353
flags: buildFlags)
5454
}
5555

56-
let clientConnection = JSONRPCConection(inFD: STDIN_FILENO, outFD: STDOUT_FILENO, closeHandler: {
56+
let clientConnection = JSONRPCConection(
57+
protocol: MessageRegistry.lspProtocol,
58+
inFD: STDIN_FILENO,
59+
outFD: STDOUT_FILENO,
60+
closeHandler: {
5761
exit(0)
5862
})
5963

Tests/LanguageServerProtocolJSONRPCTests/CodingTests.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ final class CodingTests: XCTestCase {
191191
return $0 == .string("unknown") ? nil : InitializeResult.self
192192
}
193193

194-
let info = [CodingUserInfoKey.responseTypeCallbackKey: responseTypeCallback]
194+
let info = defaultCodingInfo.merging([CodingUserInfoKey.responseTypeCallbackKey: responseTypeCallback]) { (_, new) in new }
195195

196196
checkMessageDecodingError(MessageDecodingError.invalidRequest("message not recognized as request, response or notification", id: .number(2)), json: """
197197
{"jsonrpc":"2.0","id":2,"params":{}}
@@ -215,8 +215,10 @@ final class CodingTests: XCTestCase {
215215
}
216216
}
217217

218+
let defaultCodingInfo: [CodingUserInfoKey: Any] = [CodingUserInfoKey.messageRegistryKey:MessageRegistry.lspProtocol]
219+
218220
private func checkMessageCoding<Request>(_ value: Request, id: RequestID, json: String, file: StaticString = #file, line: UInt = #line) where Request: RequestType & Equatable {
219-
checkCoding(JSONRPCMessage.request(value, id: id), json: json, file: file, line: line) {
221+
checkCoding(JSONRPCMessage.request(value, id: id), json: json, userInfo: defaultCodingInfo, file: file, line: line) {
220222

221223
guard case JSONRPCMessage.request(let decodedValueOpaque, let decodedID) = $0, let decodedValue = decodedValueOpaque as? Request else {
222224
XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line)
@@ -229,7 +231,7 @@ private func checkMessageCoding<Request>(_ value: Request, id: RequestID, json:
229231
}
230232

231233
private func checkMessageCoding<Notification>(_ value: Notification, json: String, file: StaticString = #file, line: UInt = #line) where Notification: NotificationType & Equatable {
232-
checkCoding(JSONRPCMessage.notification(value), json: json, file: file, line: line) {
234+
checkCoding(JSONRPCMessage.notification(value), json: json, userInfo: defaultCodingInfo, file: file, line: line) {
233235

234236
guard case JSONRPCMessage.notification(let decodedValueOpaque) = $0, let decodedValue = decodedValueOpaque as? Notification else {
235237
XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line)
@@ -246,7 +248,10 @@ private func checkMessageCoding<Response>(_ value: Response, id: RequestID, json
246248
return $0 == .string("unknown") ? nil : Response.self
247249
}
248250

249-
checkCoding(JSONRPCMessage.response(value, id: id), json: json, userInfo: [.responseTypeCallbackKey: callback], file: file, line: line) {
251+
var codingInfo = defaultCodingInfo
252+
codingInfo[.responseTypeCallbackKey] = callback
253+
254+
checkCoding(JSONRPCMessage.response(value, id: id), json: json, userInfo: codingInfo, file: file, line: line) {
250255

251256
guard case JSONRPCMessage.response(let decodedValueOpaque, let decodedID) = $0, let decodedValue = decodedValueOpaque as? Response else {
252257
XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line)
@@ -259,7 +264,7 @@ private func checkMessageCoding<Response>(_ value: Response, id: RequestID, json
259264
}
260265

261266
private func checkMessageCoding(_ value: ResponseError, id: RequestID, json: String, file: StaticString = #file, line: UInt = #line) {
262-
checkCoding(JSONRPCMessage.errorResponse(value, id: id), json: json, file: file, line: line) {
267+
checkCoding(JSONRPCMessage.errorResponse(value, id: id), json: json, userInfo: defaultCodingInfo, file: file, line: line) {
263268

264269
guard case JSONRPCMessage.errorResponse(let decodedValue, let decodedID) = $0 else {
265270
XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line)
@@ -271,7 +276,7 @@ private func checkMessageCoding(_ value: ResponseError, id: RequestID, json: Str
271276
}
272277
}
273278

274-
private func checkMessageDecodingError(_ expected: MessageDecodingError, json: String, userInfo: [CodingUserInfoKey: Any] = [:], file: StaticString = #file, line: UInt = #line) {
279+
private func checkMessageDecodingError(_ expected: MessageDecodingError, json: String, userInfo: [CodingUserInfoKey: Any] = defaultCodingInfo, file: StaticString = #file, line: UInt = #line) {
275280
let data = json.data(using: .utf8)!
276281
let decoder = JSONDecoder()
277282
decoder.userInfo = userInfo

0 commit comments

Comments
 (0)