@@ -45,10 +45,10 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
45
45
/// The queue on which clangd calls us back.
46
46
public let clangdCommunicationQueue : DispatchQueue = DispatchQueue ( label: " language-server-queue " , qos: . userInitiated)
47
47
48
- /// The connection to the client. In the case of `ClangLanguageServerShim`,
49
- /// the client is always a ``SourceKitServer``, which will forward the request
50
- /// to the editor.
51
- public let client : Connection
48
+ /// The ``SourceKitServer`` instance that created this `ClangLanguageServerShim`.
49
+ ///
50
+ /// Used to send requests and notifications to the editor.
51
+ private weak var sourceKitServer : SourceKitServer ?
52
52
53
53
/// The connection to the clangd LSP. `nil` until `startClangdProcesss` has been called.
54
54
var clangd : Connection !
@@ -93,9 +93,6 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
93
93
/// `clangd` doesn't have support for multi-root workspaces, so we need to start a separate `clangd` instance for every workspace root.
94
94
private let workspace : WeakWorkspace
95
95
96
- /// A callback with which `ClangLanguageServer` can request its owner to reopen all documents in case it has crashed.
97
- private let reopenDocuments : ( ToolchainLanguageServer ) async -> Void
98
-
99
96
/// The documents that have been opened and which language they have been
100
97
/// opened with.
101
98
private var openDocuments : [ DocumentURI : Language ] = [ : ]
@@ -110,12 +107,10 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
110
107
/// Creates a language server for the given client referencing the clang binary specified in `toolchain`.
111
108
/// Returns `nil` if `clangd` can't be found.
112
109
public init ? (
113
- client : LocalConnection ,
110
+ sourceKitServer : SourceKitServer ,
114
111
toolchain: Toolchain ,
115
112
options: SourceKitServer . Options ,
116
- workspace: Workspace ,
117
- reopenDocuments: @escaping ( ToolchainLanguageServer ) async -> Void ,
118
- workspaceForDocument: @escaping ( DocumentURI ) async -> Workspace ?
113
+ workspace: Workspace
119
114
) async throws {
120
115
guard let clangdPath = toolchain. clangd else {
121
116
return nil
@@ -124,9 +119,8 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
124
119
self . clangdPath = clangdPath
125
120
self . clangdOptions = options. clangdOptions
126
121
self . workspace = WeakWorkspace ( workspace)
127
- self . reopenDocuments = reopenDocuments
128
122
self . state = . connected
129
- self . client = client
123
+ self . sourceKitServer = sourceKitServer
130
124
try startClangdProcesss ( )
131
125
}
132
126
@@ -245,7 +239,11 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
245
239
// But since SourceKitServer more or less ignores them right now anyway, this should be fine for now.
246
240
_ = try self . initializeSync ( initializeRequest)
247
241
self . clientInitialized ( InitializedNotification ( ) )
248
- await self . reopenDocuments ( self )
242
+ if let sourceKitServer {
243
+ await sourceKitServer. reopenDocuments ( for: self )
244
+ } else {
245
+ log ( " Cannot reopen documents because SourceKitServer is no longer alive " , level: . error)
246
+ }
249
247
self . state = . connected
250
248
} catch {
251
249
log ( " Failed to restart clangd after a crash. " , level: . error)
@@ -256,12 +254,15 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
256
254
/// Handler for notifications received **from** clangd, ie. **clangd** is
257
255
/// sending a notification that's intended for the editor.
258
256
///
259
- /// We should either handle it ourselves or forward it to the client .
257
+ /// We should either handle it ourselves or forward it to the editor .
260
258
func handle( _ params: some NotificationType , from clientID: ObjectIdentifier ) async {
261
- if let publishDiags = params as? PublishDiagnosticsNotification {
259
+ switch params {
260
+ case let publishDiags as PublishDiagnosticsNotification :
262
261
await self . publishDiagnostics ( Notification ( publishDiags, clientID: clientID) )
263
- } else if clientID == ObjectIdentifier ( self . clangd) {
264
- self . client. send ( params)
262
+ default :
263
+ // We don't know how to handle any other notifications and ignore them.
264
+ log ( " Ignoring unknown notification \( type ( of: params) ) " , level: . warning)
265
+ break
265
266
}
266
267
}
267
268
@@ -274,13 +275,19 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
274
275
id: RequestID ,
275
276
from clientID: ObjectIdentifier ,
276
277
reply: @escaping ( LSPResult < R . Response > ) -> Void
277
- ) {
278
+ ) async {
278
279
let request = Request ( params, id: id, clientID: clientID, cancellation: CancellationToken ( ) , reply: { result in
279
280
reply ( result)
280
281
} )
282
+ guard let sourceKitServer else {
283
+ // `SourceKitServer` has been destructed. We are tearing down the language
284
+ // server. Nothing left to do.
285
+ request. reply ( . failure( . unknown( " Connection to the editor closed " ) ) )
286
+ return
287
+ }
281
288
282
289
if request. clientID == ObjectIdentifier ( self . clangd) {
283
- self . forwardRequest ( request, to : self . client )
290
+ await sourceKitServer . sendRequestToClient ( request. params , reply : request . reply )
284
291
} else {
285
292
request. reply ( . failure( ResponseError . methodNotFound ( R . method) ) )
286
293
}
@@ -354,12 +361,21 @@ extension ClangLanguageServerShim {
354
361
// non-fallback settings very shortly after, which will override the
355
362
// incorrect result, making it very temporary.
356
363
let buildSettings = await self . buildSettings ( for: params. uri)
364
+ guard let sourceKitServer else {
365
+ log ( " Cannot publish diagnostics because SourceKitServer has been destroyed " , level: . error)
366
+ return
367
+ }
357
368
if buildSettings? . isFallback ?? true {
358
369
// Fallback: send empty publish notification instead.
359
- client. send ( PublishDiagnosticsNotification (
360
- uri: params. uri, version: params. version, diagnostics: [ ] ) )
370
+ await sourceKitServer. sendNotificationToClient (
371
+ PublishDiagnosticsNotification (
372
+ uri: params. uri,
373
+ version: params. version,
374
+ diagnostics: [ ]
375
+ )
376
+ )
361
377
} else {
362
- client . send ( note. params)
378
+ await sourceKitServer . sendNotificationToClient ( note. params)
363
379
}
364
380
}
365
381
@@ -388,9 +404,6 @@ extension ClangLanguageServerShim {
388
404
guard let self else { return }
389
405
Task {
390
406
await self . clangd. send ( ExitNotification ( ) )
391
- if let localConnection = self . client as? LocalConnection {
392
- localConnection. close ( )
393
- }
394
407
continuation. resume ( )
395
408
}
396
409
}
0 commit comments