feat(editor): configure SQL variable syntax

This commit is contained in:
miracle authored and GitHub committed 2026-07-11 20:45:07 +08:00
1 parent 54632f4c16
commit 1a309b6b63
18 files changed
+450 -24

No files matched your search

@@ -88,6 +88,7 @@ import { normalizeSqlFormatterSettings, type SqlFormatterSettings } from "@/lib/
import { currentExecutableStatementRange, type SqlTextRange } from "@/lib/sql/sqlStatementRanges";
import { executableStatementRangeCacheForDoc, executableStatementRangeStartingAt, type ExecutableStatementRangeCache } from "@/lib/sql/executableStatementRangeCache";
import { EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE, parseTableColumnTemplateFields, TABLE_COLUMN_TEMPLATE_DATABASE_TYPES } from "@/lib/table/tableColumnTemplates";
import { DEFAULT_SQL_VARIABLE_SYNTAX_TOGGLES, normalizeSqlVariableSyntaxOverrides, SQL_VARIABLE_SYNTAX_DATABASE_TYPES, SQL_VARIABLE_SYNTAX_KEYS, SQL_VARIABLE_SYNTAX_TOKENS, type SqlVariableSyntaxOverrides, type SqlVariableSyntaxToggles } from "@/lib/sql/sqlVariableSyntax";
import { buildMcpCodexConfig, buildMcpJsonConfig, buildMcpOpenCodeConfig, buildMcpVsCodeConfig, type McpEnvEntry, type McpLaunchConfig } from "@/lib/mcp/mcpConfigTemplates";
import { isWindows } from "@/lib/backend/platform";
import { combineDataTypeForDatabase, dataTypeLengthInputValue, getDataTypeOptions, getDefaultLengthForType, isDataTypeLengthDisabled, splitDataType } from "@/lib/table/tableStructureEditorState";
@@ -284,6 +285,33 @@ const editInfiniteScroll = ref(settingsStore.editorSettings.infiniteScroll);
const editInfiniteScrollMaxRows = ref(settingsStore.editorSettings.infiniteScrollMaxRows);
const editTableColumnTemplateRows = ref<TableColumnTemplateGridRow[]>(tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields));
const editTableColumnTemplateDatabaseType = ref<DatabaseType>(TABLE_COLUMN_TEMPLATE_DATABASE_TYPES[0] ?? "mysql");
const editSqlVariableSyntaxOverrides = ref<SqlVariableSyntaxOverrides>(normalizeSqlVariableSyntaxOverrides(settingsStore.editorSettings.sqlVariableSyntaxOverrides));
const editSqlVariableSyntaxDatabaseType = ref<DatabaseType>(SQL_VARIABLE_SYNTAX_DATABASE_TYPES[0] ?? "mysql");
function sqlVariableSyntaxToggle(key: keyof SqlVariableSyntaxToggles): boolean {
return editSqlVariableSyntaxOverrides.value[editSqlVariableSyntaxDatabaseType.value]?.[key] ?? true;
}
function setSqlVariableSyntaxToggle(key: keyof SqlVariableSyntaxToggles, value: boolean) {
const dbType = editSqlVariableSyntaxDatabaseType.value;
const merged: SqlVariableSyntaxToggles = {
...DEFAULT_SQL_VARIABLE_SYNTAX_TOGGLES,
...editSqlVariableSyntaxOverrides.value[dbType],
[key]: value,
};
const next: SqlVariableSyntaxOverrides = { ...editSqlVariableSyntaxOverrides.value };
// Keep storage sparse: an all-enabled type has no entry; otherwise persist only the disabled syntaxes.
if (SQL_VARIABLE_SYNTAX_KEYS.every((toggleKey) => merged[toggleKey])) {
delete next[dbType];
} else {
const partial: Partial<SqlVariableSyntaxToggles> = {};
for (const toggleKey of SQL_VARIABLE_SYNTAX_KEYS) {
if (!merged[toggleKey]) partial[toggleKey] = false;
}
next[dbType] = partial;
}
editSqlVariableSyntaxOverrides.value = next;
}
const tableColumnTemplateSectionRef = ref<HTMLElement | null>(null);
const draggedTableColumnTemplateRowId = ref<string | null>(null);
let tableColumnTemplatePointerDragCleanup: (() => void) | null = null;
@@ -393,6 +421,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
updateDownloadSource: editUpdateDownloadSource.value,
toolbarItems: { ...editToolbarItems.value },
snippets: editSnippets.value,
sqlVariableSyntaxOverrides: editSqlVariableSyntaxOverrides.value,
};
}
@@ -664,6 +693,7 @@ function syncEditorSettingsDraftFromStore() {
editUpdateDownloadSource.value = settingsStore.editorSettings.updateDownloadSource;
editToolbarItems.value = { ...settingsStore.editorSettings.toolbarItems };
editSnippets.value = settingsStore.editorSettings.snippets.map(editableSnippet);
editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(settingsStore.editorSettings.sqlVariableSyntaxOverrides);
editEditorSettingsBase.value = editorSettingsDraftFromSettings(settingsStore.editorSettings);
}
@@ -818,6 +848,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editSqlSemanticDiagnosticsEnabled.value = DEFAULT_EDITOR_SETTINGS.sqlSemanticDiagnosticsEnabled;
editConfirmDangerousSqlExecution.value = DEFAULT_EDITOR_SETTINGS.confirmDangerousSqlExecution;
editConfirmUnsavedSqlClose.value = DEFAULT_EDITOR_SETTINGS.confirmUnsavedSqlClose;
editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(DEFAULT_EDITOR_SETTINGS.sqlVariableSyntaxOverrides);
} else if (tab === "formatter") {
editSqlFormatter.value = normalizeSqlFormatterSettings(DEFAULT_EDITOR_SETTINGS.sqlFormatter);
sqlFormatterConfigValid.value = true;
@@ -890,6 +921,7 @@ function resetAllDefaults() {
editSqlSemanticDiagnosticsEnabled.value = DEFAULT_EDITOR_SETTINGS.sqlSemanticDiagnosticsEnabled;
editConfirmDangerousSqlExecution.value = DEFAULT_EDITOR_SETTINGS.confirmDangerousSqlExecution;
editConfirmUnsavedSqlClose.value = DEFAULT_EDITOR_SETTINGS.confirmUnsavedSqlClose;
editSqlVariableSyntaxOverrides.value = normalizeSqlVariableSyntaxOverrides(DEFAULT_EDITOR_SETTINGS.sqlVariableSyntaxOverrides);
editAppLayout.value = DEFAULT_EDITOR_SETTINGS.appLayout;
editShowTrayIcon.value = DEFAULT_DESKTOP_SETTINGS.show_tray_icon;
editQuitOnClose.value = DEFAULT_DESKTOP_SETTINGS.quit_on_close;
@@ -2747,6 +2779,39 @@ onUnmounted(cleanupPreviewEditor);
<Separator />
<div class="space-y-3">
<div class="flex flex-wrap items-start justify-between gap-3">
<div class="space-y-1">
<div class="text-sm font-medium text-muted-foreground">{{ t("settings.sqlVariableSyntax") }}</div>
<p class="text-xs text-muted-foreground">{{ t("settings.sqlVariableSyntaxDescription") }}</p>
</div>
<Select v-model="editSqlVariableSyntaxDatabaseType">
<SelectTrigger class="h-8 w-44 px-2 text-xs">
<SelectValue />
</SelectTrigger>
<SelectContent class="max-h-72">
<SelectItem v-for="dbType in SQL_VARIABLE_SYNTAX_DATABASE_TYPES" :key="dbType" :value="dbType">
{{ dbType }}
</SelectItem>
</SelectContent>
</Select>
</div>
<div class="grid gap-3 md:grid-cols-2">
<div v-for="key in SQL_VARIABLE_SYNTAX_KEYS" :key="key" class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2">
<div class="min-w-0 space-y-1">
<Label :for="`sql-var-syntax-${key}`" class="flex items-center gap-1.5">
<span class="font-mono text-xs text-primary">{{ SQL_VARIABLE_SYNTAX_TOKENS[key] }}</span>
<span>{{ t(`settings.sqlVariableSyntax_${key}`) }}</span>
</Label>
<p class="text-xs text-muted-foreground">{{ t(`settings.sqlVariableSyntax_${key}Description`) }}</p>
</div>
<Switch :id="`sql-var-syntax-${key}`" :model-value="sqlVariableSyntaxToggle(key)" class="mt-0.5 shrink-0" @update:model-value="(value) => setSqlVariableSyntaxToggle(key, value as boolean)" />
</div>
</div>
</div>
<Separator />
<!-- Live Preview -->
<div class="space-y-2">
<Label>{{ t("settings.preview") }}</Label>
@@ -25,6 +25,7 @@ const props = defineProps<{
sql: string;
parameters: SqlParameterDescriptor[];
databaseType?: DatabaseType;
enabledSyntaxes?: SqlParameterSyntax[];
}>();
const emit = defineEmits<{
@@ -46,7 +47,7 @@ const syntaxLabels: Record<SqlParameterSyntax, string> = {
sqlserver: "@name",
};
const resolvedSql = computed(() => substituteSqlParameters(props.sql, values.value, { databaseType: props.databaseType }));
const resolvedSql = computed(() => substituteSqlParameters(props.sql, values.value, { databaseType: props.databaseType, enabledSyntaxes: props.enabledSyntaxes }));
const highlightedSql = computed(() => highlight(resolvedSql.value));
watch(
@@ -20,7 +20,7 @@ const DataGenerateDialog = defineAsyncComponent(() => import("@/components/gener
import { useConnectionStore } from "@/stores/connectionStore";
import { useDialogSources } from "@/composables/useDialogSources";
import type { ConnectionDeepLinkDraft } from "@/lib/connection/connectionDeepLink";
import type { SqlParameterDescriptor } from "@/lib/sql/sqlParameters";
import type { SqlParameterDescriptor, SqlParameterSyntax } from "@/lib/sql/sqlParameters";
import type { ConfigTab } from "@/components/connection/ConnectionDialog.vue";
import type { DatabaseType } from "@/types/database";
@@ -35,6 +35,7 @@ const props = defineProps<{
sqlParameterSourceSql: string;
sqlParameterNames: SqlParameterDescriptor[];
sqlParameterDatabaseType?: DatabaseType;
sqlParameterEnabledSyntaxes?: SqlParameterSyntax[];
}>();
const emit = defineEmits<{
@@ -135,7 +136,16 @@ watch(
@update:suppress-future-prompts="emit('update:suppressDangerConfirm', $event)"
@confirm="emit('dangerConfirm')"
/>
<SqlParameterDialog v-if="showSqlParameterDialog" :open="showSqlParameterDialog" :sql="sqlParameterSourceSql" :parameters="sqlParameterNames" :database-type="sqlParameterDatabaseType" @update:open="emit('update:showSqlParameterDialog', $event)" @execute="emit('sqlParametersConfirm', $event)" />
<SqlParameterDialog
v-if="showSqlParameterDialog"
:open="showSqlParameterDialog"
:sql="sqlParameterSourceSql"
:parameters="sqlParameterNames"
:database-type="sqlParameterDatabaseType"
:enabled-syntaxes="sqlParameterEnabledSyntaxes"
@update:open="emit('update:showSqlParameterDialog', $event)"
@execute="emit('sqlParametersConfirm', $event)"
/>
<DataTransferDialog v-model:open="dialogs.showTransferDialog.value" :prefill-connection-id="dialogs.transferPrefillConnectionId.value" :prefill-database="dialogs.transferPrefillDatabase.value" />
<SchemaDiffDialog v-if="dialogs.showSchemaDiffDialog.value" v-model:open="dialogs.showSchemaDiffDialog.value" :prefill-connection-id="dialogs.schemaDiffPrefillConnectionId.value" :prefill-database="dialogs.schemaDiffPrefillDatabase.value" :prefill-schema="dialogs.schemaDiffPrefillSchema.value" />
<DataCompareDialog