50 lines
2.6 KiB
TypeScript
50 lines
2.6 KiB
TypeScript
import type { Command, EditorView } from "@codemirror/view";
|
|
import { EditorSelection } from "@codemirror/state";
|
|
import { matchesShortcut, type ShortcutLikeEvent } from "@/lib/editor/keyboardShortcuts";
|
|
import { analyzePlainTextSelectionRanges, nextPlainTextSelectionRange } from "@/lib/editor/plainTextSelectionRanges";
|
|
import { chooseNextSemanticSelectionRange, type SemanticSelectionRange } from "@/lib/editor/semanticSelectionRanges";
|
|
import { analyzeSqlSemanticSelectionRanges, nextSqlSemanticSelectionRange, type SqlSemanticSelectionOptions } from "@/lib/editor/sqlSemanticSelectionRanges";
|
|
|
|
export interface QueryEditorExtendSelectionOptions extends SqlSemanticSelectionOptions {
|
|
language?: "sql" | "text";
|
|
}
|
|
|
|
export function extendQueryEditorSelection(view: EditorView, options: QueryEditorExtendSelectionOptions = {}): boolean {
|
|
const doc = view.state.doc.toString();
|
|
const plainTextAnalysis = analyzePlainTextSelectionRanges(doc);
|
|
const sqlAnalysis = options.language === "text" ? null : analyzeSqlSemanticSelectionRanges(doc, options);
|
|
let changed = false;
|
|
const ranges = view.state.selection.ranges.map((range) => {
|
|
const context = {
|
|
doc,
|
|
cursor: range.head,
|
|
current: { from: range.from, to: range.to },
|
|
preferDelimitedContent: options.language !== "text",
|
|
};
|
|
const candidates: SemanticSelectionRange[] = [];
|
|
const plainTextCandidate = nextPlainTextSelectionRange(context, plainTextAnalysis);
|
|
if (plainTextCandidate) candidates.push(plainTextCandidate);
|
|
const sqlCandidate = sqlAnalysis ? nextSqlSemanticSelectionRange(context, options, sqlAnalysis) : null;
|
|
if (sqlCandidate) candidates.push(sqlCandidate);
|
|
const next = chooseNextSemanticSelectionRange(context.current, context.cursor, candidates);
|
|
if (!next) return range;
|
|
changed = true;
|
|
return range.anchor <= range.head ? EditorSelection.range(next.from, next.to) : EditorSelection.range(next.to, next.from);
|
|
});
|
|
if (!changed) return false;
|
|
view.dispatch({ selection: EditorSelection.create(ranges, view.state.selection.mainIndex), scrollIntoView: true });
|
|
return true;
|
|
}
|
|
|
|
export interface PreventableShortcutEvent extends ShortcutLikeEvent {
|
|
preventDefault(): void;
|
|
}
|
|
|
|
export function runQueryEditorAltExtendSelection(event: PreventableShortcutEvent, shortcut: string, view: EditorView, command: Command | null, platform = globalThis.navigator?.platform || ""): boolean {
|
|
if (!command || !event.altKey || event.ctrlKey || event.metaKey) return false;
|
|
if (!matchesShortcut(event, shortcut, platform)) return false;
|
|
if (!command(view)) return false;
|
|
event.preventDefault();
|
|
return true;
|
|
}
|