Skip to content

Commit d8c5797

Browse files
committed
Add ServerNotificationHandler to handle notifications
1 parent 6101a44 commit d8c5797

1 file changed

Lines changed: 112 additions & 64 deletions

File tree

Tool/Sources/GitHubCopilotService/LanguageServer/CopilotLocalProcessServer.swift

Lines changed: 112 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,43 @@ class CopilotLocalProcessServer {
1818
public convenience init(
1919
path: String,
2020
arguments: [String],
21-
environment: [String: String]? = nil
21+
environment: [String: String]? = nil,
22+
serverNotificationHandler: ServerNotificationHandler
2223
) {
2324
let params = Process.ExecutionParameters(
2425
path: path,
2526
arguments: arguments,
2627
environment: environment
2728
)
2829

29-
self.init(executionParameters: params)
30+
self.init(executionParameters: params, serverNotificationHandler: serverNotificationHandler)
3031
}
3132

32-
init(executionParameters parameters: Process.ExecutionParameters) {
33+
init(
34+
executionParameters parameters: Process.ExecutionParameters,
35+
serverNotificationHandler: ServerNotificationHandler
36+
) {
3337
transport = StdioDataTransport()
3438
let framing = SeperatedHTTPHeaderMessageFraming()
3539
let messageTransport = MessageTransport(
3640
dataTransport: transport,
3741
messageProtocol: framing
3842
)
3943
customTransport = CustomDataTransport(nextTransport: messageTransport)
40-
wrappedServer = CustomJSONRPCLanguageServer(dataTransport: customTransport)
44+
wrappedServer = CustomJSONRPCLanguageServer(
45+
dataTransport: customTransport,
46+
serverNotificationHandler: serverNotificationHandler
47+
)
4148

4249
process = Process()
4350

4451
// Because the implementation of LanguageClient is so closed,
4552
// we need to get the request IDs from a custom transport before the data
4653
// is written to the language server.
4754
customTransport.onWriteRequest = { [weak self] request in
48-
if request.method == "getCompletionsCycling"
49-
|| request.method == "textDocument/inlineCompletion" {
55+
if request.method == "getCompletionsCycling"
56+
|| request.method == "textDocument/inlineCompletion"
57+
{
5058
Task { @MainActor [weak self] in
5159
self?.ongoingCompletionRequestIDs.append(request.id)
5260
}
@@ -85,7 +93,7 @@ class CopilotLocalProcessServer {
8593
get { return wrappedServer?.logMessages ?? false }
8694
set { wrappedServer?.logMessages = newValue }
8795
}
88-
96+
8997
func terminate() {
9098
process.terminate()
9199
}
@@ -155,36 +163,47 @@ final class CustomJSONRPCLanguageServer: Server {
155163

156164
private let protocolTransport: ProtocolTransport
157165

158-
public var requestHandler: RequestHandler?
159-
public var notificationHandler: NotificationHandler?
166+
var requestHandler: RequestHandler?
167+
var serverNotificationHandler: ServerNotificationHandler
168+
169+
@available(*, deprecated, message: "Use `serverNotificationHandler` instead.")
170+
var notificationHandler: NotificationHandler? {
171+
get { nil }
172+
set {}
173+
}
160174

161175
private var outOfBandError: Error?
162176

163-
init(protocolTransport: ProtocolTransport) {
177+
init(
178+
protocolTransport: ProtocolTransport,
179+
serverNotificationHandler: ServerNotificationHandler
180+
) {
181+
self.serverNotificationHandler = serverNotificationHandler
164182
self.protocolTransport = protocolTransport
165183
internalServer = JSONRPCLanguageServer(protocolTransport: protocolTransport)
166184

167-
let previouseRequestHandler = protocolTransport.requestHandler
168-
let previouseNotificationHandler = protocolTransport.notificationHandler
185+
let previousRequestHandler = protocolTransport.requestHandler
169186

170-
protocolTransport
171-
.requestHandler = { [weak self] in
172-
guard let self else { return }
173-
if !self.handleRequest($0, data: $1, callback: $2) {
174-
previouseRequestHandler?($0, $1, $2)
175-
}
176-
}
177-
protocolTransport
178-
.notificationHandler = { [weak self] in
179-
guard let self else { return }
180-
if !self.handleNotification($0, data: $1, block: $2) {
181-
previouseNotificationHandler?($0, $1, $2)
182-
}
187+
protocolTransport.requestHandler = { [weak self] in
188+
guard let self else { return }
189+
if !self.handleRequest($0, data: $1, callback: $2) {
190+
previousRequestHandler?($0, $1, $2)
183191
}
192+
}
193+
protocolTransport.notificationHandler = { [weak self] in
194+
guard let self else { return }
195+
self.handleNotification($0, data: $1, block: $2)
196+
}
184197
}
185198

186-
convenience init(dataTransport: DataTransport) {
187-
self.init(protocolTransport: ProtocolTransport(dataTransport: dataTransport))
199+
convenience init(
200+
dataTransport: DataTransport,
201+
serverNotificationHandler: ServerNotificationHandler
202+
) {
203+
self.init(
204+
protocolTransport: ProtocolTransport(dataTransport: dataTransport),
205+
serverNotificationHandler: serverNotificationHandler
206+
)
188207
}
189208

190209
deinit {
@@ -203,7 +222,72 @@ extension CustomJSONRPCLanguageServer {
203222
_ anyNotification: AnyJSONRPCNotification,
204223
data: Data,
205224
block: @escaping (Error?) -> Void
225+
) {
226+
Task {
227+
do {
228+
try await serverNotificationHandler.handleNotification(
229+
anyNotification,
230+
data: data
231+
)
232+
block(nil)
233+
} catch {
234+
block(error)
235+
}
236+
}
237+
}
238+
239+
func sendNotification(
240+
_ notif: ClientNotification,
241+
completionHandler: @escaping (ServerError?) -> Void
242+
) {
243+
internalServer.sendNotification(notif, completionHandler: completionHandler)
244+
}
245+
}
246+
247+
extension CustomJSONRPCLanguageServer {
248+
private func handleRequest(
249+
_ request: AnyJSONRPCRequest,
250+
data: Data,
251+
callback: @escaping (AnyJSONRPCResponse) -> Void
206252
) -> Bool {
253+
return false
254+
}
255+
}
256+
257+
extension CustomJSONRPCLanguageServer {
258+
public func sendRequest<Response: Codable>(
259+
_ request: ClientRequest,
260+
completionHandler: @escaping (ServerResult<Response>) -> Void
261+
) {
262+
internalServer.sendRequest(request, completionHandler: completionHandler)
263+
}
264+
}
265+
266+
@GitHubCopilotSuggestionActor
267+
final class ServerNotificationHandler {
268+
typealias Handler = (
269+
_ anyNotification: AnyJSONRPCNotification,
270+
_ data: Data
271+
) async throws -> Bool
272+
273+
var handlers = [AnyHashable: Handler]()
274+
nonisolated init() {}
275+
276+
func handleNotification(
277+
_ anyNotification: AnyJSONRPCNotification,
278+
data: Data
279+
) async throws {
280+
for handler in handlers.values {
281+
do {
282+
let handled = try await handler(anyNotification, data)
283+
if handled {
284+
return
285+
}
286+
} catch {
287+
throw ServerError.notificationDispatchFailed(error)
288+
}
289+
}
290+
207291
let methodName = anyNotification.method
208292
let debugDescription = {
209293
if let params = anyNotification.params {
@@ -223,65 +307,29 @@ extension CustomJSONRPCLanguageServer {
223307
Logger.gitHubCopilot
224308
.info("\(anyNotification.method): \(debugDescription)")
225309
}
226-
block(nil)
227-
return true
228310
case "LogMessage":
229311
if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) {
230312
Logger.gitHubCopilot
231313
.info("\(anyNotification.method): \(debugDescription)")
232314
}
233-
block(nil)//
234-
return true
235315
case "statusNotification":
236316
if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) {
237317
Logger.gitHubCopilot
238318
.info("\(anyNotification.method): \(debugDescription)")
239319
}
240-
block(nil)
241-
return true
242320
case "featureFlagsNotification":
243321
if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) {
244322
Logger.gitHubCopilot
245323
.info("\(anyNotification.method): \(debugDescription)")
246324
}
247-
block(nil)
248-
return true
249325
case "conversation/preconditionsNotification":
250326
if UserDefaults.shared.value(for: \.gitHubCopilotVerboseLog) {
251327
Logger.gitHubCopilot
252328
.info("\(anyNotification.method): \(debugDescription)")
253329
}
254-
block(nil)
255-
return true
256330
default:
257-
return false
331+
throw ServerError.handlerUnavailable(methodName)
258332
}
259333
}
260-
261-
public func sendNotification(
262-
_ notif: ClientNotification,
263-
completionHandler: @escaping (ServerError?) -> Void
264-
) {
265-
internalServer.sendNotification(notif, completionHandler: completionHandler)
266-
}
267-
}
268-
269-
extension CustomJSONRPCLanguageServer {
270-
private func handleRequest(
271-
_ request: AnyJSONRPCRequest,
272-
data: Data,
273-
callback: @escaping (AnyJSONRPCResponse) -> Void
274-
) -> Bool {
275-
return false
276-
}
277-
}
278-
279-
extension CustomJSONRPCLanguageServer {
280-
public func sendRequest<Response: Codable>(
281-
_ request: ClientRequest,
282-
completionHandler: @escaping (ServerResult<Response>) -> Void
283-
) {
284-
internalServer.sendRequest(request, completionHandler: completionHandler)
285-
}
286334
}
287335

0 commit comments

Comments
 (0)