Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Match highlighting in autocomplete suggestions (matched characters shown in bold)
- Loading spinner in autocomplete popup while fetching column metadata

### Changed

- Refactored autocomplete popup to native SwiftUI (visible selection highlight, native accent color, scroll-to-selection)
- Autocomplete now suppresses noisy empty-prefix suggestions in non-browseable contexts (e.g., after SELECT, WHERE)
- Autocomplete ranking stays consistent as you type (unified fuzzy scoring between initial display and live filtering)
- Increased autocomplete suggestion limit from 20 to 40 for schema-heavy contexts (FROM, SELECT, WHERE)

## [0.20.4] - 2026-03-19

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,11 @@ public protocol CodeSuggestionEntry {
var imageColor: Color { get }

var deprecated: Bool { get }

/// Character index ranges in the label that matched the user's typed prefix.
var matchedRanges: [Range<Int>] { get }
}

public extension CodeSuggestionEntry {
var matchedRanges: [Range<Int>] { [] }
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import AppKit
final class SuggestionViewModel: ObservableObject {
/// The items to be displayed in the window
@Published var items: [CodeSuggestionEntry] = []
@Published var selectedIndex: Int = 0
@Published var themeBackground: NSColor = .windowBackgroundColor
@Published var themeTextColor: NSColor = .labelColor

var itemsRequestTask: Task<Void, Never>?
weak var activeTextView: TextViewController?

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

var selectedItem: CodeSuggestionEntry? {
guard selectedIndex >= 0, selectedIndex < items.count else { return nil }
return items[selectedIndex]
}

func moveUp() {
guard selectedIndex > 0 else { return }
selectedIndex -= 1
notifySelection()
}

func moveDown() {
guard selectedIndex < items.count - 1 else { return }
selectedIndex += 1
notifySelection()
}

private func notifySelection() {
if let item = selectedItem {
delegate?.completionWindowDidSelect(item: item)
}
}

func updateTheme(from textView: TextViewController) {
themeTextColor = textView.theme.text.color
switch textView.systemAppearance {
case .aqua:
let color = textView.theme.background
if color != .clear {
themeBackground = NSColor(
red: color.redComponent * 0.95,
green: color.greenComponent * 0.95,
blue: color.blueComponent * 0.95,
alpha: 1.0
)
} else {
themeBackground = .windowBackgroundColor
}
case .darkAqua:
themeBackground = textView.theme.background
default:
break
}
}

func showCompletions(
textView: TextViewController,
delegate: CodeSuggestionDelegate,
Expand Down Expand Up @@ -59,7 +108,9 @@ final class SuggestionViewModel: ObservableObject {
}

self.items = completionItems.items
self.selectedIndex = 0
self.syntaxHighlightedCache = [:]
self.notifySelection()
showWindowOnParent(targetParentWindow, cursorRect)
}
} catch {
Expand Down Expand Up @@ -91,6 +142,9 @@ final class SuggestionViewModel: ObservableObject {
}

items = newItems
selectedIndex = 0
syntaxHighlightedCache = [:]
notifySelection()
}

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

func willClose() {
itemsRequestTask?.cancel()
itemsRequestTask = nil
items.removeAll()
selectedIndex = 0
activeTextView = nil
delegate = nil
}

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

This file was deleted.

This file was deleted.

Loading
Loading