Skip to content

Commit 42cae22

Browse files
committed
Debounce prompt to code
1 parent e2f776f commit 42cae22

File tree

4 files changed

+64
-10
lines changed

4 files changed

+64
-10
lines changed

Core/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ let package = Package(
275275
.product(name: "AppMonitoring", package: "Tool"),
276276
.product(name: "ChatTab", package: "Tool"),
277277
.product(name: "Logger", package: "Tool"),
278+
.product(name: "CustomAsyncAlgorithms", package: "Tool"),
278279
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
279280
.product(name: "MarkdownUI", package: "swift-markdown-ui"),
280281
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),

Core/Sources/SuggestionWidget/FeatureReducers/PromptToCode.swift

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import AppKit
22
import ComposableArchitecture
3+
import AsyncAlgorithms
34
import Dependencies
45
import Foundation
56
import PromptToCodeService
@@ -43,7 +44,7 @@ public struct PromptToCode: ReducerProtocol {
4344
}
4445
}
4546
}
46-
47+
4748
public enum FocusField: Equatable {
4849
case textField
4950
}
@@ -113,7 +114,7 @@ public struct PromptToCode: ReducerProtocol {
113114
self.generateDescriptionRequirement = generateDescriptionRequirement
114115
self.isAttachedToSelectionRange = isAttachedToSelectionRange
115116
self.commandName = commandName
116-
117+
117118
if selectionRange?.isEmpty ?? true {
118119
self.isAttachedToSelectionRange = false
119120
}
@@ -151,7 +152,7 @@ public struct PromptToCode: ReducerProtocol {
151152
switch action {
152153
case .binding:
153154
return .none
154-
155+
155156
case .focusOnTextField:
156157
state.focusedField = .textField
157158
return .none
@@ -187,13 +188,23 @@ public struct PromptToCode: ReducerProtocol {
187188
generateDescriptionRequirement: copiedState
188189
.generateDescriptionRequirement
189190
)
190-
#warning("TODO: make the action call debounced.")
191-
for try await fragment in stream {
192-
try Task.checkCancellation()
193-
await send(.modifyCodeChunkReceived(
194-
code: fragment.code,
195-
description: fragment.description
196-
))
191+
192+
if #available(macOS 13.0, *) {
193+
for try await fragment in stream.debounce(for: .milliseconds(400)) {
194+
try Task.checkCancellation()
195+
await send(.modifyCodeChunkReceived(
196+
code: fragment.code,
197+
description: fragment.description
198+
))
199+
}
200+
} else {
201+
for try await fragment in stream {
202+
try Task.checkCancellation()
203+
await send(.modifyCodeChunkReceived(
204+
code: fragment.code,
205+
description: fragment.description
206+
))
207+
}
197208
}
198209
try Task.checkCancellation()
199210
await send(.modifyCodeFinished)

Tool/Package.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ let package = Package(
4444
.library(name: "GitIgnoreCheck", targets: ["GitIgnoreCheck"]),
4545
.library(name: "DebounceFunction", targets: ["DebounceFunction"]),
4646
.library(name: "AsyncPassthroughSubject", targets: ["AsyncPassthroughSubject"]),
47+
.library(name: "CustomAsyncAlgorithms", targets: ["CustomAsyncAlgorithms"]),
4748
],
4849
dependencies: [
4950
// A fork of https://github.com/aespinilla/Tiktoken to allow loading from local files.
@@ -85,6 +86,13 @@ let package = Package(
8586

8687
.target(name: "ObjectiveCExceptionHandling"),
8788

89+
.target(
90+
name: "CustomAsyncAlgorithms",
91+
dependencies: [
92+
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms")
93+
]
94+
),
95+
8896
.target(
8997
name: "Keychain",
9098
dependencies: ["Configs", "Preferences"]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Foundation
2+
3+
/// Debounce, at the same time accumulate the values
4+
public extension AsyncSequence {
5+
func accumulatedDebounce<R>(
6+
duration: TimeInterval,
7+
initialValue: @escaping @autoclosure () -> R,
8+
accumulate: @escaping (R, Element) -> R
9+
) -> AsyncThrowingStream<R, Error> {
10+
AsyncThrowingStream { continuation in
11+
Task {
12+
var lastFireTime = Date()
13+
var value = initialValue()
14+
do {
15+
for try await item in self {
16+
value = accumulate(value, item)
17+
let now = Date()
18+
if now.timeIntervalSince(lastFireTime) > duration {
19+
lastFireTime = now
20+
continuation.yield(value)
21+
value = initialValue()
22+
print(value)
23+
}
24+
}
25+
continuation.yield(value)
26+
continuation.finish()
27+
} catch {
28+
continuation.finish(throwing: error)
29+
}
30+
}
31+
}
32+
}
33+
}
34+

0 commit comments

Comments
 (0)