added typeaheead and introspetion
This commit is contained in:
+46
-1
@@ -478,7 +478,7 @@ struct MainView: View {
|
||||
Label("Introspection — Load Table Structures into RAM", systemImage: "memorychip")
|
||||
}
|
||||
if let snapshot = model.introspection(for: db) {
|
||||
Text("Cached: \(snapshot.tables.count) tables, \(snapshot.columnsByTable.values.reduce(0) { $0 + $1.count }) columns")
|
||||
Text("Cached: \(snapshot.tables.count) tables, \(snapshot.views.count) views, \(snapshot.columnsByTable.values.reduce(0) { $0 + $1.count }) columns")
|
||||
}
|
||||
Divider()
|
||||
Button("Set as Default Schema") { model.selectedDatabase = db }
|
||||
@@ -1407,6 +1407,7 @@ struct SQLTextView: NSViewRepresentable {
|
||||
height: 8
|
||||
)
|
||||
editor.delegate = context.coordinator
|
||||
editor.isAutomaticTextCompletionEnabled = true
|
||||
editor.string = text
|
||||
|
||||
context.coordinator.highlight(editor)
|
||||
@@ -1462,6 +1463,8 @@ struct SQLTextView: NSViewRepresentable {
|
||||
final class Coordinator: NSObject, NSTextViewDelegate {
|
||||
var parent: SQLTextView
|
||||
var lastFocusRequest: Int
|
||||
private var pendingCompletion: DispatchWorkItem?
|
||||
private var showingCompletion = false
|
||||
|
||||
init(_ parent: SQLTextView) {
|
||||
self.parent = parent
|
||||
@@ -1490,6 +1493,8 @@ struct SQLTextView: NSViewRepresentable {
|
||||
return Array(Set(completionWords + parent.completions))
|
||||
.filter { $0.range(of: prefix, options: [.anchored, .caseInsensitive]) != nil }
|
||||
.sorted()
|
||||
.prefix(60)
|
||||
.map { $0 }
|
||||
}
|
||||
|
||||
func textViewDidChangeSelection(_ notification: Notification) {
|
||||
@@ -1508,6 +1513,46 @@ struct SQLTextView: NSViewRepresentable {
|
||||
|
||||
parent.text = editor.string
|
||||
highlight(editor)
|
||||
// Never open AppKit's completion panel synchronously inside
|
||||
// textDidChange: it re-enters NSTextView editing/delegate callbacks.
|
||||
pendingCompletion?.cancel()
|
||||
guard !showingCompletion,
|
||||
let event = NSApp.currentEvent,
|
||||
event.type == .keyDown,
|
||||
let characters = event.characters,
|
||||
characters.rangeOfCharacter(from: CharacterSet.letters.union(.decimalDigits).union(CharacterSet(charactersIn: "_"))) != nil,
|
||||
!parent.completions.isEmpty else { return }
|
||||
|
||||
let selected = editor.selectedRange()
|
||||
guard selected.length == 0, selected.location != NSNotFound else { return }
|
||||
let content = editor.string as NSString
|
||||
let cursor = min(selected.location, content.length)
|
||||
var start = cursor
|
||||
let valid = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "_"))
|
||||
while start > 0 {
|
||||
let scalar = content.substring(with: NSRange(location: start - 1, length: 1))
|
||||
guard scalar.unicodeScalars.allSatisfy({ valid.contains($0) }) else { break }
|
||||
start -= 1
|
||||
}
|
||||
guard cursor - start >= 2 else { return }
|
||||
let expectedText = editor.string
|
||||
let expectedCursor = cursor
|
||||
let task = DispatchWorkItem { [weak self, weak editor] in
|
||||
guard let self, let editor,
|
||||
editor.window?.firstResponder === editor,
|
||||
editor.string == expectedText,
|
||||
editor.selectedRange().location == expectedCursor,
|
||||
editor.selectedRange().length == 0,
|
||||
!self.showingCompletion else { return }
|
||||
self.showingCompletion = true
|
||||
editor.complete(nil)
|
||||
// AppKit may synchronously notify text changes while opening.
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.showingCompletion = false
|
||||
}
|
||||
}
|
||||
pendingCompletion = task
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: task)
|
||||
}
|
||||
|
||||
func highlight(_ editor: NSTextView) {
|
||||
|
||||
Reference in New Issue
Block a user