Skip to content

Commit a78ed53

Browse files
committed
Add throttler to RealtimeSuggestionController
1 parent 6d65d57 commit a78ed53

2 files changed

Lines changed: 61 additions & 27 deletions

File tree

Core/Sources/Service/RealtimeSuggestionController.swift

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ public actor RealtimeSuggestionController {
3131

3232
private func observeXcodeChange() {
3333
cancellable.forEach { $0.cancel() }
34-
34+
3535
XcodeInspector.shared.$focusedEditor
3636
.sink { [weak self] editor in
3737
guard let self else { return }
3838
Task {
39-
guard let editor else { return }
39+
guard let editor else { return }
4040
await self.handleFocusElementChange(editor)
4141
}
4242
}.store(in: &cancellable)
@@ -51,7 +51,7 @@ public actor RealtimeSuggestionController {
5151
}
5252

5353
self.sourceEditor = sourceEditor
54-
54+
5555
let notificationsFromEditor = sourceEditor.axNotifications
5656

5757
editorObservationTask?.cancel()
@@ -65,25 +65,54 @@ public actor RealtimeSuggestionController {
6565
)
6666
}
6767

68-
for await notification in notificationsFromEditor {
69-
guard let self else { return }
70-
try Task.checkCancellation()
71-
72-
switch notification.kind {
73-
case .valueChanged:
74-
await cancelInFlightTasks()
75-
await self.triggerPrefetchDebounced()
76-
await self.notifyEditingFileChange(editor: sourceEditor.element)
77-
case .selectedTextChanged:
78-
guard let fileURL = XcodeInspector.shared.activeDocumentURL
79-
else { break }
80-
await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded(
81-
fileURL: fileURL,
82-
sourceEditor: sourceEditor
83-
)
84-
default:
85-
break
68+
let valueChange = notificationsFromEditor.filter { $0.kind == .valueChanged }
69+
let selectedTextChanged = notificationsFromEditor
70+
.filter { $0.kind == .selectedTextChanged }
71+
72+
await withTaskGroup(of: Void.self) { [weak self] group in
73+
group.addTask { [weak self] in
74+
let handler = { [weak self] in
75+
guard let self else { return }
76+
await cancelInFlightTasks()
77+
await self.triggerPrefetchDebounced()
78+
await self.notifyEditingFileChange(editor: sourceEditor.element)
79+
}
80+
81+
if #available(macOS 13.0, *) {
82+
for await _ in valueChange.throttle(for: .milliseconds(200)) {
83+
if Task.isCancelled { return }
84+
await handler()
85+
}
86+
} else {
87+
for await _ in valueChange {
88+
if Task.isCancelled { return }
89+
await handler()
90+
}
91+
}
8692
}
93+
group.addTask {
94+
let handler = {
95+
guard let fileURL = XcodeInspector.shared.activeDocumentURL else { return }
96+
await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded(
97+
fileURL: fileURL,
98+
sourceEditor: sourceEditor
99+
)
100+
}
101+
102+
if #available(macOS 13.0, *) {
103+
for await _ in selectedTextChanged.throttle(for: .milliseconds(200)) {
104+
if Task.isCancelled { return }
105+
await handler()
106+
}
107+
} else {
108+
for await _ in selectedTextChanged {
109+
if Task.isCancelled { return }
110+
await handler()
111+
}
112+
}
113+
}
114+
115+
await group.waitForAll()
87116
}
88117
}
89118

@@ -94,7 +123,7 @@ public actor RealtimeSuggestionController {
94123
.fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL)
95124

96125
if filespace.codeMetadata.uti == nil {
97-
Logger.service.info("Generate cache for file.")
126+
Logger.service.info("Generate cache for file.")
98127
// avoid the command get called twice
99128
filespace.codeMetadata.uti = ""
100129
do {
@@ -111,10 +140,11 @@ public actor RealtimeSuggestionController {
111140

112141
func triggerPrefetchDebounced(force: Bool = false) {
113142
inflightPrefetchTask = Task(priority: .utility) { @WorkspaceActor in
114-
try? await Task.sleep(nanoseconds: UInt64((
143+
try? await Task.sleep(nanoseconds: UInt64(
115144
max(UserDefaults.shared.value(for: \.realtimeSuggestionDebounce), 0.15)
116-
) * 1_000_000_000))
117-
145+
* 1_000_000_000
146+
))
147+
118148
if Task.isCancelled { return }
119149

120150
guard UserDefaults.shared.value(for: \.realtimeSuggestionToggle)

Tool/Sources/XcodeInspector/SourceEditor.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ import SuggestionModel
99
public class SourceEditor {
1010
public typealias Content = EditorInformation.SourceEditorContent
1111

12-
public struct AXNotification {
12+
public struct AXNotification: Hashable {
1313
public var kind: AXNotificationKind
1414
public var element: AXUIElement
15+
16+
public func hash(into hasher: inout Hasher) {
17+
kind.hash(into: &hasher)
18+
}
1519
}
1620

17-
public enum AXNotificationKind {
21+
public enum AXNotificationKind: Hashable, Equatable {
1822
case selectedTextChanged
1923
case valueChanged
2024
case scrollPositionChanged

0 commit comments

Comments
 (0)