mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-25 07:58:58 +00:00
When the user triggers SQL execution via shortcut/toolbar/context-menu without a manual selection, a floating picker now surfaces the available execution targets: - "Current SQL" — the statement at the cursor - "All SQL" — the entire editor content Each candidate is highlighted as a preview decoration in the editor as the user navigates the picker with arrow keys. Only a single entry is shown when the cursor statement equals the full document. 前端 SQL 范围解析器 (sqlStatementRanges.ts) 按分号拆分语句,同时跳过 字符串、引号标识符、注释和美元引用体中的分隔符,与后端 dbx-core splitter 逻辑一致。选取器支持键盘导航、鼠标悬停高亮、Enter 确认、Esc/失焦取消。 Changes: - sqlExecutionTarget.ts: add SqlExecutionCandidate, SqlExecutionOverride types - sqlStatementRanges.ts: frontend SQL tokeniser (new, 848 lines) - SqlExecutionTargetPicker.vue: accessible floating listbox (new, 109 lines) - QueryEditor.vue: preview StateEffect/StateField; requestExecute() replaces executeCurrentSql() to route through picker - ContentArea.vue: expose requestQueryEditorExecute() to parent - App.vue: toolbar + global shortcut → requestActiveEditorExecute() - i18n: editor.executionPicker keys for all locales - tests: 26 new unit tests for sqlStatementRanges (all 1121 tests pass)
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import * as api from "./api";
|
|
import type { DatabaseType } from "@/types/database";
|
|
|
|
export type ExecuteMode = "all" | "current";
|
|
|
|
export interface SqlExecutionSnapshot {
|
|
fullSql: string;
|
|
selectedSql: string;
|
|
cursorPos: number;
|
|
}
|
|
|
|
export type SqlExecutionOverride = string | SqlExecutionSnapshot;
|
|
|
|
export type SqlExecutionTargetKind = "cursor" | "all";
|
|
|
|
/**
|
|
* A candidate execution target surfaced by the execution target picker.
|
|
* `from`/`to` are offsets into the full document so the editor can highlight
|
|
* the corresponding range as a preview while the picker is open.
|
|
*/
|
|
export interface SqlExecutionCandidate {
|
|
kind: SqlExecutionTargetKind;
|
|
label: string;
|
|
sql: string;
|
|
from: number;
|
|
to: number;
|
|
}
|
|
|
|
export interface SqlExecutionChoiceRequest {
|
|
fullSql: string;
|
|
selectedSql: string;
|
|
cursorPos: number;
|
|
candidates: SqlExecutionCandidate[];
|
|
}
|
|
|
|
export function isSqlExecutionSnapshot(value: SqlExecutionOverride | undefined): value is SqlExecutionSnapshot {
|
|
return typeof value === "object" && value !== null && typeof value.fullSql === "string" && typeof value.selectedSql === "string" && typeof value.cursorPos === "number";
|
|
}
|
|
|
|
export function resolveExecutableSql(fullSql: string, selectedSql: string, options?: { mode?: ExecuteMode; cursorPos?: number }): string {
|
|
const trimmedSelection = selectedSql.trim();
|
|
if (trimmedSelection) return trimmedSelection;
|
|
|
|
if (options?.mode === "current" && options.cursorPos !== undefined) {
|
|
return fullSql;
|
|
}
|
|
|
|
return fullSql;
|
|
}
|
|
|
|
export async function resolveExecutableSqlWithBackend(fullSql: string, selectedSql: string, options?: { mode?: ExecuteMode; cursorPos?: number; databaseType?: DatabaseType }): Promise<string> {
|
|
const trimmedSelection = selectedSql.trim();
|
|
if (trimmedSelection) return trimmedSelection;
|
|
|
|
if (options?.mode === "current" && options.cursorPos !== undefined) {
|
|
return await api.findStatementAtCursor(fullSql, options.cursorPos, options.databaseType);
|
|
}
|
|
|
|
return fullSql;
|
|
}
|