-
Notifications
You must be signed in to change notification settings - Fork 136
Expand file tree
/
Copy pathSuggestionViewModel.swift
More file actions
112 lines (95 loc) · 3.24 KB
/
SuggestionViewModel.swift
File metadata and controls
112 lines (95 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//
// SuggestionViewModel.swift
// CodeEditSourceEditor
//
// Created by Khan Winter on 7/22/25.
//
import AppKit
final class SuggestionViewModel: ObservableObject {
/// The items to be displayed in the window
@Published var items: [CodeSuggestionEntry] = []
var itemsRequestTask: Task<Void, Never>?
weak var activeTextView: TextViewController?
var delegate: CodeSuggestionDelegate? {
activeTextView?.completionDelegate
}
func showCompletions(
textView: TextViewController,
delegate: CodeSuggestionDelegate,
cursorPosition: CursorPosition,
showWindowOnParent: @escaping @MainActor (NSWindow, NSRect) -> Void
) {
self.activeTextView = nil
itemsRequestTask?.cancel()
guard let targetParentWindow = textView.view.window else { return }
self.activeTextView = textView
itemsRequestTask = Task {
defer { itemsRequestTask = nil }
do {
guard let completionItems = await delegate.completionSuggestionsRequested(
textView: textView,
cursorPosition: cursorPosition
) else {
return
}
try Task.checkCancellation()
try await MainActor.run {
try Task.checkCancellation()
guard let cursorPosition = textView.resolveCursorPosition(completionItems.windowPosition),
let cursorRect = textView.textView.layoutManager.rectForOffset(
cursorPosition.range.location
),
let cursorRect = textView.view.window?.convertToScreen(
textView.textView.convert(cursorRect, to: nil)
) else {
return
}
self.items = completionItems.items
showWindowOnParent(targetParentWindow, cursorRect)
}
} catch {
return
}
}
}
func cursorsUpdated(
textView: TextViewController,
delegate: CodeSuggestionDelegate,
position: CursorPosition,
close: () -> Void
) {
guard itemsRequestTask == nil else { return }
if activeTextView !== textView {
close()
return
}
guard let newItems = delegate.completionOnCursorMove(
textView: textView,
cursorPosition: position
),
!newItems.isEmpty else {
close()
return
}
items = newItems
}
func didSelect(item: CodeSuggestionEntry) {
delegate?.completionWindowDidSelect(item: item)
}
func applySelectedItem(item: CodeSuggestionEntry, window: NSWindow?) {
guard let activeTextView,
let cursorPosition = activeTextView.cursorPositions.first else {
return
}
self.delegate?.completionWindowApplyCompletion(
item: item,
textView: activeTextView,
cursorPosition: cursorPosition
)
window?.close()
}
func willClose() {
items.removeAll()
activeTextView = nil
}
}