feat(editor): allow hiding query line numbers
This commit is contained in:
1 parent
eaad88cc80
commit
4a478acf07
18 files changed
+205
-7
No files matched your search
@@ -325,6 +325,7 @@ const executeModeLabel = computed(() => translateWithExecuteShortcut("settings.e
|
||||
const editExecuteAllOnBlankLine = ref(settingsStore.editorSettings.executeAllOnBlankLine);
|
||||
const editShowExecutionTargetPicker = ref(settingsStore.editorSettings.showExecutionTargetPicker);
|
||||
const editShowStatementRunButtons = ref(settingsStore.editorSettings.showStatementRunButtons);
|
||||
const editShowLineNumbers = ref(settingsStore.editorSettings.showLineNumbers);
|
||||
const editShowCurrentStatementFrame = ref(settingsStore.editorSettings.showCurrentStatementFrame);
|
||||
const editShowInsertValueHints = ref(settingsStore.editorSettings.showInsertValueHints);
|
||||
const editAutoAliasTables = ref(settingsStore.editorSettings.autoAliasTables);
|
||||
@@ -526,6 +527,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
|
||||
executeAllOnBlankLine: editExecuteAllOnBlankLine.value,
|
||||
showExecutionTargetPicker: editShowExecutionTargetPicker.value,
|
||||
showStatementRunButtons: editShowStatementRunButtons.value,
|
||||
showLineNumbers: editShowLineNumbers.value,
|
||||
showCurrentStatementFrame: editShowCurrentStatementFrame.value,
|
||||
showInsertValueHints: editShowInsertValueHints.value,
|
||||
autoAliasTables: editAutoAliasTables.value,
|
||||
@@ -812,6 +814,7 @@ function syncEditorSettingsDraftFromStore() {
|
||||
editExecuteAllOnBlankLine.value = settingsStore.editorSettings.executeAllOnBlankLine;
|
||||
editShowExecutionTargetPicker.value = settingsStore.editorSettings.showExecutionTargetPicker;
|
||||
editShowStatementRunButtons.value = settingsStore.editorSettings.showStatementRunButtons;
|
||||
editShowLineNumbers.value = settingsStore.editorSettings.showLineNumbers;
|
||||
editShowCurrentStatementFrame.value = settingsStore.editorSettings.showCurrentStatementFrame;
|
||||
editShowInsertValueHints.value = settingsStore.editorSettings.showInsertValueHints;
|
||||
editAutoAliasTables.value = settingsStore.editorSettings.autoAliasTables;
|
||||
@@ -1063,6 +1066,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
|
||||
editExecuteAllOnBlankLine.value = DEFAULT_EDITOR_SETTINGS.executeAllOnBlankLine;
|
||||
editShowExecutionTargetPicker.value = DEFAULT_EDITOR_SETTINGS.showExecutionTargetPicker;
|
||||
editShowStatementRunButtons.value = DEFAULT_EDITOR_SETTINGS.showStatementRunButtons;
|
||||
editShowLineNumbers.value = DEFAULT_EDITOR_SETTINGS.showLineNumbers;
|
||||
editShowCurrentStatementFrame.value = DEFAULT_EDITOR_SETTINGS.showCurrentStatementFrame;
|
||||
editShowInsertValueHints.value = DEFAULT_EDITOR_SETTINGS.showInsertValueHints;
|
||||
editAutoAliasTables.value = DEFAULT_EDITOR_SETTINGS.autoAliasTables;
|
||||
@@ -1175,6 +1179,7 @@ function resetAllDefaults() {
|
||||
editExecuteAllOnBlankLine.value = DEFAULT_EDITOR_SETTINGS.executeAllOnBlankLine;
|
||||
editShowExecutionTargetPicker.value = DEFAULT_EDITOR_SETTINGS.showExecutionTargetPicker;
|
||||
editShowStatementRunButtons.value = DEFAULT_EDITOR_SETTINGS.showStatementRunButtons;
|
||||
editShowLineNumbers.value = DEFAULT_EDITOR_SETTINGS.showLineNumbers;
|
||||
editShowCurrentStatementFrame.value = DEFAULT_EDITOR_SETTINGS.showCurrentStatementFrame;
|
||||
editShowInsertValueHints.value = DEFAULT_EDITOR_SETTINGS.showInsertValueHints;
|
||||
editAutoAliasTables.value = DEFAULT_EDITOR_SETTINGS.autoAliasTables;
|
||||
@@ -3938,6 +3943,16 @@ onUnmounted(() => {
|
||||
<Switch id="editor-show-statement-run-buttons" v-model="editShowStatementRunButtons" class="mt-0.5" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="editor-show-line-numbers">{{ t("settings.showLineNumbers") }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ t("settings.showLineNumbersDescription") }}
|
||||
</p>
|
||||
</div>
|
||||
<Switch id="editor-show-line-numbers" v-model="editShowLineNumbers" class="mt-0.5" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="editor-show-current-statement-frame">{{ t("settings.showCurrentStatementFrame") }}</Label>
|
||||
|
||||
@@ -107,6 +107,7 @@ import type { SqlHighlighter } from "@/lib/sql/sqlHighlighter";
|
||||
import { EDITOR_FONT_FAMILY_CSS_VAR, EDITOR_FONT_SIZE_CSS_VAR, editorDiagnosticColors, editorThemeAppearanceFor, loadEditorTheme, editorFontTheme, shellLineCommentTheme, sqlCompletionTheme, sqlSemanticHighlightTheme } from "@/lib/editor/editorThemes";
|
||||
import { createStatementGutterMarkerDom, shouldShowStatementGutter } from "@/lib/editor/codemirrorStatementGutter";
|
||||
import { createQueryEditorSearchKeymap } from "@/lib/editor/queryEditorSearchKeymap";
|
||||
import { buildQueryEditorLineNumbersExtension } from "@/lib/editor/queryEditorLineNumbers";
|
||||
import { searchKeymapWithoutModD } from "@/lib/editor/codemirrorSearchKeymap";
|
||||
import { defaultKeymapForGlobalShortcuts } from "@/lib/editor/codemirrorDefaultKeymap";
|
||||
import { appendSqlCompletionSpace } from "@/lib/editor/sqlCompletionInsertion";
|
||||
@@ -512,12 +513,14 @@ interface EditorGestureEvent extends Event {
|
||||
}
|
||||
|
||||
let editorViewModule: typeof import("@codemirror/view") | null = null;
|
||||
let codeMirrorLineNumbers: typeof import("@codemirror/view").lineNumbers | null = null;
|
||||
let codeMirrorPrec: typeof import("@codemirror/state").Prec | null = null;
|
||||
let codeMirrorEditorSelection: typeof import("@codemirror/state").EditorSelection | null = null;
|
||||
let hoverCloseEffect: StateEffect<unknown> | null = null;
|
||||
let fontThemeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let codeMirrorTheme: import("@codemirror/state").Compartment | null = null;
|
||||
let wordWrapComp: import("@codemirror/state").Compartment | null = null;
|
||||
let lineNumbersComp: import("@codemirror/state").Compartment | null = null;
|
||||
let vimModeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let closeBracketsComp: import("@codemirror/state").Compartment | null = null;
|
||||
let sqlLanguageComp: import("@codemirror/state").Compartment | null = null;
|
||||
@@ -694,6 +697,7 @@ const queryEditorAppearanceSettings = computed(() => {
|
||||
autoCloseBrackets: settings.autoCloseBrackets,
|
||||
showCurrentStatementFrame: settings.showCurrentStatementFrame,
|
||||
showInsertValueHints: settings.showInsertValueHints,
|
||||
showLineNumbers: settings.showLineNumbers,
|
||||
shortcuts: settings.shortcuts,
|
||||
showStatementRunButtons: settings.showStatementRunButtons,
|
||||
};
|
||||
@@ -2053,6 +2057,14 @@ function wordWrapExtension() {
|
||||
return props.forceWordWrap || settingsStore.editorSettings.wordWrap ? editorViewModule.EditorView.lineWrapping : [];
|
||||
}
|
||||
|
||||
function lineNumbersExtension(enabled = settingsStore.editorSettings.showLineNumbers) {
|
||||
return buildQueryEditorLineNumbersExtension(codeMirrorLineNumbers, enabled, {
|
||||
domEventHandlers: {
|
||||
mousedown: selectSqlLineFromGutter,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function closeBracketsExtension(enabled = settingsStore.editorSettings.autoCloseBrackets) {
|
||||
if (!enabled || !codeMirrorCloseBrackets) return [];
|
||||
const exts: import("@codemirror/state").Extension[] = [codeMirrorCloseBrackets()];
|
||||
@@ -4649,12 +4661,14 @@ onMounted(async () => {
|
||||
rectangularSelection,
|
||||
} as typeof import("@codemirror/view");
|
||||
hoverCloseEffect = closeHoverTooltips;
|
||||
codeMirrorLineNumbers = lineNumbers;
|
||||
codeMirrorPrec = Prec;
|
||||
codeMirrorEditorSelection = EditorSelection;
|
||||
codeMirrorSnippetCompletion = snippetCompletion;
|
||||
fontThemeComp = new Compartment();
|
||||
codeMirrorTheme = new Compartment();
|
||||
wordWrapComp = new Compartment();
|
||||
lineNumbersComp = new Compartment();
|
||||
vimModeComp = new Compartment();
|
||||
closeBracketsComp = new Compartment();
|
||||
sqlLanguageComp = new Compartment();
|
||||
@@ -5115,11 +5129,7 @@ onMounted(async () => {
|
||||
scrollToMatch: (range) => EditorView.scrollIntoView(range, { y: "center" }),
|
||||
}),
|
||||
runGutterComp.of(runStatementGutterExtension()),
|
||||
lineNumbers({
|
||||
domEventHandlers: {
|
||||
mousedown: selectSqlLineFromGutter,
|
||||
},
|
||||
}),
|
||||
lineNumbersComp.of(lineNumbersExtension(initialSettings.showLineNumbers)),
|
||||
currentStatementFrameExtension,
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
@@ -5763,7 +5773,7 @@ function getCurrentCustomThemeColors() {
|
||||
watch(
|
||||
[queryEditorAppearanceSettings, () => isDark.value, () => themePalette.value, editorThemeAppearance],
|
||||
async ([ss]) => {
|
||||
if (!view.value || !codeMirrorTheme || !fontThemeComp || !wordWrapComp || !vimModeComp || !closeBracketsComp || !runGutterComp || !runKeymapComp || !editorViewModule) {
|
||||
if (!view.value || !codeMirrorTheme || !fontThemeComp || !wordWrapComp || !lineNumbersComp || !vimModeComp || !closeBracketsComp || !runGutterComp || !runKeymapComp || !editorViewModule) {
|
||||
return;
|
||||
}
|
||||
if (!isGestureZooming.value && !zoomCommitScheduler.hasPendingCommit() && liveFontSize.value !== ss.fontSize) {
|
||||
@@ -5773,13 +5783,14 @@ watch(
|
||||
syncEditorDiagnosticCssVars();
|
||||
const themeColors = getCurrentCustomThemeColors();
|
||||
const [themeExt] = await Promise.all([loadEditorTheme(ss.theme, editorThemeAppearance(), themeColors, themePalette.value), ss.vimModeEnabled ? ensureCodeMirrorVim() : Promise.resolve(false)]);
|
||||
if (!view.value || !codeMirrorTheme || !wordWrapComp || !vimModeComp || !closeBracketsComp || !runGutterComp || !runKeymapComp || !editorViewModule) {
|
||||
if (!view.value || !codeMirrorTheme || !wordWrapComp || !lineNumbersComp || !vimModeComp || !closeBracketsComp || !runGutterComp || !runKeymapComp || !editorViewModule) {
|
||||
return;
|
||||
}
|
||||
view.value.dispatch({
|
||||
effects: [
|
||||
codeMirrorTheme.reconfigure(themeExt),
|
||||
wordWrapComp.reconfigure(props.forceWordWrap || ss.wordWrap ? editorViewModule.EditorView.lineWrapping : []),
|
||||
lineNumbersComp.reconfigure(lineNumbersExtension(ss.showLineNumbers)),
|
||||
vimModeComp.reconfigure(vimModeExtension(settingsStore.editorSettings.vimModeEnabled)),
|
||||
closeBracketsComp.reconfigure(closeBracketsExtension(settingsStore.editorSettings.autoCloseBrackets)),
|
||||
runGutterComp.reconfigure(runStatementGutterExtension()),
|
||||
|
||||
@@ -6369,6 +6369,8 @@ export default {
|
||||
showExecutionTargetPickerDescription: "When enabled, running without a selection lets you choose between the current statement and all SQL.",
|
||||
showStatementRunButtons: "Show left-side run buttons",
|
||||
showStatementRunButtonsDescription: "Show per-statement run buttons in the SQL editor gutter. Keyboard shortcuts and context menu execution still work when disabled.",
|
||||
showLineNumbers: "Show line numbers",
|
||||
showLineNumbersDescription: "Show line numbers in the SQL editor gutter",
|
||||
showCurrentStatementFrame: "Show current statement frame",
|
||||
showCurrentStatementFrameDescription: "When enabled, the SQL editor draws an outline around the current executable statement; when disabled, the outline is hidden.",
|
||||
showInsertValueHints: "Show INSERT value column hints",
|
||||
|
||||
@@ -6055,6 +6055,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "Al activarlo, ejecutar sin selección permite elegir entre la sentencia actual y todo el SQL.",
|
||||
showStatementRunButtons: "Mostrar botones de ejecución laterales",
|
||||
showStatementRunButtonsDescription: "Muestra botones para ejecutar cada sentencia en el margen del editor SQL. Los atajos de teclado y el menú contextual seguirán funcionando al desactivarlo.",
|
||||
showLineNumbers: "Mostrar números de línea",
|
||||
showLineNumbersDescription: "Mostrar números de línea en el margen del editor SQL",
|
||||
showCurrentStatementFrame: "Mostrar marco de la sentencia actual",
|
||||
showCurrentStatementFrameDescription: "Al activarlo, el editor SQL dibuja un contorno alrededor de la sentencia ejecutable actual; al desactivarlo, se oculta.",
|
||||
showInsertValueHints: "Mostrar pistas de columnas en VALUES de INSERT",
|
||||
|
||||
@@ -6055,6 +6055,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "Se attivo, l'esecuzione senza selezione permette di scegliere tra istruzione corrente e tutto l'SQL.",
|
||||
showStatementRunButtons: "Mostra pulsanti di esecuzione laterali",
|
||||
showStatementRunButtonsDescription: "Mostra nel margine dell'editor SQL i pulsanti per eseguire ogni istruzione. Scorciatoie da tastiera e menu contestuale continuano a funzionare quando disattivati.",
|
||||
showLineNumbers: "Mostra numeri di riga",
|
||||
showLineNumbersDescription: "Mostra i numeri di riga nel margine dell'editor SQL",
|
||||
showCurrentStatementFrame: "Mostra cornice istruzione corrente",
|
||||
showCurrentStatementFrameDescription: "Se attivo, l'editor SQL disegna un contorno intorno all'istruzione eseguibile corrente; se disattivato, il contorno è nascosto.",
|
||||
showInsertValueHints: "Mostra suggerimenti colonne nei VALUES di INSERT",
|
||||
|
||||
@@ -6081,6 +6081,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "有効にすると、選択なしで実行するときに現在の文とすべてのSQLを一時的に選べます。",
|
||||
showStatementRunButtons: "左側の実行ボタンを表示",
|
||||
showStatementRunButtonsDescription: "SQLエディタのガターに文ごとの実行ボタンを表示します。無効にしてもキーボードショートカットとコンテキストメニューからの実行は引き続き使えます。",
|
||||
showLineNumbers: "行番号を表示",
|
||||
showLineNumbersDescription: "SQLエディタのガターに行番号を表示します",
|
||||
showCurrentStatementFrame: "現在の文の枠線を表示",
|
||||
showCurrentStatementFrameDescription: "有効にすると、SQLエディタで現在実行可能な文を枠線で示します。無効にすると枠線を表示しません。",
|
||||
showInsertValueHints: "INSERT 値の列名ヒントを表示",
|
||||
|
||||
@@ -5756,6 +5756,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "활성화하면 선택 없이 실행할 때 현재 구문과 전체 SQL 중에서 선택할 수 있습니다.",
|
||||
showStatementRunButtons: "왼쪽 실행 버튼 표시",
|
||||
showStatementRunButtonsDescription: "SQL 편집기 여백에 구문별 실행 버튼을 표시합니다. 비활성화해도 키보드 단축키와 컨텍스트 메뉴 실행은 계속 작동합니다.",
|
||||
showLineNumbers: "줄 번호 표시",
|
||||
showLineNumbersDescription: "SQL 편집기 여백에 줄 번호를 표시합니다",
|
||||
showCurrentStatementFrame: "현재 구문 프레임 표시",
|
||||
showCurrentStatementFrameDescription: "활성화하면 SQL 편집기가 현재 실행 가능한 구문 주위에 윤곽선을 그립니다. 비활성화하면 윤곽선이 숨겨집니다.",
|
||||
showInsertValueHints: "INSERT 값 컬럼 힌트 표시",
|
||||
|
||||
@@ -6057,6 +6057,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "Quando ativado, executar sem seleção permite escolher entre a instrução atual e todo o SQL.",
|
||||
showStatementRunButtons: "Mostrar botões de execução laterais",
|
||||
showStatementRunButtonsDescription: "Mostra botões para executar cada instrução na margem do editor SQL. Atalhos de teclado e execução pelo menu de contexto continuam funcionando quando desativado.",
|
||||
showLineNumbers: "Mostrar números de linha",
|
||||
showLineNumbersDescription: "Mostrar números de linha na margem do editor SQL",
|
||||
showCurrentStatementFrame: "Mostrar moldura da instrução atual",
|
||||
showCurrentStatementFrameDescription: "Quando ativado, o editor SQL desenha um contorno ao redor da instrução executável atual; quando desativado, o contorno fica oculto.",
|
||||
showInsertValueHints: "Mostrar dicas de colunas em VALUES de INSERT",
|
||||
|
||||
@@ -6352,6 +6352,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "开启后,无选区执行时可在当前语句和全部 SQL 之间临时选择。",
|
||||
showStatementRunButtons: "显示左侧执行按钮",
|
||||
showStatementRunButtonsDescription: "在 SQL 编辑器左侧显示按语句执行的快捷按钮。关闭后仍可通过快捷键和右键菜单执行。",
|
||||
showLineNumbers: "显示行号",
|
||||
showLineNumbersDescription: "在 SQL 编辑器左侧显示行号",
|
||||
showCurrentStatementFrame: "显示当前语句外框线",
|
||||
showCurrentStatementFrameDescription: "开启后,在 SQL 编辑器中用外框线标出当前可执行语句;关闭后不显示外框线。",
|
||||
showInsertValueHints: "显示 INSERT 值列名提示",
|
||||
|
||||
@@ -5373,6 +5373,8 @@ export default withEnglishFallback({
|
||||
showExecutionTargetPickerDescription: "啟用後,無選取執行時可在目前語句與全部 SQL 之間臨時選擇。",
|
||||
showStatementRunButtons: "顯示左側執行按鈕",
|
||||
showStatementRunButtonsDescription: "在 SQL 編輯器左側顯示按語句執行的快捷按鈕。關閉後仍可透過快捷鍵和右鍵選單執行。",
|
||||
showLineNumbers: "顯示行號",
|
||||
showLineNumbersDescription: "在 SQL 編輯器左側顯示行號",
|
||||
showCurrentStatementFrame: "顯示目前語句外框線",
|
||||
showCurrentStatementFrameDescription: "啟用後,在 SQL 編輯器中用外框線標出目前可執行語句;關閉後不顯示外框線。",
|
||||
showInsertValueHints: "顯示 INSERT 值欄位提示",
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { Compartment, EditorState } from "@codemirror/state";
|
||||
import { EditorView, GutterMarker, gutter, lineNumbers } from "@codemirror/view";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { buildQueryEditorLineNumbersExtension } from "@/lib/editor/queryEditorLineNumbers";
|
||||
|
||||
class StatementRunMarker extends GutterMarker {}
|
||||
|
||||
const activeViews: EditorView[] = [];
|
||||
|
||||
function createEditor(showLineNumbers: boolean) {
|
||||
const lineNumbersCompartment = new Compartment();
|
||||
const runGutterCompartment = new Compartment();
|
||||
const runMarker = new StatementRunMarker();
|
||||
const selectLine = vi.fn((_view: EditorView, _line: { from: number; to: number }, _event: Event) => true);
|
||||
const view = new EditorView({
|
||||
state: EditorState.create({
|
||||
doc: "SELECT 1;\nSELECT 2;",
|
||||
extensions: [
|
||||
lineNumbersCompartment.of(
|
||||
buildQueryEditorLineNumbersExtension(lineNumbers, showLineNumbers, {
|
||||
domEventHandlers: { mousedown: selectLine },
|
||||
}),
|
||||
),
|
||||
runGutterCompartment.of(
|
||||
gutter({
|
||||
class: "cm-run-statement-gutter",
|
||||
lineMarker: () => runMarker,
|
||||
}),
|
||||
),
|
||||
],
|
||||
}),
|
||||
parent: document.createElement("div"),
|
||||
});
|
||||
activeViews.push(view);
|
||||
return { lineNumbersCompartment, selectLine, view };
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const view of activeViews.splice(0)) view.destroy();
|
||||
});
|
||||
|
||||
describe("query editor line numbers", () => {
|
||||
it("keeps line selection behavior when enabled", () => {
|
||||
const { selectLine, view } = createEditor(true);
|
||||
const lineNumber = view.dom.querySelector<HTMLElement>(".cm-lineNumbers .cm-gutterElement");
|
||||
|
||||
lineNumber?.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, button: 0 }));
|
||||
|
||||
expect(view.dom.querySelector(".cm-lineNumbers")).not.toBeNull();
|
||||
expect(selectLine).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("reconfigures line numbers without removing the statement run gutter", () => {
|
||||
const { lineNumbersCompartment, view } = createEditor(true);
|
||||
|
||||
expect(view.dom.querySelector(".cm-lineNumbers")).not.toBeNull();
|
||||
expect(view.dom.querySelector(".cm-run-statement-gutter")).not.toBeNull();
|
||||
|
||||
view.dispatch({
|
||||
effects: lineNumbersCompartment.reconfigure(buildQueryEditorLineNumbersExtension(lineNumbers, false, { domEventHandlers: {} })),
|
||||
});
|
||||
|
||||
expect(view.dom.querySelector(".cm-lineNumbers")).toBeNull();
|
||||
expect(view.dom.querySelector(".cm-run-statement-gutter")).not.toBeNull();
|
||||
|
||||
view.dispatch({
|
||||
effects: lineNumbersCompartment.reconfigure(buildQueryEditorLineNumbersExtension(lineNumbers, true, { domEventHandlers: {} })),
|
||||
});
|
||||
|
||||
expect(view.dom.querySelector(".cm-lineNumbers")).not.toBeNull();
|
||||
expect(view.dom.querySelector(".cm-run-statement-gutter")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Extension } from "@codemirror/state";
|
||||
import type { lineNumbers } from "@codemirror/view";
|
||||
|
||||
type LineNumbersFactory = typeof lineNumbers;
|
||||
type LineNumbersConfig = NonNullable<Parameters<LineNumbersFactory>[0]>;
|
||||
|
||||
export function buildQueryEditorLineNumbersExtension(factory: LineNumbersFactory | null, enabled: boolean, config: LineNumbersConfig): Extension {
|
||||
return enabled && factory ? factory(config) : [];
|
||||
}
|
||||
@@ -38,6 +38,10 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
|
||||
expect(EDITOR_SETTINGS_DRAFT_KEYS).not.toContain("globalQueryTimeoutSecs");
|
||||
});
|
||||
|
||||
it("includes showLineNumbers", () => {
|
||||
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("showLineNumbers");
|
||||
});
|
||||
|
||||
it("includes continueOnErrorOnBatch", () => {
|
||||
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("continueOnErrorOnBatch");
|
||||
});
|
||||
@@ -103,6 +107,16 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
|
||||
});
|
||||
|
||||
describe("editorSettingsDraftFromSettings", () => {
|
||||
it("round-trips a hidden line-number preference through the draft patch", () => {
|
||||
const settings = makeSettings({ showLineNumbers: true });
|
||||
const draft = editorSettingsDraftFromSettings(settings);
|
||||
const base = editorSettingsDraftFromSettings(settings);
|
||||
|
||||
draft.showLineNumbers = false;
|
||||
|
||||
expect(editorSettingsPatchFromDraft(draft, base)).toEqual({ showLineNumbers: false });
|
||||
});
|
||||
|
||||
it("toggles substitution without discarding per-database overrides", () => {
|
||||
const base = editorSettingsDraftFromSettings(
|
||||
makeSettings({
|
||||
|
||||
@@ -37,6 +37,30 @@ describe("settings search", () => {
|
||||
{ id: "desktop", category: "about", titleKey: "hidden", visible: ({ isWeb }) => !isWeb },
|
||||
];
|
||||
|
||||
it("indexes the query editor line-number preference", () => {
|
||||
const definition = SETTINGS_SEARCH_DEFINITIONS.find((entry) => entry.id === "editor-line-numbers");
|
||||
expect(definition).toEqual({
|
||||
id: "editor-line-numbers",
|
||||
category: "editor",
|
||||
titleKey: "settings.showLineNumbers",
|
||||
descriptionKey: "settings.showLineNumbersDescription",
|
||||
targetId: "editor",
|
||||
});
|
||||
|
||||
const entries = resolveSettingsSearchEntries(
|
||||
[definition!],
|
||||
{ isWeb: false, visibleCategories: new Set<SettingsCategory>(["editor"]) },
|
||||
(key) =>
|
||||
({
|
||||
"settings.showLineNumbers": "Show line numbers",
|
||||
"settings.showLineNumbersDescription": "Show line numbers in the SQL editor gutter",
|
||||
})[key] ?? key,
|
||||
categoryLabels,
|
||||
);
|
||||
|
||||
expect(searchSettings(entries, "line number", "en").map((entry) => entry.id)).toEqual(["editor-line-numbers"]);
|
||||
});
|
||||
|
||||
it("does not index connection or query timeout under editor settings", () => {
|
||||
expect(SETTINGS_SEARCH_DEFINITIONS.map((definition) => definition.id)).not.toContain("editor-global-connect-timeout");
|
||||
expect(SETTINGS_SEARCH_DEFINITIONS.map((definition) => definition.id)).not.toContain("editor-global-query-timeout");
|
||||
|
||||
@@ -16,6 +16,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
|
||||
"executeAllOnBlankLine",
|
||||
"showExecutionTargetPicker",
|
||||
"showStatementRunButtons",
|
||||
"showLineNumbers",
|
||||
"showCurrentStatementFrame",
|
||||
"showInsertValueHints",
|
||||
"autoAliasTables",
|
||||
|
||||
@@ -109,6 +109,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
|
||||
{ id: "editor-execute-all-on-blank-line", category: "editor", titleKey: "settings.executeAllOnBlankLine", descriptionKey: "settings.executeAllOnBlankLineDescription", targetId: "editor" },
|
||||
{ id: "editor-execution-target", category: "editor", titleKey: "settings.showExecutionTargetPicker", descriptionKey: "settings.showExecutionTargetPickerDescription", targetId: "editor" },
|
||||
{ id: "editor-run-buttons", category: "editor", titleKey: "settings.showStatementRunButtons", descriptionKey: "settings.showStatementRunButtonsDescription", targetId: "editor" },
|
||||
{ id: "editor-line-numbers", category: "editor", titleKey: "settings.showLineNumbers", descriptionKey: "settings.showLineNumbersDescription", targetId: "editor" },
|
||||
{ id: "editor-statement-frame", category: "editor", titleKey: "settings.showCurrentStatementFrame", descriptionKey: "settings.showCurrentStatementFrameDescription", targetId: "editor" },
|
||||
{ id: "editor-value-hints", category: "editor", titleKey: "settings.showInsertValueHints", descriptionKey: "settings.showInsertValueHintsDescription", targetId: "editor" },
|
||||
{ id: "editor-word-wrap", category: "editor", titleKey: "settings.wordWrap", descriptionKey: "settings.wordWrapDescription", targetId: "editor" },
|
||||
|
||||
@@ -131,6 +131,12 @@ describe("normalizeEditorSettings", () => {
|
||||
expect(normalizeEditorSettings({ sidebarConnectionSortMode: "invalid" as any }).sidebarConnectionSortMode).toBe("manual");
|
||||
});
|
||||
|
||||
it("shows line numbers by default and preserves an explicit opt-out", () => {
|
||||
expect(normalizeEditorSettings({}).showLineNumbers).toBe(true);
|
||||
expect(normalizeEditorSettings({ showLineNumbers: false }).showLineNumbers).toBe(false);
|
||||
expect(normalizeEditorSettings({ showLineNumbers: "false" } as any).showLineNumbers).toBe(true);
|
||||
});
|
||||
|
||||
it("shows the current statement frame by default", () => {
|
||||
expect(normalizeEditorSettings({}).showCurrentStatementFrame).toBe(true);
|
||||
});
|
||||
@@ -726,6 +732,29 @@ describe("settingsStore persisted settings initialization", () => {
|
||||
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ openDataTabsNextToActive: false }));
|
||||
});
|
||||
|
||||
it("loads, persists, and reloads hidden query editor line numbers", async () => {
|
||||
let persistedSettings: Record<string, unknown> = { showLineNumbers: true };
|
||||
const loadEditorSettings = vi.fn(async () => JSON.parse(JSON.stringify(persistedSettings)));
|
||||
const saveEditorSettings = vi.fn(async (settings: Record<string, unknown>) => {
|
||||
persistedSettings = JSON.parse(JSON.stringify(settings));
|
||||
});
|
||||
vi.doMock("@/lib/backend/api", () => ({ loadEditorSettings, saveEditorSettings }));
|
||||
|
||||
const { useSettingsStore } = await import("@/stores/settingsStore");
|
||||
const store = useSettingsStore();
|
||||
await store.initEditorSettings();
|
||||
await store.updateEditorSettingsAndPersist({ showLineNumbers: false });
|
||||
|
||||
expect(store.editorSettings.showLineNumbers).toBe(false);
|
||||
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ showLineNumbers: false }));
|
||||
|
||||
setActivePinia(createPinia());
|
||||
const restartedStore = useSettingsStore();
|
||||
await restartedStore.initEditorSettings();
|
||||
|
||||
expect(restartedStore.editorSettings.showLineNumbers).toBe(false);
|
||||
});
|
||||
|
||||
it("shares concurrent initialization and applies startup changes after saved settings load", async () => {
|
||||
let resolveLoad!: (value: unknown) => void;
|
||||
const loadEditorSettings = vi.fn(
|
||||
|
||||
@@ -525,6 +525,7 @@ export interface EditorSettings {
|
||||
timeoutInheritanceMigrationVersion: number;
|
||||
showExecutionTargetPicker: boolean;
|
||||
showStatementRunButtons: boolean;
|
||||
showLineNumbers: boolean;
|
||||
showCurrentStatementFrame: boolean;
|
||||
showInsertValueHints: boolean;
|
||||
autoAliasTables: boolean;
|
||||
@@ -733,6 +734,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
timeoutInheritanceMigrationVersion: 2,
|
||||
showExecutionTargetPicker: false,
|
||||
showStatementRunButtons: true,
|
||||
showLineNumbers: true,
|
||||
showCurrentStatementFrame: true,
|
||||
showInsertValueHints: true,
|
||||
autoAliasTables: true,
|
||||
@@ -1105,6 +1107,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
||||
: 0,
|
||||
showExecutionTargetPicker: settings.showExecutionTargetPicker ?? DEFAULT_EDITOR_SETTINGS.showExecutionTargetPicker,
|
||||
showStatementRunButtons: typeof settings.showStatementRunButtons === "boolean" ? settings.showStatementRunButtons : DEFAULT_EDITOR_SETTINGS.showStatementRunButtons,
|
||||
showLineNumbers: typeof settings.showLineNumbers === "boolean" ? settings.showLineNumbers : DEFAULT_EDITOR_SETTINGS.showLineNumbers,
|
||||
showCurrentStatementFrame: typeof settings.showCurrentStatementFrame === "boolean" ? settings.showCurrentStatementFrame : DEFAULT_EDITOR_SETTINGS.showCurrentStatementFrame,
|
||||
showInsertValueHints: typeof settings.showInsertValueHints === "boolean" ? settings.showInsertValueHints : DEFAULT_EDITOR_SETTINGS.showInsertValueHints,
|
||||
autoAliasTables: settings.autoAliasTables ?? DEFAULT_EDITOR_SETTINGS.autoAliasTables,
|
||||
@@ -1705,6 +1708,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
||||
if (partial.timeoutInheritanceMigrationVersion !== undefined) editorSettings.value.timeoutInheritanceMigrationVersion = Math.max(0, Math.floor(partial.timeoutInheritanceMigrationVersion));
|
||||
if (partial.showExecutionTargetPicker !== undefined) editorSettings.value.showExecutionTargetPicker = partial.showExecutionTargetPicker;
|
||||
if (partial.showStatementRunButtons !== undefined) editorSettings.value.showStatementRunButtons = partial.showStatementRunButtons === true;
|
||||
if (partial.showLineNumbers !== undefined) editorSettings.value.showLineNumbers = partial.showLineNumbers === true;
|
||||
if (partial.showCurrentStatementFrame !== undefined) editorSettings.value.showCurrentStatementFrame = partial.showCurrentStatementFrame === true;
|
||||
if (partial.showInsertValueHints !== undefined) editorSettings.value.showInsertValueHints = partial.showInsertValueHints === true;
|
||||
if (partial.autoAliasTables !== undefined) editorSettings.value.autoAliasTables = partial.autoAliasTables;
|
||||
|
||||
Reference in new issue
Block a user