@@ -78,19 +78,19 @@ private struct BuildTargetInfo {
78
78
79
79
fileprivate extension SourceItem {
80
80
var sourceKitData : SourceKitSourceItemData ? {
81
- guard dataKind == . sourceKit, case . dictionary ( let data ) = data else {
81
+ guard dataKind == . sourceKit else {
82
82
return nil
83
83
}
84
- return SourceKitSourceItemData ( fromLSPDictionary : data)
84
+ return SourceKitSourceItemData ( fromLSPAny : data)
85
85
}
86
86
}
87
87
88
88
fileprivate extension BuildTarget {
89
89
var sourceKitData : SourceKitBuildTarget ? {
90
- guard dataKind == . sourceKit, case . dictionary ( let data ) = data else {
90
+ guard dataKind == . sourceKit else {
91
91
return nil
92
92
}
93
- return SourceKitBuildTarget ( fromLSPDictionary : data)
93
+ return SourceKitBuildTarget ( fromLSPAny : data)
94
94
}
95
95
}
96
96
@@ -99,10 +99,7 @@ fileprivate extension InitializeBuildResponse {
99
99
guard dataKind == nil || dataKind == . sourceKit else {
100
100
return nil
101
101
}
102
- guard case . dictionary( let data) = data else {
103
- return nil
104
- }
105
- return SourceKitInitializeBuildResponseData ( fromLSPDictionary: data)
102
+ return SourceKitInitializeBuildResponseData ( fromLSPAny: data)
106
103
}
107
104
}
108
105
@@ -250,7 +247,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
250
247
/// get `fileBuildSettingsChanged` and `filesDependenciesUpdated` callbacks.
251
248
private var watchedFiles : [ DocumentURI : ( mainFile: DocumentURI , language: Language ) ] = [ : ]
252
249
253
- private var connectionToClient : BuildSystemManagerConnectionToClient ?
250
+ private var connectionToClient : BuildSystemManagerConnectionToClient
254
251
255
252
/// The build system adapter that is used to answer build system queries.
256
253
private var buildSystemAdapter : BuildSystemAdapter ?
@@ -302,6 +299,10 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
302
299
}
303
300
}
304
301
302
+ /// For tasks from the build system that should create a work done progress in the client, a mapping from the `TaskId`
303
+ /// in the build system to a `WorkDoneProgressManager` that manages that work done progress in the client.
304
+ private var workDoneProgressManagers : [ TaskIdentifier : WorkDoneProgressManager ] = [ : ]
305
+
305
306
/// Debounces calls to `delegate.filesDependenciesUpdated`.
306
307
///
307
308
/// This is to ensure we don't call `filesDependenciesUpdated` for the same file multiple time if the client does not
@@ -443,6 +444,8 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
443
444
/// which could result in the connection being reported as a leak. To avoid this problem, we want to explicitly shut
444
445
/// down the build server when the `SourceKitLSPServer` gets shut down.
445
446
package func shutdown( ) async {
447
+ // Clear any pending work done progresses from the build server.
448
+ self . workDoneProgressManagers. removeAll ( )
446
449
guard let buildSystemAdapter = await self . buildSystemAdapterAfterInitialized else {
447
450
return
448
451
}
@@ -488,8 +491,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
488
491
await self . didChangeBuildTarget ( notification: notification)
489
492
case let notification as OnBuildLogMessageNotification :
490
493
await self . logMessage ( notification: notification)
491
- case let notification as BuildServerProtocol . WorkDoneProgress :
492
- await self . workDoneProgress ( notification: notification)
494
+ case let notification as TaskFinishNotification :
495
+ await self . taskFinish ( notification: notification)
496
+ case let notification as TaskProgressNotification :
497
+ await self . taskProgress ( notification: notification)
498
+ case let notification as TaskStartNotification :
499
+ await self . taskStart ( notification: notification)
493
500
default :
494
501
logger. error ( " Ignoring unknown notification \( type ( of: notification) . method) " )
495
502
}
@@ -502,8 +509,6 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
502
509
) async {
503
510
let request = RequestAndReply ( request, reply: reply)
504
511
switch request {
505
- case let request as RequestAndReply < BuildServerProtocol . CreateWorkDoneProgressRequest > :
506
- await request. reply { try await self . createWorkDoneProgress ( request: request. params) }
507
512
default :
508
513
await request. reply { throw ResponseError . methodNotFound ( Request . method) }
509
514
}
@@ -544,29 +549,51 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
544
549
} else {
545
550
notification. message
546
551
}
547
- await connectionToClient? . send (
552
+ await connectionToClient. waitUntilInitialized ( )
553
+ connectionToClient. send (
548
554
LanguageServerProtocol . LogMessageNotification ( type: . info, message: message, logName: " SourceKit-LSP: Indexing " )
549
555
)
550
556
}
551
557
552
- private func workDoneProgress( notification: BuildServerProtocol . WorkDoneProgress ) async {
553
- guard let connectionToClient else {
554
- logger. fault ( " Ignoring work done progress from build system because connection to client closed " )
558
+ private func taskStart( notification: TaskStartNotification ) async {
559
+ guard let workDoneProgressTitle = WorkDoneProgressTask ( fromLSPAny: notification. data) ? . title,
560
+ await connectionToClient. clientSupportsWorkDoneProgress
561
+ else {
562
+ return
563
+ }
564
+
565
+ guard workDoneProgressManagers [ notification. taskId. id] == nil else {
566
+ logger. error ( " Client is already tracking a work done progress for task \( notification. taskId. id) " )
555
567
return
556
568
}
557
- await connectionToClient. send ( notification as LanguageServerProtocol . WorkDoneProgress )
569
+ workDoneProgressManagers [ notification. taskId. id] = WorkDoneProgressManager (
570
+ connectionToClient: connectionToClient,
571
+ waitUntilClientInitialized: connectionToClient. waitUntilInitialized,
572
+ tokenPrefix: notification. taskId. id,
573
+ initialDebounce: options. workDoneProgressDebounceDurationOrDefault,
574
+ title: workDoneProgressTitle
575
+ )
558
576
}
559
577
560
- private func createWorkDoneProgress(
561
- request: BuildServerProtocol . CreateWorkDoneProgressRequest
562
- ) async throws -> BuildServerProtocol . CreateWorkDoneProgressRequest . Response {
563
- guard let connectionToClient else {
564
- throw ResponseError . unknown ( " Connection to client closed " )
578
+ private func taskProgress( notification: TaskProgressNotification ) async {
579
+ guard let progressManager = workDoneProgressManagers [ notification. taskId. id] else {
580
+ return
565
581
}
566
- guard await connectionToClient. clientSupportsWorkDoneProgress else {
567
- throw ResponseError . unknown ( " Client does not support work done progress " )
582
+ let percentage : Int ? =
583
+ if let progress = notification. progress, let total = notification. total {
584
+ Int ( ( Double ( progress) / Double( total) * 100 ) . rounded ( ) )
585
+ } else {
586
+ nil
587
+ }
588
+ await progressManager. update ( message: notification. message, percentage: percentage)
589
+ }
590
+
591
+ private func taskFinish( notification: TaskFinishNotification ) async {
592
+ guard let progressManager = workDoneProgressManagers [ notification. taskId. id] else {
593
+ return
568
594
}
569
- return try await connectionToClient. send ( request as LanguageServerProtocol . CreateWorkDoneProgressRequest )
595
+ await progressManager. end ( )
596
+ workDoneProgressManagers [ notification. taskId. id] = nil
570
597
}
571
598
572
599
// MARK: Build System queries
0 commit comments