feat(editor): add table ddl hover preview switch

This commit is contained in:
二丫讲梵
2026-09-01 00:20:34 +08:00
committed by GitHub
parent f958bf4602
commit 5eb071bc22
17 changed files with 54 additions and 2 deletions
@@ -520,6 +520,7 @@ const editOpenDataTabsNextToActive = ref(settingsStore.editorSettings.openDataTa
const editPrefillNewQueryWithSelect = ref(settingsStore.editorSettings.prefillNewQueryWithSelect);
const editGenerateSqlIncludeDatabaseName = ref(settingsStore.editorSettings.generateSqlIncludeDatabaseName);
const editFormatSqlOnSqlFileSave = ref(settingsStore.editorSettings.formatSqlOnSqlFileSave);
const editShowTableDdlHoverPreview = ref(settingsStore.editorSettings.showTableDdlHoverPreview);
const editClickTableNavigationTarget = ref<ClickTableNavigationTarget>(settingsStore.editorSettings.clickTableNavigationTarget);
const editUpdateNotificationsEnabled = ref(settingsStore.editorSettings.updateNotificationsEnabled);
const editSidebarHiddenTablePrefixes = ref(settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n"));
@@ -664,6 +665,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
prefillNewQueryWithSelect: editPrefillNewQueryWithSelect.value,
generateSqlIncludeDatabaseName: editGenerateSqlIncludeDatabaseName.value,
formatSqlOnSqlFileSave: editFormatSqlOnSqlFileSave.value,
showTableDdlHoverPreview: editShowTableDdlHoverPreview.value,
updateNotificationsEnabled: editUpdateNotificationsEnabled.value,
sidebarObjectInfoMode: editSidebarObjectInfoMode.value,
sidebarAllowHorizontalScroll: editSidebarAllowHorizontalScroll.value,
@@ -1060,6 +1062,7 @@ function syncEditorSettingsDraftFromStore() {
editPrefillNewQueryWithSelect.value = settingsStore.editorSettings.prefillNewQueryWithSelect;
editGenerateSqlIncludeDatabaseName.value = settingsStore.editorSettings.generateSqlIncludeDatabaseName;
editFormatSqlOnSqlFileSave.value = settingsStore.editorSettings.formatSqlOnSqlFileSave;
editShowTableDdlHoverPreview.value = settingsStore.editorSettings.showTableDdlHoverPreview;
editClickTableNavigationTarget.value = settingsStore.editorSettings.clickTableNavigationTarget;
editUpdateNotificationsEnabled.value = settingsStore.editorSettings.updateNotificationsEnabled;
editSidebarHiddenTablePrefixes.value = settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n");
@@ -1285,6 +1288,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editConfirmUnsavedSqlClose.value = DEFAULT_EDITOR_SETTINGS.confirmUnsavedSqlClose;
editAppCloseUnsavedTabsMode.value = DEFAULT_EDITOR_SETTINGS.appCloseUnsavedTabsMode;
editSavedSqlOpenTargetMode.value = DEFAULT_EDITOR_SETTINGS.savedSqlOpenTargetMode;
editShowTableDdlHoverPreview.value = DEFAULT_EDITOR_SETTINGS.showTableDdlHoverPreview;
editClickTableNavigationTarget.value = DEFAULT_EDITOR_SETTINGS.clickTableNavigationTarget;
editSqlVariableSubstitutionEnabled.value = DEFAULT_EDITOR_SETTINGS.sqlVariableSubstitutionEnabled;
editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(DEFAULT_EDITOR_SETTINGS.sqlVariableSyntaxOverrides);
@@ -1455,6 +1459,7 @@ function resetAllDefaults() {
editPrefillNewQueryWithSelect.value = DEFAULT_EDITOR_SETTINGS.prefillNewQueryWithSelect;
editGenerateSqlIncludeDatabaseName.value = DEFAULT_EDITOR_SETTINGS.generateSqlIncludeDatabaseName;
editFormatSqlOnSqlFileSave.value = DEFAULT_EDITOR_SETTINGS.formatSqlOnSqlFileSave;
editShowTableDdlHoverPreview.value = DEFAULT_EDITOR_SETTINGS.showTableDdlHoverPreview;
editUpdateNotificationsEnabled.value = DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled;
editSidebarObjectInfoMode.value = DEFAULT_EDITOR_SETTINGS.sidebarObjectInfoMode;
editSidebarAllowHorizontalScroll.value = DEFAULT_EDITOR_SETTINGS.sidebarAllowHorizontalScroll;
@@ -4548,6 +4553,16 @@ onUnmounted(() => {
</div>
<Switch id="editor-sql-semantic-diagnostics" :model-value="editSqlSemanticDiagnosticsEnabled" size="sm" @update:model-value="onSqlSemanticDiagnosticsEnabledChange" />
</div>
<div class="flex min-w-0 items-center justify-between gap-2 rounded-md border bg-muted/20 px-2 py-1.5">
<div class="flex min-w-0 items-center gap-1">
<Label for="editor-show-table-ddl-hover-preview" class="truncate text-xs">{{ t("settings.showTableDdlHoverPreview") }}</Label>
<HelpTooltip :label="t('settings.showTableDdlHoverPreview')" trigger-class="[&_svg]:h-3 [&_svg]:w-3" content-class="max-w-64">
{{ t("settings.showTableDdlHoverPreviewDescription") }}
</HelpTooltip>
</div>
<Switch id="editor-show-table-ddl-hover-preview" v-model="editShowTableDdlHoverPreview" size="sm" />
</div>
</div>
<Separator />
@@ -2805,7 +2805,7 @@ async function resolveSqlHoverTooltip(currentView: EditorViewType, pos: number)
cachedTables = mergeCompletionTables(cachedTables, remoteHoverTables);
table = matchTable(qualifiedTableLookup, hoverTables) ?? matchTable(tableLookupName, hoverTables) ?? matchTable(identifier, hoverTables) ?? matchTable(name, hoverTables);
}
if (table && !semanticQualifierIsRowSource && (!qualifier || table.schema?.toLowerCase() === qualifier.toLowerCase() || table.name === name)) {
if (table && settingsStore.editorSettings.showTableDdlHoverPreview && !semanticQualifierIsRowSource && (!qualifier || table.schema?.toLowerCase() === qualifier.toLowerCase() || table.name === name)) {
const hoverDatabase = hoverScope.database;
const hoverSchema = hoverScope.schema ?? table.schema ?? "";
const hoverQualifiedName = [hoverScope.catalog, hoverDatabase, hoverSchema, table.name].filter(Boolean).join(".");
@@ -37,7 +37,7 @@ describe("EditorSettingsDialog live preview placement", () => {
expect(dialogSource.match(/ref="previewRef"/g)).toHaveLength(1);
expect(editorSection).not.toContain('ref="previewRef"');
for (const id of ["editor-show-statement-run-buttons", "editor-show-line-numbers", "editor-show-current-statement-frame", "editor-sql-semantic-diagnostics"]) {
for (const id of ["editor-show-statement-run-buttons", "editor-show-line-numbers", "editor-show-current-statement-frame", "editor-sql-semantic-diagnostics", "editor-show-table-ddl-hover-preview"]) {
expect(editorSection.indexOf(`id="${id}"`)).toBeGreaterThan(previewControls);
expect(editorSection.indexOf(`id="${id}"`)).toBeLessThan(executeMode);
expect(editorSection.match(new RegExp(`id="${id}"`, "g"))).toHaveLength(1);
+2
View File
@@ -6698,6 +6698,8 @@ export default {
formatSqlOnSqlFileSaveDescription: "Automatically format SQL when saving a SQL file, including saved SQL library files and queries saved as .sql files.",
clickTableNavigationTarget: "Ctrl+Click Table Opens DDL",
clickTableNavigationTargetDescription: "When enabled, Ctrl/Cmd+clicking a table name opens the table structure editor (DDL). When disabled, it opens the table data view.",
showTableDdlHoverPreview: "Show table DDL on hover",
showTableDdlHoverPreviewDescription: "Show a DDL preview when hovering over a table name in the SQL editor.",
sqlVariableSyntax: "SQL variable & placeholder substitution",
sqlVariableSyntaxDescription: "Choose which variable and placeholder syntaxes DBX substitutes before running SQL, per database type. All are enabled by default.",
sqlVariableSubstitutionEnabled: "Enabled",
+2
View File
@@ -6336,6 +6336,8 @@ export default withEnglishFallback({
formatSqlOnSqlFileSaveDescription: "Formatea automáticamente el SQL al guardar un archivo SQL, incluidos los archivos guardados en la biblioteca SQL y las consultas guardadas como archivos .sql.",
clickTableNavigationTarget: "Ctrl+Clic abre DDL de tabla",
clickTableNavigationTargetDescription: "Cuando está activado, Ctrl/Cmd+clic en el nombre de la tabla abre el editor de estructura (DDL). Cuando está desactivado, abre la vista de datos.",
showTableDdlHoverPreview: "Mostrar DDL de tabla al pasar el cursor",
showTableDdlHoverPreviewDescription: "Muestra una vista previa del DDL al pasar el cursor sobre el nombre de una tabla en el editor SQL.",
sqlVariableSyntax: "Sustitución de variables y marcadores SQL",
sqlVariableSyntaxDescription: "Elige qué sintaxis de variables y marcadores sustituye DBX antes de ejecutar SQL, por tipo de base de datos. Todas están habilitadas de forma predeterminada.",
sqlVariableSubstitutionEnabled: "Activada",
+2
View File
@@ -6336,6 +6336,8 @@ export default withEnglishFallback({
formatSqlOnSqlFileSaveDescription: "Formatta automaticamente l'SQL durante il salvataggio di un file SQL, inclusi i file salvati nella libreria SQL e le query salvate come file .sql.",
clickTableNavigationTarget: "Ctrl+Clic apre DDL tabella",
clickTableNavigationTargetDescription: "Quando attivato, Ctrl/Cmd+clic sul nome della tabella apre l'editor di struttura (DDL). Quando disattivato, apre la vista dati.",
showTableDdlHoverPreview: "Mostra DDL tabella al passaggio del mouse",
showTableDdlHoverPreviewDescription: "Mostra un'anteprima DDL quando si passa sul nome di una tabella nell'editor SQL.",
sqlVariableSyntax: "Sostituzione di variabili e segnaposto SQL",
sqlVariableSyntaxDescription: "Scegli quali sintassi di variabili e segnaposto DBX sostituisce prima di eseguire SQL, per tipo di database. Tutte abilitate per impostazione predefinita.",
sqlVariableSubstitutionEnabled: "Attiva",
+2
View File
@@ -6351,6 +6351,8 @@ export default withEnglishFallback({
formatSqlOnSqlFileSaveDescription: "SQL ファイル保存時に SQL を自動フォーマットします。SQL ライブラリに保存されたファイルや、新規クエリを .sql ファイルとして保存する場合も含みます。",
clickTableNavigationTarget: "Ctrl+クリックでテーブル構造を開く",
clickTableNavigationTargetDescription: "有効にすると、Ctrl/Cmd+クリックでテーブル構造エディタ(DDL)を開きます。無効の場合はテーブルデータビューを開きます。",
showTableDdlHoverPreview: "ホバー時にテーブル DDL を表示",
showTableDdlHoverPreviewDescription: "SQL エディタでテーブル名にホバーしたときに DDL プレビューを表示します。",
sqlVariableSyntax: "SQL 変数・プレースホルダー置換",
sqlVariableSyntaxDescription: "SQL 実行前に置換する変数・プレースホルダー構文をデータベース種別ごとに選択します。既定ではすべて有効です。",
sqlVariableSubstitutionEnabled: "置換を有効化",
+2
View File
@@ -5558,6 +5558,8 @@ export default withEnglishFallback({
clearSettingsSearch: "설정 검색 지우기",
exitSettingsSearch: "설정으로 돌아가기",
editorTab: "편집기",
showTableDdlHoverPreview: "호버 시 테이블 DDL 표시",
showTableDdlHoverPreviewDescription: "SQL 편집기에서 테이블 이름에 마우스를 올리면 DDL 미리 보기를 표시합니다.",
globalConnectTimeout: "전역 연결 제한 시간 (초)",
globalConnectTimeoutDescription: "전역 설정을 상속하는 연결에 사용됩니다.",
globalQueryTimeout: "전역 쿼리 제한 시간 (초)",
+2
View File
@@ -6338,6 +6338,8 @@ export default withEnglishFallback({
formatSqlOnSqlFileSaveDescription: "Formata automaticamente o SQL ao salvar um arquivo SQL, incluindo arquivos salvos na biblioteca SQL e consultas salvas como arquivos .sql.",
clickTableNavigationTarget: "Ctrl+Clique abre DDL da tabela",
clickTableNavigationTargetDescription: "Quando ativado, Ctrl/Cmd+clicar no nome da tabela abre o editor de estrutura (DDL). Quando desativado, abre a visualização de dados.",
showTableDdlHoverPreview: "Mostrar DDL da tabela ao passar o mouse",
showTableDdlHoverPreviewDescription: "Mostra uma prévia do DDL ao passar o mouse sobre o nome de uma tabela no editor SQL.",
sqlVariableSyntax: "Substituição de variáveis e espaços reservados SQL",
sqlVariableSyntaxDescription: "Escolha quais sintaxes de variáveis e espaços reservados o DBX substitui antes de executar SQL, por tipo de banco de dados. Todas ativadas por padrão.",
sqlVariableSubstitutionEnabled: "Ativada",
+2
View File
@@ -6679,6 +6679,8 @@ export default withEnglishFallback({
formatSqlOnSqlFileSaveDescription: "保存 SQL 文件时自动格式化 SQL,包括通过 SQL 库保存的文件以及将新查询保存为 .sql 文件等场景。",
clickTableNavigationTarget: "Ctrl+点击表名打开 DDL",
clickTableNavigationTargetDescription: "开启后,Ctrl/Cmd+点击表名将打开表结构编辑器(DDL);关闭则打开表数据视图。",
showTableDdlHoverPreview: "悬浮表名时显示 DDL",
showTableDdlHoverPreviewDescription: "在 SQL 编辑器中悬浮表名时显示 DDL 预览。",
sqlVariableSyntax: "SQL 变量与占位符替换",
sqlVariableSyntaxDescription: "按数据库类型选择执行 SQL 前替换哪些变量与占位符语法,默认全部开启。",
sqlVariableSubstitutionEnabled: "启用替换",
+2
View File
@@ -5652,6 +5652,8 @@ export default withEnglishFallback({
formatSqlOnSqlFileSaveDescription: "儲存 SQL 檔案時自動格式化 SQL,包括透過 SQL 庫儲存的檔案,以及將新查詢儲存為 .sql 檔案等場景。",
clickTableNavigationTarget: "Ctrl+點擊表名開啟 DDL",
clickTableNavigationTargetDescription: "開啟後,Ctrl/Cmd+點擊表名將開啟表結構編輯器(DDL);關閉則開啟表資料檢視。",
showTableDdlHoverPreview: "滑過表名時顯示 DDL",
showTableDdlHoverPreviewDescription: "在 SQL 編輯器中滑過表名時顯示 DDL 預覽。",
sqlVariableSyntax: "SQL 變數與佔位符替換",
sqlVariableSyntaxDescription: "依資料庫類型選擇執行 SQL 前替換哪些變數與佔位符語法,預設全部開啟。",
sqlVariableSubstitutionEnabled: "啟用替換",
@@ -4,6 +4,11 @@ import { describe, expect, it } from "vitest";
const queryEditorSource = readFileSync(new URL("../../../components/editor/QueryEditor.vue", import.meta.url), "utf8");
describe("QueryEditor hover DDL copy", () => {
it("gates only the table DDL branch on the preference, keeping column hover", () => {
expect(queryEditorSource).not.toContain("if (!settingsStore.editorSettings.showTableDdlHoverPreview) return null;");
expect(queryEditorSource).toContain("if (table && settingsStore.editorSettings.showTableDdlHoverPreview && !semanticQualifierIsRowSource");
});
it("adds an accessible copy button that preserves SQL whitespace semantics", () => {
expect(queryEditorSource).toContain('copyButton.textContent = t("grid.copyDdl");');
expect(queryEditorSource).toContain('copyButton.setAttribute("aria-label", t("grid.copyDdl"));');
@@ -241,6 +241,7 @@ describe("settings search", () => {
{ titleKey: "settings.prefillNewQueryWithSelect", category: "navigation", targetId: "navigation" },
{ titleKey: "settings.generateSqlIncludeDatabaseName", category: "editor", targetId: "editor" },
{ titleKey: "settings.formatSqlOnSqlFileSave", category: "editor", targetId: "editor" },
{ titleKey: "settings.showTableDdlHoverPreview", category: "editor", targetId: "editor" },
{ titleKey: "settings.sqlFormatterKeywordCase", category: "formatter", targetId: "formatter" },
{ titleKey: "settings.sqlFormatterFunctionCase", category: "formatter", targetId: "formatter" },
{ titleKey: "settings.sqlFormatterDataTypeCase", category: "formatter", targetId: "formatter" },
@@ -73,6 +73,7 @@ export const EDITOR_SETTINGS_DRAFT_KEYS = [
"prefillNewQueryWithSelect",
"generateSqlIncludeDatabaseName",
"formatSqlOnSqlFileSave",
"showTableDdlHoverPreview",
"updateNotificationsEnabled",
"sidebarObjectInfoMode",
"sidebarAllowHorizontalScroll",
@@ -124,6 +124,7 @@ export const SETTINGS_SEARCH_DEFINITIONS: readonly SettingsSearchDefinition[] =
{ id: "editor-prefill-query", category: "navigation", titleKey: "settings.prefillNewQueryWithSelect", descriptionKey: "settings.prefillNewQueryWithSelectDescription", targetId: "navigation" },
{ id: "editor-generate-sql-include-database", category: "editor", titleKey: "settings.generateSqlIncludeDatabaseName", descriptionKey: "settings.generateSqlIncludeDatabaseNameDescription", targetId: "editor" },
{ id: "editor-format-sql-on-sql-file-save", category: "editor", titleKey: "settings.formatSqlOnSqlFileSave", descriptionKey: "settings.formatSqlOnSqlFileSaveDescription", targetId: "editor" },
{ id: "editor-table-ddl-hover-preview", category: "editor", titleKey: "settings.showTableDdlHoverPreview", descriptionKey: "settings.showTableDdlHoverPreviewDescription", targetId: "editor" },
{ id: "editor-diagnostics", category: "editor", titleKey: "settings.sqlSemanticDiagnosticsEnabled", descriptionKey: "settings.sqlSemanticDiagnosticsEnabledDescription", targetId: "editor" },
{ id: "editor-sql-variables", category: "editor", titleKey: "settings.sqlVariableSyntax", descriptionKey: "settings.sqlVariableSyntaxDescription", targetId: "editor" },
{ id: "editor-saved-sql-target", category: "editor", titleKey: "settings.savedSqlOpenTarget", targetId: "editor" },
@@ -506,6 +506,15 @@ describe("normalizeEditorSettings - clickTableNavigationTarget", () => {
});
});
describe("normalizeEditorSettings - showTableDdlHoverPreview", () => {
it("keeps table DDL hover previews enabled unless explicitly disabled", () => {
expect(normalizeEditorSettings({}).showTableDdlHoverPreview).toBe(true);
expect(normalizeEditorSettings({ showTableDdlHoverPreview: false }).showTableDdlHoverPreview).toBe(false);
expect(normalizeEditorSettings({ showTableDdlHoverPreview: true }).showTableDdlHoverPreview).toBe(true);
expect(normalizeEditorSettings({ showTableDdlHoverPreview: "true" } as any).showTableDdlHoverPreview).toBe(true);
});
});
describe("normalizeEditorSettings - completionTriggerMode", () => {
it("defaults completionTriggerMode to positional", () => {
expect(normalizeEditorSettings({}).completionTriggerMode).toBe("positional");
+4
View File
@@ -697,6 +697,7 @@ export interface EditorSettings {
sqlVariableSubstitutionEnabled: boolean;
sqlVariableSyntaxOverrides: SqlVariableSyntaxOverrides;
continueOnErrorOnBatch: boolean;
showTableDdlHoverPreview: boolean;
clickTableNavigationTarget: ClickTableNavigationTarget;
completionTriggerMode: SqlCompletionTriggerMode;
defaultTransactionMode: DefaultTransactionMode;
@@ -913,6 +914,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
sqlVariableSubstitutionEnabled: true,
sqlVariableSyntaxOverrides: {},
continueOnErrorOnBatch: false,
showTableDdlHoverPreview: true,
clickTableNavigationTarget: "data",
completionTriggerMode: "positional",
defaultTransactionMode: "auto",
@@ -1363,6 +1365,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
sqlVariableSubstitutionEnabled: typeof settings.sqlVariableSubstitutionEnabled === "boolean" ? settings.sqlVariableSubstitutionEnabled : DEFAULT_EDITOR_SETTINGS.sqlVariableSubstitutionEnabled,
sqlVariableSyntaxOverrides: normalizeSqlVariableSyntaxOverrides(settings.sqlVariableSyntaxOverrides),
continueOnErrorOnBatch: settings.continueOnErrorOnBatch === true,
showTableDdlHoverPreview: typeof settings.showTableDdlHoverPreview === "boolean" ? settings.showTableDdlHoverPreview : DEFAULT_EDITOR_SETTINGS.showTableDdlHoverPreview,
clickTableNavigationTarget: normalizeClickTableNavigationTarget(settings.clickTableNavigationTarget),
completionTriggerMode: normalizeCompletionTriggerMode(settings.completionTriggerMode),
defaultTransactionMode: normalizeDefaultTransactionMode(settings.defaultTransactionMode),
@@ -1948,6 +1951,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.sqlVariableSubstitutionEnabled !== undefined) editorSettings.value.sqlVariableSubstitutionEnabled = partial.sqlVariableSubstitutionEnabled === true;
if (partial.sqlVariableSyntaxOverrides !== undefined) editorSettings.value.sqlVariableSyntaxOverrides = normalizeSqlVariableSyntaxOverrides(partial.sqlVariableSyntaxOverrides);
if (partial.continueOnErrorOnBatch !== undefined) editorSettings.value.continueOnErrorOnBatch = partial.continueOnErrorOnBatch === true;
if (partial.showTableDdlHoverPreview !== undefined) editorSettings.value.showTableDdlHoverPreview = partial.showTableDdlHoverPreview === true;
if (partial.clickTableNavigationTarget !== undefined) editorSettings.value.clickTableNavigationTarget = normalizeClickTableNavigationTarget(partial.clickTableNavigationTarget);
if (partial.completionTriggerMode !== undefined) editorSettings.value.completionTriggerMode = normalizeCompletionTriggerMode(partial.completionTriggerMode);
if (partial.defaultTransactionMode !== undefined) editorSettings.value.defaultTransactionMode = normalizeDefaultTransactionMode(partial.defaultTransactionMode);