Skip to content

Commit 60ce3b2

Browse files
committed
Fix an issue in Debounce that added up debounce durations
If calls from `Debouncer` were scheduled every 0.5s but the debounce duration was 1s, we would always schedule the pending task and schedule a new one, never actually committing one. Debounce at most the debounce duration from the initially scheduled call.
1 parent f5fee4e commit 60ce3b2

File tree

2 files changed

+11
-9
lines changed

2 files changed

+11
-9
lines changed

Sources/SKCore/Debouncer.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ public actor Debouncer<Parameter> {
3333
private let makeCall: (Parameter) async -> Void
3434

3535
/// In the time between the call to `scheduleCall` and the call actually being committed (ie. in the time that the
36-
/// call can be debounced), the task that would commit the call (unless cancelled) and the parameter with which this
37-
/// call should be made.
38-
private var inProgressData: (Parameter, Task<Void, Never>)?
36+
/// call can be debounced), the task that would commit the call (unless cancelled), the parameter with which this
37+
/// call should be made and the time at which the call should be made. Keeping track of the time ensures that we don't
38+
/// indefinitely debounce if a new `scheduleCall` is made every 0.4s but we debounce for 0.5s.
39+
private var inProgressData: (Parameter, ContinuousClock.Instant, Task<Void, Never>)?
3940

4041
public init(
4142
debounceDuration: Duration,
4243
combineResults: @escaping (Parameter, Parameter) -> Parameter,
43-
_ makeCall: @escaping (Parameter) async -> Void
44+
_ makeCall: @Sendable @escaping (Parameter) async -> Void
4445
) {
4546
self.debounceDuration = debounceDuration
4647
self.combineParameters = combineResults
@@ -52,26 +53,28 @@ public actor Debouncer<Parameter> {
5253
/// `debounceDuration` after the second `scheduleCall` call.
5354
public func scheduleCall(_ parameter: Parameter) {
5455
var parameter = parameter
55-
if let (inProgressParameter, inProgressTask) = inProgressData {
56+
var targetDate = ContinuousClock.now + debounceDuration
57+
if let (inProgressParameter, inProgressTargetDate, inProgressTask) = inProgressData {
5658
inProgressTask.cancel()
5759
parameter = combineParameters(inProgressParameter, parameter)
60+
targetDate = inProgressTargetDate
5861
}
5962
let task = Task {
6063
do {
61-
try await Task.sleep(for: debounceDuration)
64+
try await Task.sleep(until: targetDate)
6265
try Task.checkCancellation()
6366
} catch {
6467
return
6568
}
6669
inProgressData = nil
6770
await makeCall(parameter)
6871
}
69-
inProgressData = (parameter, task)
72+
inProgressData = (parameter, ContinuousClock.now + debounceDuration, task)
7073
}
7174
}
7275

7376
extension Debouncer<Void> {
74-
public init(debounceDuration: Duration, _ makeCall: @escaping () async -> Void) {
77+
public init(debounceDuration: Duration, _ makeCall: @Sendable @escaping () async -> Void) {
7578
self.init(debounceDuration: debounceDuration, combineResults: { _, _ in }, makeCall)
7679
}
7780

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,6 @@ public final actor SemanticIndexManager {
414414
///
415415
/// If file's target is known to be up-to-date, this returns almost immediately.
416416
public func prepareFileForEditorFunctionality(_ uri: DocumentURI) async {
417-
// Should be kept in sync with `schedulePreparationForEditorFunctionality`.
418417
guard let target = await buildSystemManager.canonicalConfiguredTarget(for: uri) else {
419418
return
420419
}

0 commit comments

Comments
 (0)