@@ -357,6 +357,22 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
357
357
}
358
358
}
359
359
360
+ /// Debounces calls to `delegate.fileBuildSettingsChanged`.
361
+ ///
362
+ /// This helps in the following situation: A build system takes 5s to return build settings for a file and we have 10
363
+ /// requests for those build settings coming in that time period. Once we get build settings, we get 10 calls to
364
+ /// `resultReceivedAfterTimeout` in `buildSettings(for:in:language:fallbackAfterTimeout:)`, all for the same document.
365
+ /// But calling `fileBuildSettingsChanged` once is totally sufficient.
366
+ ///
367
+ /// Force-unwrapped optional because initializing it requires access to `self`.
368
+ private var filesBuildSettingsChangedDebouncer : Debouncer < Set < DocumentURI > > ! = nil {
369
+ didSet {
370
+ // Must only be set once
371
+ precondition ( oldValue == nil )
372
+ precondition ( filesBuildSettingsChangedDebouncer != nil )
373
+ }
374
+ }
375
+
360
376
private var cachedAdjustedSourceKitOptions = RequestCache < TextDocumentSourceKitOptionsRequest > ( )
361
377
362
378
private var cachedBuildTargets = Cache < WorkspaceBuildTargetsRequest , [ BuildTargetIdentifier : BuildTargetInfo ] > ( )
@@ -421,6 +437,24 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
421
437
}
422
438
}
423
439
440
+ // We don't need a large debounce duration here. It just needs to be big enough to accumulate
441
+ // `resultReceivedAfterTimeout` calls for the same document (see comment on `filesBuildSettingsChangedDebouncer`).
442
+ // Since they should all come in at the same time, a couple of milliseconds should be sufficient here, an 20ms be
443
+ // plenty while still not causing a noticeable delay to the user.
444
+ self . filesBuildSettingsChangedDebouncer = Debouncer (
445
+ debounceDuration: . milliseconds( 20 ) ,
446
+ combineResults: { $0. union ( $1) }
447
+ ) {
448
+ [ weak self] ( filesWithChangedBuildSettings) in
449
+ guard let self, let delegate = await self . delegate else {
450
+ logger. fault ( " Not calling fileBuildSettingsChanged because no delegate exists in SwiftPMBuildSystem " )
451
+ return
452
+ }
453
+ if !filesWithChangedBuildSettings. isEmpty {
454
+ await delegate. fileBuildSettingsChanged ( filesWithChangedBuildSettings)
455
+ }
456
+ }
457
+
424
458
// TODO: Forward file watch patterns from this initialize request to the client
425
459
// (https://github.com/swiftlang/sourcekit-lsp/issues/1671)
426
460
initializeResult = Task { ( ) -> InitializeBuildResponse ? in
@@ -579,7 +613,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
579
613
self . cachedSourceFilesAndDirectories. clearAll ( isolation: self )
580
614
581
615
await delegate? . buildTargetsChanged ( notification. changes)
582
- await delegate ? . fileBuildSettingsChanged ( Set ( watchedFiles. keys) )
616
+ await filesBuildSettingsChangedDebouncer . scheduleCall ( Set ( watchedFiles. keys) )
583
617
}
584
618
585
619
private func logMessage( notification: BuildServerProtocol . OnBuildLogMessageNotification ) async {
@@ -837,7 +871,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
837
871
try await withTimeout ( options. buildSettingsTimeoutOrDefault) {
838
872
return try await self . buildSettingsFromBuildSystem ( for: document, in: target, language: language)
839
873
} resultReceivedAfterTimeout: {
840
- await self . delegate ? . fileBuildSettingsChanged ( [ document] )
874
+ await self . filesBuildSettingsChangedDebouncer . scheduleCall ( [ document] )
841
875
}
842
876
} else {
843
877
try await self . buildSettingsFromBuildSystem ( for: document, in: target, language: language)
@@ -895,7 +929,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
895
929
try await withTimeout ( options. buildSettingsTimeoutOrDefault) {
896
930
await self . canonicalTarget ( for: mainFile)
897
931
} resultReceivedAfterTimeout: {
898
- await self . delegate ? . fileBuildSettingsChanged ( [ document] )
932
+ await self . filesBuildSettingsChangedDebouncer . scheduleCall ( [ document] )
899
933
}
900
934
}
901
935
var languageForFile : Language
@@ -955,6 +989,10 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
955
989
}
956
990
// Handle any messages the build system might have sent us while updating.
957
991
await messageHandlingQueue. async ( metadata: . stateChange) { } . valuePropagatingCancellation
992
+
993
+ // Ensure that we send out all delegate calls so that everybody is informed about the changes.
994
+ await filesBuildSettingsChangedDebouncer. flush ( )
995
+ await filesDependenciesUpdatedDebouncer. flush ( )
958
996
}
959
997
960
998
/// The root targets of the project have depth of 0 and all target dependencies have a greater depth than the target
0 commit comments