Skip to content

Commit 837f4ab

Browse files
authored
refactor: replace autocomplete NSTableView with native SwiftUI (#375)
* refactor: replace autocomplete NSTableView with native SwiftUI List * fix: address code review issues in autocomplete refactor * fix: remove unrelated D1 entry from changelog * fix: address CodeRabbit review findings
1 parent db3664b commit 837f4ab

18 files changed

Lines changed: 647 additions & 712 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Match highlighting in autocomplete suggestions (matched characters shown in bold)
13+
- Loading spinner in autocomplete popup while fetching column metadata
14+
15+
### Changed
16+
17+
- Refactored autocomplete popup to native SwiftUI (visible selection highlight, native accent color, scroll-to-selection)
18+
- Autocomplete now suppresses noisy empty-prefix suggestions in non-browseable contexts (e.g., after SELECT, WHERE)
19+
- Autocomplete ranking stays consistent as you type (unified fuzzy scoring between initial display and live filtering)
20+
- Increased autocomplete suggestion limit from 20 to 40 for schema-heavy contexts (FROM, SELECT, WHERE)
21+
1022
## [0.20.4] - 2026-03-19
1123

1224
### Fixed

LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/CodeSuggestion/Model/CodeSuggestionEntry.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,11 @@ public protocol CodeSuggestionEntry {
2323
var imageColor: Color { get }
2424

2525
var deprecated: Bool { get }
26+
27+
/// Character index ranges in the label that matched the user's typed prefix.
28+
var matchedRanges: [Range<Int>] { get }
29+
}
30+
31+
public extension CodeSuggestionEntry {
32+
var matchedRanges: [Range<Int>] { [] }
2633
}

LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/CodeSuggestion/Model/SuggestionViewModel.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import AppKit
1111
final class SuggestionViewModel: ObservableObject {
1212
/// The items to be displayed in the window
1313
@Published var items: [CodeSuggestionEntry] = []
14+
@Published var selectedIndex: Int = 0
15+
@Published var themeBackground: NSColor = .windowBackgroundColor
16+
@Published var themeTextColor: NSColor = .labelColor
17+
1418
var itemsRequestTask: Task<Void, Never>?
1519
weak var activeTextView: TextViewController?
1620

@@ -19,6 +23,51 @@ final class SuggestionViewModel: ObservableObject {
1923
private var cursorPosition: CursorPosition?
2024
private var syntaxHighlightedCache: [Int: NSAttributedString] = [:]
2125

26+
var selectedItem: CodeSuggestionEntry? {
27+
guard selectedIndex >= 0, selectedIndex < items.count else { return nil }
28+
return items[selectedIndex]
29+
}
30+
31+
func moveUp() {
32+
guard selectedIndex > 0 else { return }
33+
selectedIndex -= 1
34+
notifySelection()
35+
}
36+
37+
func moveDown() {
38+
guard selectedIndex < items.count - 1 else { return }
39+
selectedIndex += 1
40+
notifySelection()
41+
}
42+
43+
private func notifySelection() {
44+
if let item = selectedItem {
45+
delegate?.completionWindowDidSelect(item: item)
46+
}
47+
}
48+
49+
func updateTheme(from textView: TextViewController) {
50+
themeTextColor = textView.theme.text.color
51+
switch textView.systemAppearance {
52+
case .aqua:
53+
let color = textView.theme.background
54+
if color != .clear {
55+
themeBackground = NSColor(
56+
red: color.redComponent * 0.95,
57+
green: color.greenComponent * 0.95,
58+
blue: color.blueComponent * 0.95,
59+
alpha: 1.0
60+
)
61+
} else {
62+
themeBackground = .windowBackgroundColor
63+
}
64+
case .darkAqua:
65+
themeBackground = textView.theme.background
66+
default:
67+
break
68+
}
69+
}
70+
2271
func showCompletions(
2372
textView: TextViewController,
2473
delegate: CodeSuggestionDelegate,
@@ -59,7 +108,9 @@ final class SuggestionViewModel: ObservableObject {
59108
}
60109

61110
self.items = completionItems.items
111+
self.selectedIndex = 0
62112
self.syntaxHighlightedCache = [:]
113+
self.notifySelection()
63114
showWindowOnParent(targetParentWindow, cursorRect)
64115
}
65116
} catch {
@@ -91,6 +142,9 @@ final class SuggestionViewModel: ObservableObject {
91142
}
92143

93144
items = newItems
145+
selectedIndex = 0
146+
syntaxHighlightedCache = [:]
147+
notifySelection()
94148
}
95149

96150
func didSelect(item: CodeSuggestionEntry) {
@@ -110,8 +164,12 @@ final class SuggestionViewModel: ObservableObject {
110164
}
111165

112166
func willClose() {
167+
itemsRequestTask?.cancel()
168+
itemsRequestTask = nil
113169
items.removeAll()
170+
selectedIndex = 0
114171
activeTextView = nil
172+
delegate = nil
115173
}
116174

117175
func syntaxHighlights(forIndex index: Int) -> NSAttributedString? {

LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/CodeSuggestion/TableView/CodeSuggestionLabelView.swift

Lines changed: 0 additions & 53 deletions
This file was deleted.

LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/CodeSuggestion/TableView/CodeSuggestionPreviewView.swift

Lines changed: 0 additions & 169 deletions
This file was deleted.

0 commit comments

Comments
 (0)