@@ -50,8 +50,31 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
50
50
/// to the editor.
51
51
public let client : Connection
52
52
53
- /// The connection to the clangd LSP. `nil` until `startClangdProcesss` has been called.
54
- var clangd : Connection !
53
+ /// The connection to the clangd LSP.
54
+ ///
55
+ /// `nil` until `startClangdProcesss` has been called.
56
+ ///
57
+ /// - Important: Access `clangd` to ensure all documents have been reopened
58
+ /// after clangd crashes.
59
+ var unsafeClangd : Connection !
60
+
61
+ /// The connection to `clangd`.
62
+ ///
63
+ /// After `clangd` crashes, this property doesn't return until `clangd` has
64
+ /// been relaunched, documents have been reopened in `clangd` and `clangd`
65
+ /// is ready to receive requests again.
66
+ var clangd : Connection {
67
+ get async {
68
+ // If clangd has crashed, wait for it to restart before sending any
69
+ // messages to it.
70
+ _ = await clangdRestartTask? . value
71
+ return unsafeClangd
72
+ }
73
+ }
74
+
75
+ /// If `clangd` is restared, the task to relaunch it and reopen all the
76
+ /// documents within.
77
+ var clangdRestartTask : Task < Void , Never > ?
55
78
56
79
/// Capabilities of the clangd LSP, if received.
57
80
var capabilities : ServerCapabilities ? = nil
@@ -179,7 +202,7 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
179
202
inFD: clangdToUs. fileHandleForReading,
180
203
outFD: usToClangd. fileHandleForWriting
181
204
)
182
- self . clangd = connectionToClangd
205
+ self . unsafeClangd = connectionToClangd
183
206
184
207
connectionToClangd. start ( receiveHandler: self ) {
185
208
// FIXME: keep the pipes alive until we close the connection. This
@@ -235,8 +258,8 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
235
258
}
236
259
self . lastClangdRestart = Date ( )
237
260
238
- Task {
239
- try await Task . sleep ( nanoseconds: UInt64 ( restartDelay) * 1_000_000_000 )
261
+ clangdRestartTask = Task {
262
+ try ? await Task . sleep ( nanoseconds: UInt64 ( restartDelay) * 1_000_000_000 )
240
263
self . clangRestartScheduled = false
241
264
do {
242
265
try self . startClangdProcesss ( )
@@ -250,7 +273,8 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
250
273
} catch {
251
274
log ( " Failed to restart clangd after a crash. " , level: . error)
252
275
}
253
- }
276
+ clangdRestartTask = nil
277
+ }
254
278
}
255
279
256
280
/// Handler for notifications received **from** clangd, ie. **clangd** is
@@ -260,7 +284,7 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
260
284
func handle( _ params: some NotificationType , from clientID: ObjectIdentifier ) async {
261
285
if let publishDiags = params as? PublishDiagnosticsNotification {
262
286
await self . publishDiagnostics ( Notification ( publishDiags, clientID: clientID) )
263
- } else if clientID == ObjectIdentifier ( self . clangd ) {
287
+ } else if clientID == ObjectIdentifier ( self . unsafeClangd ) {
264
288
self . client. send ( params)
265
289
}
266
290
}
@@ -279,7 +303,7 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
279
303
reply ( result)
280
304
} )
281
305
282
- if request. clientID == ObjectIdentifier ( self . clangd ) {
306
+ if request. clientID == ObjectIdentifier ( self . unsafeClangd ) {
283
307
self . forwardRequest ( request, to: self . client)
284
308
} else {
285
309
request. reply ( . failure( ResponseError . methodNotFound ( R . method) ) )
@@ -373,16 +397,23 @@ extension ClangLanguageServerShim {
373
397
// Store the initialize request so we can replay it in case clangd crashes
374
398
self . initializeRequest = initialize
375
399
376
- let result = try clangd. sendSync ( initialize)
400
+ // This must not access `clangd` because it is called from the
401
+ // `clangdRestartTask` and accessing `clangd` is blocked until that task
402
+ // completes.
403
+ let result = try unsafeClangd. sendSync ( initialize)
377
404
self . capabilities = result. capabilities
378
405
return result
379
406
}
380
407
381
408
public func clientInitialized( _ initialized: InitializedNotification ) {
382
- clangd. send ( initialized)
409
+ // This must not access `clangd` because it is called from the
410
+ // `clangdRestartTask` and accessing `clangd` is blocked until that task
411
+ // completes.
412
+ unsafeClangd. send ( initialized)
383
413
}
384
414
385
415
public func shutdown( ) async {
416
+ let clangd = await clangd
386
417
await withCheckedContinuation { continuation in
387
418
_ = clangd. send ( ShutdownRequest ( ) , queue: self . clangdCommunicationQueue) { [ weak self] _ in
388
419
guard let self else { return }
@@ -405,24 +436,24 @@ extension ClangLanguageServerShim {
405
436
// sending the open notification, so that the initial diagnostics already
406
437
// have build settings.
407
438
await documentUpdatedBuildSettings ( note. textDocument. uri, change: . removedOrUnavailable)
408
- clangd. send ( note)
439
+ await clangd. send ( note)
409
440
}
410
441
411
- public func closeDocument( _ note: DidCloseTextDocumentNotification ) {
442
+ public func closeDocument( _ note: DidCloseTextDocumentNotification ) async {
412
443
openDocuments [ note. textDocument. uri] = nil
413
- clangd. send ( note)
444
+ await clangd. send ( note)
414
445
}
415
446
416
- public func changeDocument( _ note: DidChangeTextDocumentNotification ) {
417
- clangd. send ( note)
447
+ public func changeDocument( _ note: DidChangeTextDocumentNotification ) async {
448
+ await clangd. send ( note)
418
449
}
419
450
420
451
public func willSaveDocument( _ note: WillSaveTextDocumentNotification ) {
421
452
422
453
}
423
454
424
- public func didSaveDocument( _ note: DidSaveTextDocumentNotification ) {
425
- clangd. send ( note)
455
+ public func didSaveDocument( _ note: DidSaveTextDocumentNotification ) async {
456
+ await clangd. send ( note)
426
457
}
427
458
428
459
// MARK: - Build System Integration
@@ -448,101 +479,101 @@ extension ClangLanguageServerShim {
448
479
let note = DidChangeConfigurationNotification ( settings: . clangd(
449
480
ClangWorkspaceSettings (
450
481
compilationDatabaseChanges: [ pathString: compileCommand] ) ) )
451
- clangd. send ( note)
482
+ await clangd. send ( note)
452
483
}
453
484
}
454
485
455
- public func documentDependenciesUpdated( _ uri: DocumentURI ) {
486
+ public func documentDependenciesUpdated( _ uri: DocumentURI ) async {
456
487
// In order to tell clangd to reload an AST, we send it an empty `didChangeTextDocument`
457
488
// with `forceRebuild` set in case any missing header files have been added.
458
489
// This works well for us as the moment since clangd ignores the document version.
459
490
let note = DidChangeTextDocumentNotification (
460
491
textDocument: VersionedTextDocumentIdentifier ( uri, version: 0 ) ,
461
492
contentChanges: [ ] ,
462
493
forceRebuild: true )
463
- clangd. send ( note)
494
+ await clangd. send ( note)
464
495
}
465
496
466
497
// MARK: - Text Document
467
498
468
499
469
500
/// Returns true if the `ToolchainLanguageServer` will take ownership of the request.
470
- public func definition( _ req: Request < DefinitionRequest > ) -> Bool {
501
+ public func definition( _ req: Request < DefinitionRequest > ) async -> Bool {
471
502
// We handle it to provide jump-to-header support for #import/#include.
472
- self . forwardRequest ( req, to: self . clangd)
503
+ self . forwardRequest ( req, to: await self . clangd)
473
504
return true
474
505
}
475
506
476
507
/// Returns true if the `ToolchainLanguageServer` will take ownership of the request.
477
- public func declaration( _ req: Request < DeclarationRequest > ) -> Bool {
508
+ public func declaration( _ req: Request < DeclarationRequest > ) async -> Bool {
478
509
// We handle it to provide jump-to-header support for #import/#include.
479
- forwardRequest ( req, to: clangd)
510
+ forwardRequest ( req, to: await clangd)
480
511
return true
481
512
}
482
513
483
- func completion( _ req: Request < CompletionRequest > ) {
484
- forwardRequest ( req, to: clangd)
514
+ func completion( _ req: Request < CompletionRequest > ) async {
515
+ forwardRequest ( req, to: await clangd)
485
516
}
486
517
487
- func hover( _ req: Request < HoverRequest > ) {
488
- forwardRequest ( req, to: clangd)
518
+ func hover( _ req: Request < HoverRequest > ) async {
519
+ forwardRequest ( req, to: await clangd)
489
520
}
490
521
491
- func symbolInfo( _ req: Request < SymbolInfoRequest > ) {
492
- forwardRequest ( req, to: clangd)
522
+ func symbolInfo( _ req: Request < SymbolInfoRequest > ) async {
523
+ forwardRequest ( req, to: await clangd)
493
524
}
494
525
495
- func documentSymbolHighlight( _ req: Request < DocumentHighlightRequest > ) {
496
- forwardRequest ( req, to: clangd)
526
+ func documentSymbolHighlight( _ req: Request < DocumentHighlightRequest > ) async {
527
+ forwardRequest ( req, to: await clangd)
497
528
}
498
529
499
- func documentSymbol( _ req: Request < DocumentSymbolRequest > ) {
500
- forwardRequest ( req, to: clangd)
530
+ func documentSymbol( _ req: Request < DocumentSymbolRequest > ) async {
531
+ forwardRequest ( req, to: await clangd)
501
532
}
502
533
503
- func documentColor( _ req: Request < DocumentColorRequest > ) {
534
+ func documentColor( _ req: Request < DocumentColorRequest > ) async {
504
535
if self . capabilities? . colorProvider? . isSupported == true {
505
- forwardRequest ( req, to: clangd)
536
+ forwardRequest ( req, to: await clangd)
506
537
} else {
507
538
req. reply ( . success( [ ] ) )
508
539
}
509
540
}
510
541
511
- func documentSemanticTokens( _ req: Request < DocumentSemanticTokensRequest > ) {
512
- forwardRequest ( req, to: clangd)
542
+ func documentSemanticTokens( _ req: Request < DocumentSemanticTokensRequest > ) async {
543
+ forwardRequest ( req, to: await clangd)
513
544
}
514
545
515
- func documentSemanticTokensDelta( _ req: Request < DocumentSemanticTokensDeltaRequest > ) {
516
- forwardRequest ( req, to: clangd)
546
+ func documentSemanticTokensDelta( _ req: Request < DocumentSemanticTokensDeltaRequest > ) async {
547
+ forwardRequest ( req, to: await clangd)
517
548
}
518
549
519
- func documentSemanticTokensRange( _ req: Request < DocumentSemanticTokensRangeRequest > ) {
520
- forwardRequest ( req, to: clangd)
550
+ func documentSemanticTokensRange( _ req: Request < DocumentSemanticTokensRangeRequest > ) async {
551
+ forwardRequest ( req, to: await clangd)
521
552
}
522
553
523
- func colorPresentation( _ req: Request < ColorPresentationRequest > ) {
554
+ func colorPresentation( _ req: Request < ColorPresentationRequest > ) async {
524
555
if self . capabilities? . colorProvider? . isSupported == true {
525
- forwardRequest ( req, to: clangd)
556
+ forwardRequest ( req, to: await clangd)
526
557
} else {
527
558
req. reply ( . success( [ ] ) )
528
559
}
529
560
}
530
561
531
- func codeAction( _ req: Request < CodeActionRequest > ) {
532
- forwardRequest ( req, to: clangd)
562
+ func codeAction( _ req: Request < CodeActionRequest > ) async {
563
+ forwardRequest ( req, to: await clangd)
533
564
}
534
565
535
- func inlayHint( _ req: Request < InlayHintRequest > ) {
536
- forwardRequest ( req, to: clangd)
566
+ func inlayHint( _ req: Request < InlayHintRequest > ) async {
567
+ forwardRequest ( req, to: await clangd)
537
568
}
538
569
539
- func documentDiagnostic( _ req: Request < DocumentDiagnosticsRequest > ) {
540
- forwardRequest ( req, to: clangd)
570
+ func documentDiagnostic( _ req: Request < DocumentDiagnosticsRequest > ) async {
571
+ forwardRequest ( req, to: await clangd)
541
572
}
542
573
543
- func foldingRange( _ req: Request < FoldingRangeRequest > ) {
574
+ func foldingRange( _ req: Request < FoldingRangeRequest > ) async {
544
575
if self . capabilities? . foldingRangeProvider? . isSupported == true {
545
- forwardRequest ( req, to: clangd)
576
+ forwardRequest ( req, to: await clangd)
546
577
} else {
547
578
req. reply ( . success( nil ) )
548
579
}
@@ -554,8 +585,8 @@ extension ClangLanguageServerShim {
554
585
555
586
// MARK: - Other
556
587
557
- func executeCommand( _ req: Request < ExecuteCommandRequest > ) {
558
- forwardRequest ( req, to: clangd)
588
+ func executeCommand( _ req: Request < ExecuteCommandRequest > ) async {
589
+ forwardRequest ( req, to: await clangd)
559
590
}
560
591
}
561
592
0 commit comments