@@ -114,6 +114,10 @@ final actor WorkDoneProgressState {
114
114
case progressCreationFailed
115
115
}
116
116
117
+ /// A queue so we can have synchronous `startProgress` and `endProgress` functions that don't need to wait for the
118
+ /// work done progress to be started or ended.
119
+ private let queue = AsyncQueue < Serial > ( )
120
+
117
121
/// How many active tasks are running.
118
122
///
119
123
/// A work done progress should be displayed if activeTasks > 0
@@ -134,7 +138,14 @@ final actor WorkDoneProgressState {
134
138
/// Start a new task, creating a new `WorkDoneProgress` if none is running right now.
135
139
///
136
140
/// - Parameter server: The server that is used to create the `WorkDoneProgress` on the client
137
- func startProgress( server: SourceKitLSPServer ) async {
141
+ nonisolated func startProgress( server: SourceKitLSPServer ) {
142
+ queue. async {
143
+ self . startProgress ( server: server)
144
+ }
145
+ }
146
+
147
+ func startProgressImpl( server: SourceKitLSPServer ) async {
148
+ await server. waitUntilInitialized ( )
138
149
activeTasks += 1
139
150
guard await server. capabilityRegistry? . clientCapabilities. window? . workDoneProgress ?? false else {
140
151
// If the client doesn't support workDoneProgress, keep track of the active task count but don't update the state.
@@ -179,7 +190,13 @@ final actor WorkDoneProgressState {
179
190
/// If this drops the active task count to 0, the work done progress is ended on the client.
180
191
///
181
192
/// - Parameter server: The server that is used to send and update of the `WorkDoneProgress` to the client
182
- func endProgress( server: SourceKitLSPServer ) async {
193
+ nonisolated func endProgress( server: SourceKitLSPServer ) {
194
+ queue. async {
195
+ await self . endProgressImpl ( server: server)
196
+ }
197
+ }
198
+
199
+ func endProgressImpl( server: SourceKitLSPServer ) async {
183
200
assert ( activeTasks > 0 , " Unbalanced startProgress/endProgress calls " )
184
201
activeTasks -= 1
185
202
guard await server. capabilityRegistry? . clientCapabilities. window? . workDoneProgress ?? false else {
@@ -439,11 +456,16 @@ public actor SourceKitLSPServer {
439
456
/// The connection to the editor.
440
457
public let client : Connection
441
458
459
+ /// Set to `true` after the `SourceKitLSPServer` has send the reply to the `InitializeRequest`.
460
+ ///
461
+ /// Initialization can be awaited using `waitUntilInitialized`.
462
+ private var initialized : Bool = false
463
+
442
464
var options : Options
443
465
444
466
let toolchainRegistry : ToolchainRegistry
445
467
446
- var capabilityRegistry : CapabilityRegistry ?
468
+ public var capabilityRegistry : CapabilityRegistry ?
447
469
448
470
var languageServices : [ LanguageServerType : [ LanguageService ] ] = [ : ]
449
471
@@ -507,19 +529,15 @@ public actor SourceKitLSPServer {
507
529
self . inProgressRequests [ id] = task
508
530
}
509
531
510
- let fs : FileSystem
511
-
512
532
var onExit : ( ) -> Void
513
533
514
534
/// Creates a language server for the given client.
515
535
public init (
516
536
client: Connection ,
517
- fileSystem: FileSystem = localFileSystem,
518
537
toolchainRegistry: ToolchainRegistry ,
519
538
options: Options ,
520
539
onExit: @escaping ( ) -> Void = { }
521
540
) {
522
- self . fs = fileSystem
523
541
self . toolchainRegistry = toolchainRegistry
524
542
self . options = options
525
543
self . onExit = onExit
@@ -533,6 +551,22 @@ public actor SourceKitLSPServer {
533
551
] )
534
552
}
535
553
554
+ /// Await until the server has send the reply to the initialize request.
555
+ func waitUntilInitialized( ) async {
556
+ // The polling of `initialized` is not perfect but it should be OK, because
557
+ // - In almost all cases the server should already be initialized.
558
+ // - If it's not initialized, we expect initialization to finish fairly quickly. Even if initialization takes 5s
559
+ // this only results in 50 polls, which is acceptable.
560
+ // Alternative solutions that signal via an async sequence seem overkill here.
561
+ while !initialized {
562
+ do {
563
+ try await Task . sleep ( for: . seconds( 0.1 ) )
564
+ } catch {
565
+ break
566
+ }
567
+ }
568
+ }
569
+
536
570
/// Search through all the parent directories of `uri` and check if any of these directories contain a workspace
537
571
/// capable of handling `uri`.
538
572
///
@@ -952,6 +986,8 @@ extension SourceKitLSPServer: MessageHandler {
952
986
switch request {
953
987
case let request as RequestAndReply < InitializeRequest > :
954
988
await request. reply { try await initialize ( request. params) }
989
+ // Only set `initialized` to `true` after we have sent the response to the initialize request to the client.
990
+ initialized = true
955
991
case let request as RequestAndReply < ShutdownRequest > :
956
992
await request. reply { try await shutdown ( request. params) }
957
993
case let request as RequestAndReply < WorkspaceSymbolsRequest > :
0 commit comments