Merge PR #1024
This commit is contained in:
parent
ddfbc2c3b5
commit
634e26e8e7
|
|
@ -830,6 +830,13 @@ async function downloadWebDavSnapshot() {
|
|||
});
|
||||
}
|
||||
|
||||
const oldPassword = ref("");
|
||||
const newPassword = ref("");
|
||||
const confirmNewPassword = ref("");
|
||||
const passwordMessage = ref("");
|
||||
const passwordError = ref(false);
|
||||
const changingPassword = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
async (open) => {
|
||||
|
|
@ -868,13 +875,6 @@ onMounted(() => {
|
|||
void refreshWebDavPasswordStatus();
|
||||
});
|
||||
|
||||
const oldPassword = ref("");
|
||||
const newPassword = ref("");
|
||||
const confirmNewPassword = ref("");
|
||||
const passwordMessage = ref("");
|
||||
const passwordError = ref(false);
|
||||
const changingPassword = ref(false);
|
||||
|
||||
async function changePassword() {
|
||||
if (newPassword.value !== confirmNewPassword.value) {
|
||||
passwordMessage.value = t("auth.passwordMismatch");
|
||||
|
|
|
|||
|
|
@ -1,27 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
|
||||
import { Upload, Download, RotateCcw, WandSparkles, Save } from "@lucide/vue";
|
||||
import { Upload, Download, RotateCcw, WandSparkles, Save, Copy } from "@lucide/vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { copyToClipboard } from "@/lib/clipboard";
|
||||
import {
|
||||
DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
SQL_FORMATTER_CONFIG_FORMATTER,
|
||||
SQL_FORMATTER_CONFIG_VERSION,
|
||||
normalizeSqlFormatterEditorSettings,
|
||||
normalizeSqlFormatterSettings,
|
||||
parseSqlFormatterConfig,
|
||||
serializeSqlFormatterConfig,
|
||||
sqlFormatterPlatformFromNavigator,
|
||||
syncSqlFormatterConfigDraft,
|
||||
validateSqlFormatterEditorSettings,
|
||||
type SqlFormatterCase,
|
||||
type SqlFormatterEditorShortcut,
|
||||
type SqlFormatterEditorShortcutId,
|
||||
type SqlFormatterExpressionWidth,
|
||||
type SqlFormatterIndentStyle,
|
||||
type SqlFormatterLinesBetweenQueries,
|
||||
type SqlFormatterLogicalOperatorNewline,
|
||||
type SqlFormatterOptionSettings,
|
||||
type SqlFormatterParamTypes,
|
||||
type SqlFormatterPlatform,
|
||||
type SqlFormatterSettings,
|
||||
type SqlFormatterTabWidth,
|
||||
} from "@/lib/sqlFormatterConfig";
|
||||
import { createSqlFormatterConfigKeymap, sqlFormatterConfigShortcutRows } from "@/lib/sqlFormatterConfigEditor";
|
||||
import { createSqlFormatterConfigKeymap, sqlFormatterConfigShortcutLabelKey, sqlFormatterConfigShortcutRows } from "@/lib/sqlFormatterConfigEditor";
|
||||
|
||||
type EditorViewInstance = import("@codemirror/view").EditorView;
|
||||
type CodeMirrorModules = {
|
||||
|
|
@ -50,14 +63,21 @@ const jsonEditorRef = ref<HTMLDivElement>();
|
|||
const jsonDraft = ref(serializeSqlFormatterConfig(props.modelValue));
|
||||
const jsonValidationMessage = ref("");
|
||||
const importError = ref("");
|
||||
const advancedConfigError = ref("");
|
||||
const editorShortcutError = ref("");
|
||||
const jsonEditorLoading = ref(false);
|
||||
const jsonEditorReady = ref(false);
|
||||
const jsonEditorLoadError = ref("");
|
||||
const paramTypesDraft = ref("");
|
||||
const focusedAdvancedOption = ref<"paramTypes" | null>(null);
|
||||
|
||||
let cmView: EditorViewInstance | null = null;
|
||||
let cmModules: CodeMirrorModules | null = null;
|
||||
let keymapCompartment: import("@codemirror/state").Compartment | null = null;
|
||||
let lastValidity: boolean | null = null;
|
||||
|
||||
const settings = computed(() => normalizeSqlFormatterSettings(props.modelValue));
|
||||
const shortcutRows = computed(() => sqlFormatterConfigShortcutRows(globalThis.navigator?.platform || ""));
|
||||
const shortcutRows = computed(() => sqlFormatterConfigShortcutRows(globalThis.navigator?.platform || "", settings.value.editor));
|
||||
|
||||
const caseOptions: { value: SqlFormatterCase; labelKey: string }[] = [
|
||||
{ value: "upper", labelKey: "settings.sqlFormatterCaseUpper" },
|
||||
|
|
@ -70,13 +90,29 @@ const logicalOperatorOptions: { value: SqlFormatterLogicalOperatorNewline; label
|
|||
{ value: "after", labelKey: "settings.sqlFormatterLogicalAfter" },
|
||||
];
|
||||
|
||||
const indentStyleOptions: { value: SqlFormatterIndentStyle; labelKey: string }[] = [
|
||||
{ value: "standard", labelKey: "settings.sqlFormatterIndentStyleStandard" },
|
||||
{ value: "tabularLeft", labelKey: "settings.sqlFormatterIndentStyleTabularLeft" },
|
||||
{ value: "tabularRight", labelKey: "settings.sqlFormatterIndentStyleTabularRight" },
|
||||
];
|
||||
|
||||
const shortcutPlatformOptions: { value: SqlFormatterPlatform; labelKey: string }[] = [
|
||||
{ value: "windows", labelKey: "settings.sqlFormatterShortcutWindows" },
|
||||
{ value: "linux", labelKey: "settings.sqlFormatterShortcutLinux" },
|
||||
{ value: "macos", labelKey: "settings.sqlFormatterShortcutMacos" },
|
||||
];
|
||||
const currentShortcutPlatform = computed(() => sqlFormatterPlatformFromNavigator(globalThis.navigator?.platform || ""));
|
||||
const currentShortcutPlatformOption = computed(() => shortcutPlatformOptions.find((option) => option.value === currentShortcutPlatform.value) ?? shortcutPlatformOptions[0]);
|
||||
|
||||
const tabWidthOptions: SqlFormatterTabWidth[] = [2, 4];
|
||||
const expressionWidthOptions: SqlFormatterExpressionWidth[] = [50, 80, 120];
|
||||
const linesBetweenQueriesOptions: SqlFormatterLinesBetweenQueries[] = [0, 1, 2];
|
||||
const sqlFormatterOptionLabelKeys: Record<keyof SqlFormatterSettings, string> = {
|
||||
const sqlFormatterOptionLabelKeys: Record<keyof SqlFormatterOptionSettings, string> = {
|
||||
keywordCase: "settings.sqlFormatterKeywordCase",
|
||||
dataTypeCase: "settings.sqlFormatterDataTypeCase",
|
||||
functionCase: "settings.sqlFormatterFunctionCase",
|
||||
identifierCase: "settings.sqlFormatterIdentifierCase",
|
||||
indentStyle: "settings.sqlFormatterIndentStyle",
|
||||
useTabs: "settings.sqlFormatterIndent",
|
||||
tabWidth: "settings.sqlFormatterTabWidth",
|
||||
logicalOperatorNewline: "settings.sqlFormatterLogicalOperatorNewline",
|
||||
|
|
@ -84,13 +120,20 @@ const sqlFormatterOptionLabelKeys: Record<keyof SqlFormatterSettings, string> =
|
|||
linesBetweenQueries: "settings.sqlFormatterLinesBetweenQueries",
|
||||
denseOperators: "settings.sqlFormatterDenseOperators",
|
||||
newlineBeforeSemicolon: "settings.sqlFormatterNewlineBeforeSemicolon",
|
||||
paramTypes: "settings.sqlFormatterParamTypes",
|
||||
};
|
||||
const sqlFormatterConfigErrorKeys: Record<string, string> = {
|
||||
"Invalid JSON.": "settings.sqlFormatterConfigErrorInvalidJson",
|
||||
"Config must be a JSON object.": "settings.sqlFormatterConfigErrorObject",
|
||||
"Unsupported config version.": "settings.sqlFormatterConfigErrorVersion",
|
||||
"Unsupported formatter.": "settings.sqlFormatterConfigErrorFormatter",
|
||||
"Unsupported formatter option: params.": "settings.sqlFormatterConfigErrorUnsupportedParams",
|
||||
"Config options must be a JSON object.": "settings.sqlFormatterConfigErrorOptionsObject",
|
||||
"Config editor must be a JSON object.": "settings.sqlFormatterConfigErrorEditorObject",
|
||||
"Unsupported editor scope.": "settings.sqlFormatterConfigErrorEditorScope",
|
||||
"Invalid editor platforms.": "settings.sqlFormatterConfigErrorEditorPlatforms",
|
||||
"Config editor shortcuts must be an array.": "settings.sqlFormatterConfigErrorEditorShortcutsArray",
|
||||
"Invalid editor shortcut value.": "settings.sqlFormatterConfigErrorEditorShortcutValue",
|
||||
};
|
||||
|
||||
function emitValidity(value: boolean) {
|
||||
|
|
@ -118,12 +161,27 @@ function localizeSqlFormatterConfigError(message: string): string {
|
|||
|
||||
const invalidOption = message.match(/^Invalid formatter option value: (.+)\.$/);
|
||||
if (invalidOption?.[1]) {
|
||||
const labelKey = sqlFormatterOptionLabelKeys[invalidOption[1] as keyof SqlFormatterSettings];
|
||||
const labelKey = sqlFormatterOptionLabelKeys[invalidOption[1] as keyof SqlFormatterOptionSettings];
|
||||
if (labelKey) {
|
||||
return t("settings.sqlFormatterConfigErrorInvalidOptionValue", { option: t(labelKey) });
|
||||
}
|
||||
}
|
||||
|
||||
const unknownShortcut = message.match(/^Unknown editor shortcut: (.+)\.$/);
|
||||
if (unknownShortcut?.[1]) {
|
||||
return t("settings.sqlFormatterConfigErrorUnknownEditorShortcut", { shortcut: unknownShortcut[1] });
|
||||
}
|
||||
|
||||
const invalidShortcut = message.match(/^Invalid editor shortcut value: (.+)\.$/);
|
||||
if (invalidShortcut?.[1]) {
|
||||
return t("settings.sqlFormatterConfigErrorInvalidEditorShortcut", { shortcut: invalidShortcut[1] });
|
||||
}
|
||||
|
||||
const duplicateShortcut = message.match(/^Duplicate editor shortcut: (.+)\.$/);
|
||||
if (duplicateShortcut?.[1]) {
|
||||
return t("settings.sqlFormatterConfigErrorDuplicateEditorShortcut", { shortcut: duplicateShortcut[1] });
|
||||
}
|
||||
|
||||
return t("settings.sqlFormatterConfigErrorInvalidConfig");
|
||||
}
|
||||
|
||||
|
|
@ -144,9 +202,17 @@ function syncJsonDraft(text = jsonDraft.value): boolean {
|
|||
|
||||
function updateSettings(next: unknown) {
|
||||
importError.value = "";
|
||||
advancedConfigError.value = "";
|
||||
emit("update:modelValue", normalizeSqlFormatterSettings(next));
|
||||
}
|
||||
|
||||
function validateEditorShortcuts(value = settings.value.editor): boolean {
|
||||
const result = validateSqlFormatterEditorSettings(value);
|
||||
editorShortcutError.value = result.ok ? "" : localizeSqlFormatterConfigError(result.message);
|
||||
emitValidity(activeMode.value === "form" ? result.ok : !jsonValidationMessage.value);
|
||||
return result.ok;
|
||||
}
|
||||
|
||||
function updateOption<K extends keyof SqlFormatterSettings>(key: K, value: SqlFormatterSettings[K]) {
|
||||
updateSettings({ ...settings.value, [key]: value });
|
||||
}
|
||||
|
|
@ -155,6 +221,14 @@ function onCaseOption(key: "keywordCase" | "functionCase" | "dataTypeCase", valu
|
|||
if (value === "upper" || value === "lower" || value === "preserve") updateOption(key, value);
|
||||
}
|
||||
|
||||
function onIdentifierCase(value: any) {
|
||||
if (value === "upper" || value === "lower" || value === "preserve") updateOption("identifierCase", value);
|
||||
}
|
||||
|
||||
function onIndentStyle(value: any) {
|
||||
if (value === "standard" || value === "tabularLeft" || value === "tabularRight") updateOption("indentStyle", value);
|
||||
}
|
||||
|
||||
function onLogicalOperatorNewline(value: any) {
|
||||
if (value === "before" || value === "after") updateOption("logicalOperatorNewline", value);
|
||||
}
|
||||
|
|
@ -178,6 +252,93 @@ function restoreDefaults() {
|
|||
updateSettings(DEFAULT_SQL_FORMATTER_SETTINGS);
|
||||
}
|
||||
|
||||
function stringifyAdvancedOption(value: SqlFormatterParamTypes | null): string {
|
||||
return JSON.stringify(value, null, 2);
|
||||
}
|
||||
|
||||
function setAdvancedDraft(value: SqlFormatterParamTypes | null) {
|
||||
paramTypesDraft.value = stringifyAdvancedOption(value);
|
||||
}
|
||||
|
||||
function onAdvancedJsonInput(event: Event) {
|
||||
paramTypesDraft.value = (event.target as HTMLTextAreaElement).value;
|
||||
}
|
||||
|
||||
function onJsonDraftTextareaInput(event: Event) {
|
||||
const text = (event.target as HTMLTextAreaElement).value;
|
||||
jsonDraft.value = text;
|
||||
syncJsonDraft(text);
|
||||
}
|
||||
|
||||
function onAdvancedJsonFocus() {
|
||||
focusedAdvancedOption.value = "paramTypes";
|
||||
}
|
||||
|
||||
function onAdvancedJsonBlur() {
|
||||
focusedAdvancedOption.value = null;
|
||||
applyAdvancedJsonOption();
|
||||
}
|
||||
|
||||
function applyAdvancedJsonOption() {
|
||||
let parsed: unknown = null;
|
||||
const draft = paramTypesDraft.value.trim();
|
||||
if (draft) {
|
||||
try {
|
||||
parsed = JSON.parse(draft);
|
||||
} catch {
|
||||
advancedConfigError.value = localizeSqlFormatterConfigError("Invalid JSON.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const result = parseSqlFormatterConfig(
|
||||
JSON.stringify({
|
||||
version: SQL_FORMATTER_CONFIG_VERSION,
|
||||
formatter: SQL_FORMATTER_CONFIG_FORMATTER,
|
||||
options: { paramTypes: parsed },
|
||||
}),
|
||||
);
|
||||
if (!result.ok) {
|
||||
advancedConfigError.value = localizeSqlFormatterConfigError(result.message);
|
||||
return;
|
||||
}
|
||||
|
||||
updateOption("paramTypes", result.settings.paramTypes);
|
||||
setAdvancedDraft(result.settings.paramTypes);
|
||||
advancedConfigError.value = "";
|
||||
}
|
||||
|
||||
function updateEditorShortcut(id: SqlFormatterEditorShortcutId, updater: (shortcut: SqlFormatterEditorShortcut) => SqlFormatterEditorShortcut) {
|
||||
const editor = normalizeSqlFormatterEditorSettings(settings.value.editor);
|
||||
const next = {
|
||||
...settings.value,
|
||||
editor: {
|
||||
...editor,
|
||||
shortcuts: editor.shortcuts.map((shortcut) => (shortcut.id === id ? updater({ ...shortcut, keys: { ...shortcut.keys } }) : shortcut)),
|
||||
},
|
||||
};
|
||||
updateSettings(next);
|
||||
validateEditorShortcuts(next.editor);
|
||||
}
|
||||
|
||||
function updateEditorShortcutEnabled(id: SqlFormatterEditorShortcutId, enabled: boolean) {
|
||||
updateEditorShortcut(id, (shortcut) => ({ ...shortcut, enabled }));
|
||||
}
|
||||
|
||||
function updateEditorShortcutKey(id: SqlFormatterEditorShortcutId, platform: SqlFormatterPlatform, value: string) {
|
||||
updateEditorShortcut(id, (shortcut) => ({
|
||||
...shortcut,
|
||||
keys: {
|
||||
...shortcut.keys,
|
||||
[platform]: value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
function shortcutLabelKey(id: SqlFormatterEditorShortcutId): string {
|
||||
return sqlFormatterConfigShortcutLabelKey(id);
|
||||
}
|
||||
|
||||
function importConfig() {
|
||||
importError.value = "";
|
||||
fileInputRef.value?.click();
|
||||
|
|
@ -215,6 +376,15 @@ function exportConfig() {
|
|||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
async function copyJsonDraft() {
|
||||
try {
|
||||
await copyToClipboard(cmView?.state.doc.toString() ?? jsonDraft.value);
|
||||
toast(t("settings.sqlFormatterCopyJsonSuccess"));
|
||||
} catch (e: any) {
|
||||
toast(t("settings.sqlFormatterCopyJsonFailed", { message: e?.message || String(e) }), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
function applyJsonDraft(): boolean {
|
||||
const result = parseSqlFormatterConfig(jsonDraft.value);
|
||||
if (!result.ok) {
|
||||
|
|
@ -255,37 +425,59 @@ async function loadCodeMirrorModules(): Promise<CodeMirrorModules> {
|
|||
function destroyJsonEditor() {
|
||||
cmView?.destroy();
|
||||
cmView = null;
|
||||
keymapCompartment = null;
|
||||
jsonEditorReady.value = false;
|
||||
jsonEditorLoadError.value = "";
|
||||
}
|
||||
|
||||
function jsonEditorKeymapExtension(modules: CodeMirrorModules) {
|
||||
const { keymap } = modules.view;
|
||||
const commands = modules.commands;
|
||||
const search = modules.search;
|
||||
const customKeymap = createSqlFormatterConfigKeymap(
|
||||
{
|
||||
indentMore: commands.indentMore,
|
||||
indentLess: commands.indentLess,
|
||||
copyLineDown: commands.copyLineDown,
|
||||
copyLineUp: commands.copyLineUp,
|
||||
deleteLine: commands.deleteLine,
|
||||
moveLineUp: commands.moveLineUp,
|
||||
moveLineDown: commands.moveLineDown,
|
||||
undo: commands.undo,
|
||||
redo: commands.redo,
|
||||
selectAll: commands.selectAll,
|
||||
openSearchPanel: search.openSearchPanel,
|
||||
},
|
||||
{
|
||||
apply: applyJsonDraft,
|
||||
formatJson: formatJsonDraft,
|
||||
},
|
||||
settings.value.editor,
|
||||
);
|
||||
return keymap.of([...customKeymap, ...search.searchKeymap, ...commands.historyKeymap, ...commands.defaultKeymap]);
|
||||
}
|
||||
|
||||
function reconfigureJsonEditorKeymap() {
|
||||
if (!cmView || !cmModules || !keymapCompartment) return;
|
||||
cmView.dispatch({
|
||||
effects: keymapCompartment.reconfigure(jsonEditorKeymapExtension(cmModules)),
|
||||
});
|
||||
}
|
||||
|
||||
async function initJsonEditor() {
|
||||
if (cmView || !jsonEditorRef.value) return;
|
||||
jsonEditorLoading.value = true;
|
||||
jsonEditorLoadError.value = "";
|
||||
try {
|
||||
const modules = await loadCodeMirrorModules();
|
||||
if (activeMode.value !== "json" || cmView || !jsonEditorRef.value) return;
|
||||
|
||||
const { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter } = modules.view;
|
||||
const { EditorState } = modules.state;
|
||||
const { EditorView, lineNumbers, highlightActiveLine, highlightActiveLineGutter } = modules.view;
|
||||
const { EditorState, Compartment } = modules.state;
|
||||
const { json } = modules.langJson;
|
||||
const commands = modules.commands;
|
||||
const search = modules.search;
|
||||
|
||||
const customKeymap = createSqlFormatterConfigKeymap(
|
||||
{
|
||||
indentMore: commands.indentMore,
|
||||
indentLess: commands.indentLess,
|
||||
copyLineDown: commands.copyLineDown,
|
||||
copyLineUp: commands.copyLineUp,
|
||||
deleteLine: commands.deleteLine,
|
||||
moveLineUp: commands.moveLineUp,
|
||||
moveLineDown: commands.moveLineDown,
|
||||
openSearchPanel: search.openSearchPanel,
|
||||
},
|
||||
{
|
||||
apply: applyJsonDraft,
|
||||
formatJson: formatJsonDraft,
|
||||
},
|
||||
);
|
||||
keymapCompartment = new Compartment();
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: jsonDraft.value,
|
||||
|
|
@ -296,7 +488,7 @@ async function initJsonEditor() {
|
|||
commands.history(),
|
||||
search.search({ top: true }),
|
||||
json(),
|
||||
keymap.of([...customKeymap, ...search.searchKeymap, ...commands.historyKeymap, ...commands.defaultKeymap]),
|
||||
keymapCompartment.of(jsonEditorKeymapExtension(modules)),
|
||||
EditorView.lineWrapping,
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (!update.docChanged) return;
|
||||
|
|
@ -333,11 +525,32 @@ async function initJsonEditor() {
|
|||
});
|
||||
|
||||
cmView = new EditorView({ state, parent: jsonEditorRef.value });
|
||||
jsonEditorReady.value = true;
|
||||
} catch (e: any) {
|
||||
jsonEditorReady.value = false;
|
||||
jsonEditorLoadError.value = e?.message || String(e);
|
||||
} finally {
|
||||
jsonEditorLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => settings.value.paramTypes,
|
||||
(value) => {
|
||||
if (focusedAdvancedOption.value !== "paramTypes") paramTypesDraft.value = stringifyAdvancedOption(value);
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => settings.value.editor,
|
||||
(value) => {
|
||||
validateEditorShortcuts(value);
|
||||
if (activeMode.value === "json") reconfigureJsonEditorKeymap();
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
|
|
@ -365,7 +578,7 @@ watch(
|
|||
await initJsonEditor();
|
||||
return;
|
||||
}
|
||||
emitValidity(true);
|
||||
validateEditorShortcuts();
|
||||
destroyJsonEditor();
|
||||
},
|
||||
{ immediate: true },
|
||||
|
|
@ -405,7 +618,7 @@ onBeforeUnmount(() => {
|
|||
</TabsList>
|
||||
|
||||
<TabsContent value="form" class="m-0 flex flex-col gap-4 pt-2">
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div class="grid gap-4 md:grid-cols-4">
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.sqlFormatterKeywordCase") }}</Label>
|
||||
<Select :model-value="settings.keywordCase" @update:model-value="(value: any) => onCaseOption('keywordCase', value)">
|
||||
|
|
@ -447,9 +660,23 @@ onBeforeUnmount(() => {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.sqlFormatterIdentifierCase") }}</Label>
|
||||
<Select :model-value="settings.identifierCase" @update:model-value="onIdentifierCase">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="option in caseOptions" :key="option.value" :value="option.value">
|
||||
{{ t(option.labelKey) }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-[minmax(0,1fr)_10rem]">
|
||||
<div class="grid gap-4 md:grid-cols-[minmax(0,1fr)_10rem_12rem]">
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.sqlFormatterIndent") }}</Label>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
|
|
@ -475,6 +702,20 @@ onBeforeUnmount(() => {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.sqlFormatterIndentStyle") }}</Label>
|
||||
<Select :model-value="settings.indentStyle" @update:model-value="onIndentStyle">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="option in indentStyleOptions" :key="option.value" :value="option.value">
|
||||
{{ t(option.labelKey) }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
|
|
@ -534,6 +775,46 @@ onBeforeUnmount(() => {
|
|||
<Switch id="sql-formatter-newline-before-semicolon" :model-value="settings.newlineBeforeSemicolon" @update:model-value="(value: boolean) => updateOption('newlineBeforeSemicolon', value)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 rounded-md border border-border/70 bg-muted/10 p-3">
|
||||
<div class="text-sm font-medium">{{ t("settings.sqlFormatterAdvancedOptions") }}</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="sql-formatter-param-types">{{ t("settings.sqlFormatterParamTypes") }}</Label>
|
||||
<textarea
|
||||
id="sql-formatter-param-types"
|
||||
:value="paramTypesDraft"
|
||||
spellcheck="false"
|
||||
class="min-h-28 w-full resize-y rounded-md border border-input bg-background px-3 py-2 font-mono text-xs outline-none transition-colors focus:border-ring focus:ring-1 focus:ring-ring/40"
|
||||
@input="onAdvancedJsonInput"
|
||||
@focus="onAdvancedJsonFocus"
|
||||
@blur="onAdvancedJsonBlur"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="advancedConfigError" class="rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
||||
{{ advancedConfigError }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 rounded-md border border-border/70 bg-muted/10 p-3">
|
||||
<div class="text-sm font-medium">{{ t("settings.sqlFormatterEditorShortcuts") }}</div>
|
||||
<div class="overflow-x-auto rounded-md border bg-background">
|
||||
<div class="grid min-w-[420px] grid-cols-[minmax(10rem,1fr)_5rem_minmax(12rem,1.2fr)] items-center gap-2 border-b bg-muted/30 px-3 py-2 text-xs font-medium text-muted-foreground">
|
||||
<span>{{ t("settings.sqlFormatterShortcutAction") }}</span>
|
||||
<span class="text-center">{{ t("settings.sqlFormatterShortcutEnabled") }}</span>
|
||||
<span>{{ t(currentShortcutPlatformOption.labelKey) }}</span>
|
||||
</div>
|
||||
<div v-for="shortcut in settings.editor.shortcuts" :key="shortcut.id" class="grid min-w-[420px] grid-cols-[minmax(10rem,1fr)_5rem_minmax(12rem,1.2fr)] items-center gap-2 border-b px-3 py-2 text-xs last:border-b-0">
|
||||
<span class="min-w-0 truncate text-muted-foreground">{{ t(shortcutLabelKey(shortcut.id)) }}</span>
|
||||
<div class="flex justify-center">
|
||||
<Switch :model-value="shortcut.enabled" @update:model-value="(value: boolean) => updateEditorShortcutEnabled(shortcut.id, value)" />
|
||||
</div>
|
||||
<Input :model-value="shortcut.keys[currentShortcutPlatform]" class="h-8 font-mono text-xs" :disabled="!shortcut.enabled" @update:model-value="(value: string | number) => updateEditorShortcutKey(shortcut.id, currentShortcutPlatform, String(value))" />
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="editorShortcutError" class="rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
||||
{{ editorShortcutError }}
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="json" class="m-0 flex min-h-0 flex-col gap-3 pt-2">
|
||||
|
|
@ -542,6 +823,10 @@ onBeforeUnmount(() => {
|
|||
<WandSparkles class="mr-2 h-4 w-4" />
|
||||
{{ t("settings.sqlFormatterShortcutFormatJson") }}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" @click="copyJsonDraft">
|
||||
<Copy class="mr-2 h-4 w-4" />
|
||||
{{ t("settings.sqlFormatterCopyJson") }}
|
||||
</Button>
|
||||
<Button type="button" size="sm" :disabled="!!jsonValidationMessage" @click="applyJsonDraft">
|
||||
<Save class="mr-2 h-4 w-4" />
|
||||
{{ t("settings.sqlFormatterShortcutApply") }}
|
||||
|
|
@ -549,7 +834,19 @@ onBeforeUnmount(() => {
|
|||
<span v-if="jsonEditorLoading" class="text-xs text-muted-foreground">{{ t("common.loading") }}</span>
|
||||
</div>
|
||||
|
||||
<div ref="jsonEditorRef" class="min-h-[260px]" />
|
||||
<div ref="jsonEditorRef" v-show="jsonEditorReady || jsonEditorLoading" class="min-h-[320px]" />
|
||||
|
||||
<textarea
|
||||
v-if="!jsonEditorReady && !jsonEditorLoading"
|
||||
:value="jsonDraft"
|
||||
spellcheck="false"
|
||||
class="min-h-[320px] w-full resize-y rounded-md border border-input bg-background px-3 py-2 font-mono text-xs outline-none transition-colors focus:border-ring focus:ring-1 focus:ring-ring/40"
|
||||
@input="onJsonDraftTextareaInput"
|
||||
/>
|
||||
|
||||
<p v-if="jsonEditorLoadError" class="rounded-md border border-amber-300/60 bg-amber-50 px-3 py-2 text-xs text-amber-800 dark:border-amber-400/40 dark:bg-amber-950/30 dark:text-amber-200">
|
||||
{{ t("settings.sqlFormatterJsonEditorLoadFailed", { message: jsonEditorLoadError }) }}
|
||||
</p>
|
||||
|
||||
<p v-if="jsonValidationMessage" class="rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
||||
{{ jsonValidationMessage }}
|
||||
|
|
|
|||
|
|
@ -1731,12 +1731,17 @@
|
|||
sqlFormatterImport: "Import config",
|
||||
sqlFormatterExport: "Export config",
|
||||
sqlFormatterImportSuccess: "Formatter config imported.",
|
||||
sqlFormatterCopyJson: "Copy JSON",
|
||||
sqlFormatterCopyJsonSuccess: "JSON copied.",
|
||||
sqlFormatterCopyJsonFailed: "Copy failed: {message}",
|
||||
sqlFormatterJsonEditorLoadFailed: "JSON editor failed to load. Switched to a basic input: {message}",
|
||||
sqlFormatterRestoreDefaults: "Restore defaults",
|
||||
sqlFormatterFormMode: "Form",
|
||||
sqlFormatterJsonMode: "JSON",
|
||||
sqlFormatterKeywordCase: "Keyword case",
|
||||
sqlFormatterFunctionCase: "Function case",
|
||||
sqlFormatterDataTypeCase: "Data type case",
|
||||
sqlFormatterIdentifierCase: "Identifier case",
|
||||
sqlFormatterCaseUpper: "Uppercase",
|
||||
sqlFormatterCaseLower: "Lowercase",
|
||||
sqlFormatterCasePreserve: "Preserve",
|
||||
|
|
@ -1744,6 +1749,10 @@
|
|||
sqlFormatterIndentSpaces: "Spaces",
|
||||
sqlFormatterIndentTabs: "Tabs",
|
||||
sqlFormatterTabWidth: "Tab width",
|
||||
sqlFormatterIndentStyle: "Indent style",
|
||||
sqlFormatterIndentStyleStandard: "Standard",
|
||||
sqlFormatterIndentStyleTabularLeft: "Tabular left",
|
||||
sqlFormatterIndentStyleTabularRight: "Tabular right",
|
||||
sqlFormatterLogicalOperatorNewline: "Logical operator newline",
|
||||
sqlFormatterLogicalBefore: "Before operator",
|
||||
sqlFormatterLogicalAfter: "After operator",
|
||||
|
|
@ -1751,6 +1760,15 @@
|
|||
sqlFormatterLinesBetweenQueries: "Lines between queries",
|
||||
sqlFormatterDenseOperators: "Dense operators",
|
||||
sqlFormatterNewlineBeforeSemicolon: "Semicolon on new line",
|
||||
sqlFormatterAdvancedOptions: "Advanced options",
|
||||
sqlFormatterParams: "Params",
|
||||
sqlFormatterParamTypes: "Param types",
|
||||
sqlFormatterEditorShortcuts: "JSON editor shortcuts",
|
||||
sqlFormatterShortcutAction: "Action",
|
||||
sqlFormatterShortcutEnabled: "Enabled",
|
||||
sqlFormatterShortcutWindows: "Windows",
|
||||
sqlFormatterShortcutLinux: "Linux",
|
||||
sqlFormatterShortcutMacos: "macOS",
|
||||
sqlFormatterShortcutFind: "Find",
|
||||
sqlFormatterShortcutReplace: "Replace",
|
||||
sqlFormatterShortcutIndentMore: "Indent more",
|
||||
|
|
@ -1758,16 +1776,32 @@
|
|||
sqlFormatterShortcutDuplicateLine: "Duplicate current line",
|
||||
sqlFormatterShortcutDeleteLine: "Delete current line",
|
||||
sqlFormatterShortcutMoveLine: "Move line",
|
||||
sqlFormatterShortcutMoveLineUp: "Move line up",
|
||||
sqlFormatterShortcutMoveLineDown: "Move line down",
|
||||
sqlFormatterShortcutCopyLine: "Copy line",
|
||||
sqlFormatterShortcutCopyLineUp: "Copy line up",
|
||||
sqlFormatterShortcutCopyLineDown: "Copy line down",
|
||||
sqlFormatterShortcutUndo: "Undo",
|
||||
sqlFormatterShortcutRedo: "Redo",
|
||||
sqlFormatterShortcutSelectAll: "Select all",
|
||||
sqlFormatterShortcutFormatJson: "Format JSON",
|
||||
sqlFormatterShortcutApply: "Apply config",
|
||||
sqlFormatterConfigErrorInvalidJson: "Invalid JSON.",
|
||||
sqlFormatterConfigErrorObject: "Config must be a JSON object.",
|
||||
sqlFormatterConfigErrorVersion: "Unsupported config version.",
|
||||
sqlFormatterConfigErrorFormatter: "Unsupported formatter.",
|
||||
sqlFormatterConfigErrorUnsupportedParams: "The params replacement option is not supported. SQL formatting will not replace placeholders.",
|
||||
sqlFormatterConfigErrorOptionsObject: "Config options must be a JSON object.",
|
||||
sqlFormatterConfigErrorEditorObject: "Editor config must be a JSON object.",
|
||||
sqlFormatterConfigErrorEditorScope: "Unsupported editor scope.",
|
||||
sqlFormatterConfigErrorEditorPlatforms: "Invalid editor platforms.",
|
||||
sqlFormatterConfigErrorEditorShortcutsArray: "Editor shortcuts must be an array.",
|
||||
sqlFormatterConfigErrorEditorShortcutValue: "Invalid editor shortcut.",
|
||||
sqlFormatterConfigErrorUnknownOption: "Unknown formatter option: {option}.",
|
||||
sqlFormatterConfigErrorInvalidOptionValue: "Invalid value for {option}.",
|
||||
sqlFormatterConfigErrorUnknownEditorShortcut: "Unknown editor shortcut: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidEditorShortcut: "Invalid editor shortcut: {shortcut}.",
|
||||
sqlFormatterConfigErrorDuplicateEditorShortcut: "Duplicate editor shortcut: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidConfig: "Invalid formatter config.",
|
||||
appearanceTab: "Appearance",
|
||||
navigationTab: "Navigation",
|
||||
|
|
|
|||
|
|
@ -1510,12 +1510,17 @@
|
|||
sqlFormatterImport: "Importar configuración",
|
||||
sqlFormatterExport: "Exportar configuración",
|
||||
sqlFormatterImportSuccess: "Configuración del formateador importada.",
|
||||
sqlFormatterCopyJson: "Copiar JSON",
|
||||
sqlFormatterCopyJsonSuccess: "JSON copiado.",
|
||||
sqlFormatterCopyJsonFailed: "Error al copiar: {message}",
|
||||
sqlFormatterJsonEditorLoadFailed: "No se pudo cargar el editor JSON. Se cambió a una entrada básica: {message}",
|
||||
sqlFormatterRestoreDefaults: "Restaurar valores por defecto",
|
||||
sqlFormatterFormMode: "Formulario",
|
||||
sqlFormatterJsonMode: "JSON",
|
||||
sqlFormatterKeywordCase: "Mayúsculas/minúsculas de palabras clave",
|
||||
sqlFormatterFunctionCase: "Mayúsculas/minúsculas de funciones",
|
||||
sqlFormatterDataTypeCase: "Mayúsculas/minúsculas de tipos de datos",
|
||||
sqlFormatterIdentifierCase: "Mayúsculas/minúsculas de identificadores",
|
||||
sqlFormatterCaseUpper: "Mayúsculas",
|
||||
sqlFormatterCaseLower: "Minúsculas",
|
||||
sqlFormatterCasePreserve: "Conservar",
|
||||
|
|
@ -1523,6 +1528,10 @@
|
|||
sqlFormatterIndentSpaces: "Espacios",
|
||||
sqlFormatterIndentTabs: "Tabulaciones",
|
||||
sqlFormatterTabWidth: "Ancho de tabulación",
|
||||
sqlFormatterIndentStyle: "Estilo de sangría",
|
||||
sqlFormatterIndentStyleStandard: "Estándar",
|
||||
sqlFormatterIndentStyleTabularLeft: "Tabular izquierda",
|
||||
sqlFormatterIndentStyleTabularRight: "Tabular derecha",
|
||||
sqlFormatterLogicalOperatorNewline: "Salto de línea en operador lógico",
|
||||
sqlFormatterLogicalBefore: "Antes del operador",
|
||||
sqlFormatterLogicalAfter: "Después del operador",
|
||||
|
|
@ -1530,6 +1539,15 @@
|
|||
sqlFormatterLinesBetweenQueries: "Líneas entre consultas",
|
||||
sqlFormatterDenseOperators: "Operadores compactos",
|
||||
sqlFormatterNewlineBeforeSemicolon: "Punto y coma en nueva línea",
|
||||
sqlFormatterAdvancedOptions: "Opciones avanzadas",
|
||||
sqlFormatterParams: "Parámetros",
|
||||
sqlFormatterParamTypes: "Tipos de parámetro",
|
||||
sqlFormatterEditorShortcuts: "Atajos del editor JSON",
|
||||
sqlFormatterShortcutAction: "Acción",
|
||||
sqlFormatterShortcutEnabled: "Activo",
|
||||
sqlFormatterShortcutWindows: "Windows",
|
||||
sqlFormatterShortcutLinux: "Linux",
|
||||
sqlFormatterShortcutMacos: "macOS",
|
||||
sqlFormatterShortcutFind: "Buscar",
|
||||
sqlFormatterShortcutReplace: "Reemplazar",
|
||||
sqlFormatterShortcutIndentMore: "Aumentar sangría",
|
||||
|
|
@ -1537,16 +1555,32 @@
|
|||
sqlFormatterShortcutDuplicateLine: "Duplicar línea actual",
|
||||
sqlFormatterShortcutDeleteLine: "Eliminar línea actual",
|
||||
sqlFormatterShortcutMoveLine: "Mover línea",
|
||||
sqlFormatterShortcutMoveLineUp: "Mover línea arriba",
|
||||
sqlFormatterShortcutMoveLineDown: "Mover línea abajo",
|
||||
sqlFormatterShortcutCopyLine: "Copiar línea",
|
||||
sqlFormatterShortcutCopyLineUp: "Copiar línea arriba",
|
||||
sqlFormatterShortcutCopyLineDown: "Copiar línea abajo",
|
||||
sqlFormatterShortcutUndo: "Deshacer",
|
||||
sqlFormatterShortcutRedo: "Rehacer",
|
||||
sqlFormatterShortcutSelectAll: "Seleccionar todo",
|
||||
sqlFormatterShortcutFormatJson: "Formatear JSON",
|
||||
sqlFormatterShortcutApply: "Aplicar configuración",
|
||||
sqlFormatterConfigErrorInvalidJson: "JSON no válido.",
|
||||
sqlFormatterConfigErrorObject: "La configuración debe ser un objeto JSON.",
|
||||
sqlFormatterConfigErrorVersion: "Versión de configuración no compatible.",
|
||||
sqlFormatterConfigErrorFormatter: "Formateador no compatible.",
|
||||
sqlFormatterConfigErrorUnsupportedParams: "La sustitución params no es compatible. El formateo SQL no reemplazará marcadores.",
|
||||
sqlFormatterConfigErrorOptionsObject: "Las opciones de configuración deben ser un objeto JSON.",
|
||||
sqlFormatterConfigErrorEditorObject: "La configuración del editor debe ser un objeto JSON.",
|
||||
sqlFormatterConfigErrorEditorScope: "Ámbito de editor no compatible.",
|
||||
sqlFormatterConfigErrorEditorPlatforms: "Plataformas de editor no válidas.",
|
||||
sqlFormatterConfigErrorEditorShortcutsArray: "Los atajos del editor deben ser un array.",
|
||||
sqlFormatterConfigErrorEditorShortcutValue: "Atajo de editor no válido.",
|
||||
sqlFormatterConfigErrorUnknownOption: "Opción desconocida del formateador: {option}.",
|
||||
sqlFormatterConfigErrorInvalidOptionValue: "Valor no válido para {option}.",
|
||||
sqlFormatterConfigErrorUnknownEditorShortcut: "Atajo de editor desconocido: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidEditorShortcut: "Atajo de editor no válido: {shortcut}.",
|
||||
sqlFormatterConfigErrorDuplicateEditorShortcut: "Atajo de editor duplicado: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidConfig: "Configuración del formateador no válida.",
|
||||
appearanceTab: "Apariencia",
|
||||
navigationTab: "Navegación",
|
||||
|
|
|
|||
|
|
@ -1627,12 +1627,17 @@
|
|||
sqlFormatterImport: "Importa configurazione",
|
||||
sqlFormatterExport: "Esporta configurazione",
|
||||
sqlFormatterImportSuccess: "Configurazione del formattatore importata.",
|
||||
sqlFormatterCopyJson: "Copia JSON",
|
||||
sqlFormatterCopyJsonSuccess: "JSON copiato.",
|
||||
sqlFormatterCopyJsonFailed: "Copia non riuscita: {message}",
|
||||
sqlFormatterJsonEditorLoadFailed: "Impossibile caricare l'editor JSON. Uso di un input di base: {message}",
|
||||
sqlFormatterRestoreDefaults: "Ripristina predefiniti",
|
||||
sqlFormatterFormMode: "Modulo",
|
||||
sqlFormatterJsonMode: "JSON",
|
||||
sqlFormatterKeywordCase: "Maiuscole/minuscole parole chiave",
|
||||
sqlFormatterFunctionCase: "Maiuscole/minuscole funzioni",
|
||||
sqlFormatterDataTypeCase: "Maiuscole/minuscole tipi di dati",
|
||||
sqlFormatterIdentifierCase: "Maiuscole/minuscole identificatori",
|
||||
sqlFormatterCaseUpper: "Maiuscolo",
|
||||
sqlFormatterCaseLower: "Minuscolo",
|
||||
sqlFormatterCasePreserve: "Mantieni",
|
||||
|
|
@ -1640,6 +1645,10 @@
|
|||
sqlFormatterIndentSpaces: "Spazi",
|
||||
sqlFormatterIndentTabs: "Tabulazioni",
|
||||
sqlFormatterTabWidth: "Larghezza tab",
|
||||
sqlFormatterIndentStyle: "Stile rientro",
|
||||
sqlFormatterIndentStyleStandard: "Standard",
|
||||
sqlFormatterIndentStyleTabularLeft: "Tabellare sinistra",
|
||||
sqlFormatterIndentStyleTabularRight: "Tabellare destra",
|
||||
sqlFormatterLogicalOperatorNewline: "Nuova riga operatore logico",
|
||||
sqlFormatterLogicalBefore: "Prima dell'operatore",
|
||||
sqlFormatterLogicalAfter: "Dopo l'operatore",
|
||||
|
|
@ -1647,6 +1656,15 @@
|
|||
sqlFormatterLinesBetweenQueries: "Righe tra query",
|
||||
sqlFormatterDenseOperators: "Operatori compatti",
|
||||
sqlFormatterNewlineBeforeSemicolon: "Punto e virgola su nuova riga",
|
||||
sqlFormatterAdvancedOptions: "Opzioni avanzate",
|
||||
sqlFormatterParams: "Parametri",
|
||||
sqlFormatterParamTypes: "Tipi parametro",
|
||||
sqlFormatterEditorShortcuts: "Scorciatoie editor JSON",
|
||||
sqlFormatterShortcutAction: "Azione",
|
||||
sqlFormatterShortcutEnabled: "Attiva",
|
||||
sqlFormatterShortcutWindows: "Windows",
|
||||
sqlFormatterShortcutLinux: "Linux",
|
||||
sqlFormatterShortcutMacos: "macOS",
|
||||
sqlFormatterShortcutFind: "Trova",
|
||||
sqlFormatterShortcutReplace: "Sostituisci",
|
||||
sqlFormatterShortcutIndentMore: "Aumenta rientro",
|
||||
|
|
@ -1654,16 +1672,32 @@
|
|||
sqlFormatterShortcutDuplicateLine: "Duplica riga corrente",
|
||||
sqlFormatterShortcutDeleteLine: "Elimina riga corrente",
|
||||
sqlFormatterShortcutMoveLine: "Sposta riga",
|
||||
sqlFormatterShortcutMoveLineUp: "Sposta riga su",
|
||||
sqlFormatterShortcutMoveLineDown: "Sposta riga giù",
|
||||
sqlFormatterShortcutCopyLine: "Copia riga",
|
||||
sqlFormatterShortcutCopyLineUp: "Copia riga su",
|
||||
sqlFormatterShortcutCopyLineDown: "Copia riga giù",
|
||||
sqlFormatterShortcutUndo: "Annulla",
|
||||
sqlFormatterShortcutRedo: "Ripeti",
|
||||
sqlFormatterShortcutSelectAll: "Seleziona tutto",
|
||||
sqlFormatterShortcutFormatJson: "Formatta JSON",
|
||||
sqlFormatterShortcutApply: "Applica configurazione",
|
||||
sqlFormatterConfigErrorInvalidJson: "JSON non valido.",
|
||||
sqlFormatterConfigErrorObject: "La configurazione deve essere un oggetto JSON.",
|
||||
sqlFormatterConfigErrorVersion: "Versione configurazione non supportata.",
|
||||
sqlFormatterConfigErrorFormatter: "Formattatore non supportato.",
|
||||
sqlFormatterConfigErrorUnsupportedParams: "La sostituzione params non è supportata. La formattazione SQL non sostituirà i segnaposto.",
|
||||
sqlFormatterConfigErrorOptionsObject: "Le opzioni di configurazione devono essere un oggetto JSON.",
|
||||
sqlFormatterConfigErrorEditorObject: "La configurazione dell'editor deve essere un oggetto JSON.",
|
||||
sqlFormatterConfigErrorEditorScope: "Ambito editor non supportato.",
|
||||
sqlFormatterConfigErrorEditorPlatforms: "Piattaforme editor non valide.",
|
||||
sqlFormatterConfigErrorEditorShortcutsArray: "Le scorciatoie editor devono essere un array.",
|
||||
sqlFormatterConfigErrorEditorShortcutValue: "Scorciatoia editor non valida.",
|
||||
sqlFormatterConfigErrorUnknownOption: "Opzione del formattatore sconosciuta: {option}.",
|
||||
sqlFormatterConfigErrorInvalidOptionValue: "Valore non valido per {option}.",
|
||||
sqlFormatterConfigErrorUnknownEditorShortcut: "Scorciatoia editor sconosciuta: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidEditorShortcut: "Scorciatoia editor non valida: {shortcut}.",
|
||||
sqlFormatterConfigErrorDuplicateEditorShortcut: "Scorciatoia editor duplicata: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidConfig: "Configurazione del formattatore non valida.",
|
||||
appearanceTab: "Aspetto",
|
||||
navigationTab: "Navigazione",
|
||||
|
|
|
|||
|
|
@ -1627,12 +1627,17 @@
|
|||
sqlFormatterImport: "Importar configuração",
|
||||
sqlFormatterExport: "Exportar configuração",
|
||||
sqlFormatterImportSuccess: "Configuração do formatador importada.",
|
||||
sqlFormatterCopyJson: "Copiar JSON",
|
||||
sqlFormatterCopyJsonSuccess: "JSON copiado.",
|
||||
sqlFormatterCopyJsonFailed: "Falha ao copiar: {message}",
|
||||
sqlFormatterJsonEditorLoadFailed: "Falha ao carregar o editor JSON. Alternado para uma entrada básica: {message}",
|
||||
sqlFormatterRestoreDefaults: "Restaurar padrões",
|
||||
sqlFormatterFormMode: "Formulário",
|
||||
sqlFormatterJsonMode: "JSON",
|
||||
sqlFormatterKeywordCase: "Maiúsculas/minúsculas de palavras-chave",
|
||||
sqlFormatterFunctionCase: "Maiúsculas/minúsculas de funções",
|
||||
sqlFormatterDataTypeCase: "Maiúsculas/minúsculas de tipos de dados",
|
||||
sqlFormatterIdentifierCase: "Maiúsculas/minúsculas de identificadores",
|
||||
sqlFormatterCaseUpper: "Maiúsculas",
|
||||
sqlFormatterCaseLower: "Minúsculas",
|
||||
sqlFormatterCasePreserve: "Preservar",
|
||||
|
|
@ -1640,6 +1645,10 @@
|
|||
sqlFormatterIndentSpaces: "Espaços",
|
||||
sqlFormatterIndentTabs: "Tabulações",
|
||||
sqlFormatterTabWidth: "Largura do tab",
|
||||
sqlFormatterIndentStyle: "Estilo de recuo",
|
||||
sqlFormatterIndentStyleStandard: "Padrão",
|
||||
sqlFormatterIndentStyleTabularLeft: "Tabular esquerda",
|
||||
sqlFormatterIndentStyleTabularRight: "Tabular direita",
|
||||
sqlFormatterLogicalOperatorNewline: "Quebra de linha no operador lógico",
|
||||
sqlFormatterLogicalBefore: "Antes do operador",
|
||||
sqlFormatterLogicalAfter: "Depois do operador",
|
||||
|
|
@ -1647,6 +1656,15 @@
|
|||
sqlFormatterLinesBetweenQueries: "Linhas entre consultas",
|
||||
sqlFormatterDenseOperators: "Operadores compactos",
|
||||
sqlFormatterNewlineBeforeSemicolon: "Ponto e vírgula em nova linha",
|
||||
sqlFormatterAdvancedOptions: "Opções avançadas",
|
||||
sqlFormatterParams: "Parâmetros",
|
||||
sqlFormatterParamTypes: "Tipos de parâmetro",
|
||||
sqlFormatterEditorShortcuts: "Atalhos do editor JSON",
|
||||
sqlFormatterShortcutAction: "Ação",
|
||||
sqlFormatterShortcutEnabled: "Ativo",
|
||||
sqlFormatterShortcutWindows: "Windows",
|
||||
sqlFormatterShortcutLinux: "Linux",
|
||||
sqlFormatterShortcutMacos: "macOS",
|
||||
sqlFormatterShortcutFind: "Localizar",
|
||||
sqlFormatterShortcutReplace: "Substituir",
|
||||
sqlFormatterShortcutIndentMore: "Aumentar recuo",
|
||||
|
|
@ -1654,16 +1672,32 @@
|
|||
sqlFormatterShortcutDuplicateLine: "Duplicar linha atual",
|
||||
sqlFormatterShortcutDeleteLine: "Excluir linha atual",
|
||||
sqlFormatterShortcutMoveLine: "Mover linha",
|
||||
sqlFormatterShortcutMoveLineUp: "Mover linha para cima",
|
||||
sqlFormatterShortcutMoveLineDown: "Mover linha para baixo",
|
||||
sqlFormatterShortcutCopyLine: "Copiar linha",
|
||||
sqlFormatterShortcutCopyLineUp: "Copiar linha para cima",
|
||||
sqlFormatterShortcutCopyLineDown: "Copiar linha para baixo",
|
||||
sqlFormatterShortcutUndo: "Desfazer",
|
||||
sqlFormatterShortcutRedo: "Refazer",
|
||||
sqlFormatterShortcutSelectAll: "Selecionar tudo",
|
||||
sqlFormatterShortcutFormatJson: "Formatar JSON",
|
||||
sqlFormatterShortcutApply: "Aplicar configuração",
|
||||
sqlFormatterConfigErrorInvalidJson: "JSON inválido.",
|
||||
sqlFormatterConfigErrorObject: "A configuração deve ser um objeto JSON.",
|
||||
sqlFormatterConfigErrorVersion: "Versão de configuração não compatível.",
|
||||
sqlFormatterConfigErrorFormatter: "Formatador não compatível.",
|
||||
sqlFormatterConfigErrorUnsupportedParams: "A substituição params não é compatível. A formatação SQL não substituirá marcadores.",
|
||||
sqlFormatterConfigErrorOptionsObject: "As opções de configuração devem ser um objeto JSON.",
|
||||
sqlFormatterConfigErrorEditorObject: "A configuração do editor deve ser um objeto JSON.",
|
||||
sqlFormatterConfigErrorEditorScope: "Escopo de editor não compatível.",
|
||||
sqlFormatterConfigErrorEditorPlatforms: "Plataformas de editor inválidas.",
|
||||
sqlFormatterConfigErrorEditorShortcutsArray: "Os atalhos do editor devem ser um array.",
|
||||
sqlFormatterConfigErrorEditorShortcutValue: "Atalho de editor inválido.",
|
||||
sqlFormatterConfigErrorUnknownOption: "Opção desconhecida do formatador: {option}.",
|
||||
sqlFormatterConfigErrorInvalidOptionValue: "Valor inválido para {option}.",
|
||||
sqlFormatterConfigErrorUnknownEditorShortcut: "Atalho de editor desconhecido: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidEditorShortcut: "Atalho de editor inválido: {shortcut}.",
|
||||
sqlFormatterConfigErrorDuplicateEditorShortcut: "Atalho de editor duplicado: {shortcut}.",
|
||||
sqlFormatterConfigErrorInvalidConfig: "Configuração do formatador inválida.",
|
||||
appearanceTab: "Aparência",
|
||||
navigationTab: "Navegação",
|
||||
|
|
|
|||
|
|
@ -1730,12 +1730,17 @@
|
|||
sqlFormatterImport: "导入配置",
|
||||
sqlFormatterExport: "导出配置",
|
||||
sqlFormatterImportSuccess: "格式化配置已导入。",
|
||||
sqlFormatterCopyJson: "复制 JSON",
|
||||
sqlFormatterCopyJsonSuccess: "JSON 已复制。",
|
||||
sqlFormatterCopyJsonFailed: "复制失败:{message}",
|
||||
sqlFormatterJsonEditorLoadFailed: "JSON 编辑器加载失败,已切换为基础输入框:{message}",
|
||||
sqlFormatterRestoreDefaults: "恢复默认",
|
||||
sqlFormatterFormMode: "表单",
|
||||
sqlFormatterJsonMode: "JSON",
|
||||
sqlFormatterKeywordCase: "关键字大小写",
|
||||
sqlFormatterFunctionCase: "函数名大小写",
|
||||
sqlFormatterDataTypeCase: "数据类型大小写",
|
||||
sqlFormatterIdentifierCase: "标识符大小写",
|
||||
sqlFormatterCaseUpper: "大写",
|
||||
sqlFormatterCaseLower: "小写",
|
||||
sqlFormatterCasePreserve: "保持原样",
|
||||
|
|
@ -1743,6 +1748,10 @@
|
|||
sqlFormatterIndentSpaces: "空格",
|
||||
sqlFormatterIndentTabs: "Tab",
|
||||
sqlFormatterTabWidth: "缩进宽度",
|
||||
sqlFormatterIndentStyle: "缩进风格",
|
||||
sqlFormatterIndentStyleStandard: "标准",
|
||||
sqlFormatterIndentStyleTabularLeft: "表格式左对齐",
|
||||
sqlFormatterIndentStyleTabularRight: "表格式右对齐",
|
||||
sqlFormatterLogicalOperatorNewline: "逻辑运算符换行",
|
||||
sqlFormatterLogicalBefore: "运算符前换行",
|
||||
sqlFormatterLogicalAfter: "运算符后换行",
|
||||
|
|
@ -1750,6 +1759,15 @@
|
|||
sqlFormatterLinesBetweenQueries: "查询之间空行",
|
||||
sqlFormatterDenseOperators: "紧凑运算符",
|
||||
sqlFormatterNewlineBeforeSemicolon: "分号单独换行",
|
||||
sqlFormatterAdvancedOptions: "高级选项",
|
||||
sqlFormatterParams: "参数替换",
|
||||
sqlFormatterParamTypes: "参数类型",
|
||||
sqlFormatterEditorShortcuts: "JSON 编辑器快捷键",
|
||||
sqlFormatterShortcutAction: "功能",
|
||||
sqlFormatterShortcutEnabled: "启用",
|
||||
sqlFormatterShortcutWindows: "Windows",
|
||||
sqlFormatterShortcutLinux: "Linux",
|
||||
sqlFormatterShortcutMacos: "macOS",
|
||||
sqlFormatterShortcutFind: "查找",
|
||||
sqlFormatterShortcutReplace: "替换",
|
||||
sqlFormatterShortcutIndentMore: "增加缩进",
|
||||
|
|
@ -1757,16 +1775,32 @@
|
|||
sqlFormatterShortcutDuplicateLine: "复制当前行",
|
||||
sqlFormatterShortcutDeleteLine: "删除当前行",
|
||||
sqlFormatterShortcutMoveLine: "移动行",
|
||||
sqlFormatterShortcutMoveLineUp: "当前行上移",
|
||||
sqlFormatterShortcutMoveLineDown: "当前行下移",
|
||||
sqlFormatterShortcutCopyLine: "复制行",
|
||||
sqlFormatterShortcutCopyLineUp: "向上复制当前行",
|
||||
sqlFormatterShortcutCopyLineDown: "向下复制当前行",
|
||||
sqlFormatterShortcutUndo: "撤销",
|
||||
sqlFormatterShortcutRedo: "重做",
|
||||
sqlFormatterShortcutSelectAll: "全选",
|
||||
sqlFormatterShortcutFormatJson: "格式化 JSON",
|
||||
sqlFormatterShortcutApply: "应用配置",
|
||||
sqlFormatterConfigErrorInvalidJson: "JSON 无效。",
|
||||
sqlFormatterConfigErrorObject: "配置必须是 JSON 对象。",
|
||||
sqlFormatterConfigErrorVersion: "不支持的配置版本。",
|
||||
sqlFormatterConfigErrorFormatter: "不支持的格式化器。",
|
||||
sqlFormatterConfigErrorUnsupportedParams: "不支持 params 参数替换。SQL 格式化不会替换占位符。",
|
||||
sqlFormatterConfigErrorOptionsObject: "配置选项必须是 JSON 对象。",
|
||||
sqlFormatterConfigErrorEditorObject: "编辑器配置必须是 JSON 对象。",
|
||||
sqlFormatterConfigErrorEditorScope: "不支持的编辑器作用域。",
|
||||
sqlFormatterConfigErrorEditorPlatforms: "编辑器平台配置无效。",
|
||||
sqlFormatterConfigErrorEditorShortcutsArray: "编辑器快捷键必须是数组。",
|
||||
sqlFormatterConfigErrorEditorShortcutValue: "编辑器快捷键无效。",
|
||||
sqlFormatterConfigErrorUnknownOption: "未知格式化选项:{option}。",
|
||||
sqlFormatterConfigErrorInvalidOptionValue: "“{option}”的值无效。",
|
||||
sqlFormatterConfigErrorUnknownEditorShortcut: "未知编辑器快捷键:{shortcut}。",
|
||||
sqlFormatterConfigErrorInvalidEditorShortcut: "编辑器快捷键无效:{shortcut}。",
|
||||
sqlFormatterConfigErrorDuplicateEditorShortcut: "编辑器快捷键重复:{shortcut}。",
|
||||
sqlFormatterConfigErrorInvalidConfig: "格式化配置无效。",
|
||||
appearanceTab: "外观",
|
||||
navigationTab: "导航",
|
||||
|
|
|
|||
|
|
@ -1604,12 +1604,17 @@
|
|||
sqlFormatterImport: "匯入設定",
|
||||
sqlFormatterExport: "匯出設定",
|
||||
sqlFormatterImportSuccess: "格式化設定已匯入。",
|
||||
sqlFormatterCopyJson: "複製 JSON",
|
||||
sqlFormatterCopyJsonSuccess: "JSON 已複製。",
|
||||
sqlFormatterCopyJsonFailed: "複製失敗:{message}",
|
||||
sqlFormatterJsonEditorLoadFailed: "JSON 編輯器載入失敗,已切換為基本輸入框:{message}",
|
||||
sqlFormatterRestoreDefaults: "還原預設",
|
||||
sqlFormatterFormMode: "表單",
|
||||
sqlFormatterJsonMode: "JSON",
|
||||
sqlFormatterKeywordCase: "關鍵字大小寫",
|
||||
sqlFormatterFunctionCase: "函式名稱大小寫",
|
||||
sqlFormatterDataTypeCase: "資料型別大小寫",
|
||||
sqlFormatterIdentifierCase: "識別符大小寫",
|
||||
sqlFormatterCaseUpper: "大寫",
|
||||
sqlFormatterCaseLower: "小寫",
|
||||
sqlFormatterCasePreserve: "保持原樣",
|
||||
|
|
@ -1617,6 +1622,10 @@
|
|||
sqlFormatterIndentSpaces: "空格",
|
||||
sqlFormatterIndentTabs: "Tab",
|
||||
sqlFormatterTabWidth: "縮排寬度",
|
||||
sqlFormatterIndentStyle: "縮排風格",
|
||||
sqlFormatterIndentStyleStandard: "標準",
|
||||
sqlFormatterIndentStyleTabularLeft: "表格式靠左",
|
||||
sqlFormatterIndentStyleTabularRight: "表格式靠右",
|
||||
sqlFormatterLogicalOperatorNewline: "邏輯運算子換行",
|
||||
sqlFormatterLogicalBefore: "運算子前換行",
|
||||
sqlFormatterLogicalAfter: "運算子後換行",
|
||||
|
|
@ -1624,6 +1633,15 @@
|
|||
sqlFormatterLinesBetweenQueries: "查詢之間空行",
|
||||
sqlFormatterDenseOperators: "緊湊運算子",
|
||||
sqlFormatterNewlineBeforeSemicolon: "分號獨立換行",
|
||||
sqlFormatterAdvancedOptions: "進階選項",
|
||||
sqlFormatterParams: "參數替換",
|
||||
sqlFormatterParamTypes: "參數類型",
|
||||
sqlFormatterEditorShortcuts: "JSON 編輯器快速鍵",
|
||||
sqlFormatterShortcutAction: "功能",
|
||||
sqlFormatterShortcutEnabled: "啟用",
|
||||
sqlFormatterShortcutWindows: "Windows",
|
||||
sqlFormatterShortcutLinux: "Linux",
|
||||
sqlFormatterShortcutMacos: "macOS",
|
||||
sqlFormatterShortcutFind: "尋找",
|
||||
sqlFormatterShortcutReplace: "取代",
|
||||
sqlFormatterShortcutIndentMore: "增加縮排",
|
||||
|
|
@ -1631,16 +1649,32 @@
|
|||
sqlFormatterShortcutDuplicateLine: "複製目前行",
|
||||
sqlFormatterShortcutDeleteLine: "刪除目前行",
|
||||
sqlFormatterShortcutMoveLine: "移動行",
|
||||
sqlFormatterShortcutMoveLineUp: "目前行上移",
|
||||
sqlFormatterShortcutMoveLineDown: "目前行下移",
|
||||
sqlFormatterShortcutCopyLine: "複製行",
|
||||
sqlFormatterShortcutCopyLineUp: "向上複製目前行",
|
||||
sqlFormatterShortcutCopyLineDown: "向下複製目前行",
|
||||
sqlFormatterShortcutUndo: "復原",
|
||||
sqlFormatterShortcutRedo: "重做",
|
||||
sqlFormatterShortcutSelectAll: "全選",
|
||||
sqlFormatterShortcutFormatJson: "格式化 JSON",
|
||||
sqlFormatterShortcutApply: "套用設定",
|
||||
sqlFormatterConfigErrorInvalidJson: "JSON 無效。",
|
||||
sqlFormatterConfigErrorObject: "設定必須是 JSON 物件。",
|
||||
sqlFormatterConfigErrorVersion: "不支援的設定版本。",
|
||||
sqlFormatterConfigErrorFormatter: "不支援的格式化器。",
|
||||
sqlFormatterConfigErrorUnsupportedParams: "不支援 params 參數替換。SQL 格式化不會替換佔位符。",
|
||||
sqlFormatterConfigErrorOptionsObject: "設定選項必須是 JSON 物件。",
|
||||
sqlFormatterConfigErrorEditorObject: "編輯器設定必須是 JSON 物件。",
|
||||
sqlFormatterConfigErrorEditorScope: "不支援的編輯器作用域。",
|
||||
sqlFormatterConfigErrorEditorPlatforms: "編輯器平台設定無效。",
|
||||
sqlFormatterConfigErrorEditorShortcutsArray: "編輯器快速鍵必須是陣列。",
|
||||
sqlFormatterConfigErrorEditorShortcutValue: "編輯器快速鍵無效。",
|
||||
sqlFormatterConfigErrorUnknownOption: "未知格式化選項:{option}。",
|
||||
sqlFormatterConfigErrorInvalidOptionValue: "「{option}」的值無效。",
|
||||
sqlFormatterConfigErrorUnknownEditorShortcut: "未知編輯器快速鍵:{shortcut}。",
|
||||
sqlFormatterConfigErrorInvalidEditorShortcut: "編輯器快速鍵無效:{shortcut}。",
|
||||
sqlFormatterConfigErrorDuplicateEditorShortcut: "編輯器快速鍵重複:{shortcut}。",
|
||||
sqlFormatterConfigErrorInvalidConfig: "格式化設定無效。",
|
||||
appearanceTab: "外觀",
|
||||
navigationTab: "導覽",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ function formatterLanguage(dialect: SqlFormatDialect) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function formatSqlText(sql: string, dialect: SqlFormatDialect = "generic", settings: SqlFormatterSettings = DEFAULT_SQL_FORMATTER_SETTINGS): Promise<string> {
|
||||
export async function formatSqlText(sql: string, dialect: SqlFormatDialect = "generic", settings: Partial<SqlFormatterSettings> = DEFAULT_SQL_FORMATTER_SETTINGS): Promise<string> {
|
||||
if (!sql.trim()) return sql;
|
||||
if (sql.length > MAX_SQL_FORMAT_CHARS) {
|
||||
throw new Error("SQL is too large to format safely.");
|
||||
|
|
|
|||
|
|
@ -1,22 +1,44 @@
|
|||
export const SQL_FORMATTER_CONFIG_VERSION = 1;
|
||||
export const SQL_FORMATTER_CONFIG_FORMATTER = "sql-formatter";
|
||||
export const SQL_FORMATTER_EDITOR_SCOPE = "sqlFormatterConfigJsonEditor";
|
||||
|
||||
const CASE_VALUES = ["preserve", "upper", "lower"] as const;
|
||||
const INDENT_STYLE_VALUES = ["standard", "tabularLeft", "tabularRight"] as const;
|
||||
const LOGICAL_OPERATOR_NEWLINE_VALUES = ["before", "after"] as const;
|
||||
const TAB_WIDTH_VALUES = [2, 4] as const;
|
||||
const EXPRESSION_WIDTH_VALUES = [50, 80, 120] as const;
|
||||
const LINES_BETWEEN_QUERIES_VALUES = [0, 1, 2] as const;
|
||||
const SQL_FORMATTER_PLATFORM_VALUES = ["windows", "linux", "macos"] as const;
|
||||
const SQL_FORMATTER_PARAM_TYPE_MARKERS = ["?", ":", "$"] as const;
|
||||
const SQL_FORMATTER_NAMED_PARAM_TYPE_MARKERS = [":", "@", "$"] as const;
|
||||
const SQL_FORMATTER_LEGACY_OPTION_KEYS = new Set(["params"]);
|
||||
|
||||
export type SqlFormatterCase = (typeof CASE_VALUES)[number];
|
||||
export type SqlFormatterIndentStyle = (typeof INDENT_STYLE_VALUES)[number];
|
||||
export type SqlFormatterLogicalOperatorNewline = (typeof LOGICAL_OPERATOR_NEWLINE_VALUES)[number];
|
||||
export type SqlFormatterTabWidth = (typeof TAB_WIDTH_VALUES)[number];
|
||||
export type SqlFormatterExpressionWidth = (typeof EXPRESSION_WIDTH_VALUES)[number];
|
||||
export type SqlFormatterLinesBetweenQueries = (typeof LINES_BETWEEN_QUERIES_VALUES)[number];
|
||||
export type SqlFormatterPlatform = (typeof SQL_FORMATTER_PLATFORM_VALUES)[number];
|
||||
|
||||
export interface SqlFormatterSettings {
|
||||
export interface SqlFormatterCustomParameter {
|
||||
regex: string;
|
||||
}
|
||||
|
||||
export interface SqlFormatterParamTypes {
|
||||
positional?: boolean;
|
||||
numbered?: ("?" | ":" | "$")[];
|
||||
named?: (":" | "@" | "$")[];
|
||||
quoted?: (":" | "@" | "$")[];
|
||||
custom?: SqlFormatterCustomParameter[];
|
||||
}
|
||||
|
||||
export interface SqlFormatterOptionSettings {
|
||||
keywordCase: SqlFormatterCase;
|
||||
dataTypeCase: SqlFormatterCase;
|
||||
functionCase: SqlFormatterCase;
|
||||
identifierCase: SqlFormatterCase;
|
||||
indentStyle: SqlFormatterIndentStyle;
|
||||
useTabs: boolean;
|
||||
tabWidth: SqlFormatterTabWidth;
|
||||
logicalOperatorNewline: SqlFormatterLogicalOperatorNewline;
|
||||
|
|
@ -24,20 +46,70 @@ export interface SqlFormatterSettings {
|
|||
linesBetweenQueries: SqlFormatterLinesBetweenQueries;
|
||||
denseOperators: boolean;
|
||||
newlineBeforeSemicolon: boolean;
|
||||
paramTypes: SqlFormatterParamTypes | null;
|
||||
}
|
||||
|
||||
export type SqlFormatterEditorShortcutId = "find" | "replace" | "indentMore" | "indentLess" | "duplicateLine" | "deleteLine" | "moveLineUp" | "moveLineDown" | "copyLineUp" | "copyLineDown" | "undo" | "redo" | "selectAll" | "formatJson" | "applyConfig";
|
||||
|
||||
export type SqlFormatterEditorShortcutAction = "openSearchPanel" | "indentMore" | "indentLess" | "copyLineDown" | "copyLineUp" | "deleteLine" | "moveLineUp" | "moveLineDown" | "undo" | "redo" | "selectAll" | "formatJson" | "applyJsonDraft";
|
||||
|
||||
export interface SqlFormatterEditorShortcutKeys {
|
||||
windows: string;
|
||||
linux: string;
|
||||
macos: string;
|
||||
}
|
||||
|
||||
export interface SqlFormatterEditorShortcut {
|
||||
id: SqlFormatterEditorShortcutId;
|
||||
action: SqlFormatterEditorShortcutAction;
|
||||
keys: SqlFormatterEditorShortcutKeys;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface SqlFormatterEditorSettings {
|
||||
scope: typeof SQL_FORMATTER_EDITOR_SCOPE;
|
||||
platforms: SqlFormatterPlatform[];
|
||||
shortcuts: SqlFormatterEditorShortcut[];
|
||||
}
|
||||
|
||||
export interface SqlFormatterSettings extends SqlFormatterOptionSettings {
|
||||
editor: SqlFormatterEditorSettings;
|
||||
}
|
||||
|
||||
export interface SqlFormatterConfigFile {
|
||||
version: typeof SQL_FORMATTER_CONFIG_VERSION;
|
||||
formatter: typeof SQL_FORMATTER_CONFIG_FORMATTER;
|
||||
options: SqlFormatterSettings;
|
||||
options: SqlFormatterOptionSettings;
|
||||
editor: SqlFormatterEditorSettings;
|
||||
}
|
||||
|
||||
export type SqlFormatterConfigParseResult = { ok: true; settings: SqlFormatterSettings } | { ok: false; message: string };
|
||||
export type SqlFormatterEditorValidationResult = { ok: true } | { ok: false; message: string };
|
||||
|
||||
export const DEFAULT_SQL_FORMATTER_EDITOR_SHORTCUTS: SqlFormatterEditorShortcut[] = [
|
||||
{ id: "find", action: "openSearchPanel", keys: { windows: "Ctrl+F", linux: "Ctrl+F", macos: "Cmd+F" }, enabled: true },
|
||||
{ id: "replace", action: "openSearchPanel", keys: { windows: "Ctrl+H", linux: "Ctrl+H", macos: "Cmd+Option+F" }, enabled: true },
|
||||
{ id: "indentMore", action: "indentMore", keys: { windows: "Tab", linux: "Tab", macos: "Tab" }, enabled: true },
|
||||
{ id: "indentLess", action: "indentLess", keys: { windows: "Shift+Tab", linux: "Shift+Tab", macos: "Shift+Tab" }, enabled: true },
|
||||
{ id: "duplicateLine", action: "copyLineDown", keys: { windows: "Ctrl+D", linux: "Ctrl+D", macos: "Cmd+D" }, enabled: true },
|
||||
{ id: "deleteLine", action: "deleteLine", keys: { windows: "Ctrl+Shift+K", linux: "Ctrl+Shift+K", macos: "Cmd+Shift+K" }, enabled: true },
|
||||
{ id: "moveLineUp", action: "moveLineUp", keys: { windows: "Alt+Up", linux: "Alt+Up", macos: "Option+Up" }, enabled: true },
|
||||
{ id: "moveLineDown", action: "moveLineDown", keys: { windows: "Alt+Down", linux: "Alt+Down", macos: "Option+Down" }, enabled: true },
|
||||
{ id: "copyLineUp", action: "copyLineUp", keys: { windows: "Shift+Alt+Up", linux: "Shift+Alt+Up", macos: "Shift+Option+Up" }, enabled: true },
|
||||
{ id: "copyLineDown", action: "copyLineDown", keys: { windows: "Shift+Alt+Down", linux: "Shift+Alt+Down", macos: "Shift+Option+Down" }, enabled: true },
|
||||
{ id: "undo", action: "undo", keys: { windows: "Ctrl+Z", linux: "Ctrl+Z", macos: "Cmd+Z" }, enabled: true },
|
||||
{ id: "redo", action: "redo", keys: { windows: "Ctrl+Y", linux: "Ctrl+Shift+Z", macos: "Cmd+Shift+Z" }, enabled: true },
|
||||
{ id: "selectAll", action: "selectAll", keys: { windows: "Ctrl+A", linux: "Ctrl+A", macos: "Cmd+A" }, enabled: true },
|
||||
{ id: "formatJson", action: "formatJson", keys: { windows: "Shift+Alt+F", linux: "Shift+Alt+F", macos: "Shift+Cmd+F" }, enabled: true },
|
||||
{ id: "applyConfig", action: "applyJsonDraft", keys: { windows: "Ctrl+S", linux: "Ctrl+S", macos: "Cmd+S" }, enabled: true },
|
||||
];
|
||||
|
||||
export const DEFAULT_SQL_FORMATTER_SETTINGS: SqlFormatterSettings = {
|
||||
keywordCase: "upper",
|
||||
dataTypeCase: "preserve",
|
||||
functionCase: "preserve",
|
||||
identifierCase: "preserve",
|
||||
indentStyle: "standard",
|
||||
useTabs: false,
|
||||
tabWidth: 2,
|
||||
logicalOperatorNewline: "before",
|
||||
|
|
@ -45,14 +117,36 @@ export const DEFAULT_SQL_FORMATTER_SETTINGS: SqlFormatterSettings = {
|
|||
linesBetweenQueries: 1,
|
||||
denseOperators: false,
|
||||
newlineBeforeSemicolon: false,
|
||||
paramTypes: null,
|
||||
editor: {
|
||||
scope: SQL_FORMATTER_EDITOR_SCOPE,
|
||||
platforms: [...SQL_FORMATTER_PLATFORM_VALUES],
|
||||
shortcuts: cloneShortcuts(DEFAULT_SQL_FORMATTER_EDITOR_SHORTCUTS),
|
||||
},
|
||||
};
|
||||
|
||||
const SQL_FORMATTER_OPTION_KEYS = new Set<keyof SqlFormatterSettings>(["keywordCase", "dataTypeCase", "functionCase", "useTabs", "tabWidth", "logicalOperatorNewline", "expressionWidth", "linesBetweenQueries", "denseOperators", "newlineBeforeSemicolon"]);
|
||||
const SQL_FORMATTER_OPTION_KEYS = new Set<keyof SqlFormatterOptionSettings>([
|
||||
"keywordCase",
|
||||
"dataTypeCase",
|
||||
"functionCase",
|
||||
"identifierCase",
|
||||
"indentStyle",
|
||||
"useTabs",
|
||||
"tabWidth",
|
||||
"logicalOperatorNewline",
|
||||
"expressionWidth",
|
||||
"linesBetweenQueries",
|
||||
"denseOperators",
|
||||
"newlineBeforeSemicolon",
|
||||
"paramTypes",
|
||||
]);
|
||||
|
||||
const SQL_FORMATTER_OPTION_VALIDATORS: Record<keyof SqlFormatterSettings, (value: unknown) => boolean> = {
|
||||
const SQL_FORMATTER_OPTION_VALIDATORS: Record<keyof SqlFormatterOptionSettings, (value: unknown) => boolean> = {
|
||||
keywordCase: (value) => isStringChoice(value, CASE_VALUES),
|
||||
dataTypeCase: (value) => isStringChoice(value, CASE_VALUES),
|
||||
functionCase: (value) => isStringChoice(value, CASE_VALUES),
|
||||
identifierCase: (value) => isStringChoice(value, CASE_VALUES),
|
||||
indentStyle: (value) => isStringChoice(value, INDENT_STYLE_VALUES),
|
||||
useTabs: (value) => typeof value === "boolean",
|
||||
tabWidth: (value) => isNumberChoice(value, TAB_WIDTH_VALUES),
|
||||
logicalOperatorNewline: (value) => isStringChoice(value, LOGICAL_OPERATOR_NEWLINE_VALUES),
|
||||
|
|
@ -60,6 +154,7 @@ const SQL_FORMATTER_OPTION_VALIDATORS: Record<keyof SqlFormatterSettings, (value
|
|||
linesBetweenQueries: (value) => isNumberChoice(value, LINES_BETWEEN_QUERIES_VALUES),
|
||||
denseOperators: (value) => typeof value === "boolean",
|
||||
newlineBeforeSemicolon: (value) => typeof value === "boolean",
|
||||
paramTypes: isSqlFormatterParamTypes,
|
||||
};
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
|
|
@ -74,6 +169,32 @@ function isNumberChoice(value: unknown, values: readonly number[]): boolean {
|
|||
return typeof value === "number" && values.includes(value);
|
||||
}
|
||||
|
||||
function isMarkerArray<T extends readonly string[]>(value: unknown, markers: T): value is T[number][] {
|
||||
return Array.isArray(value) && value.every((item) => typeof item === "string" && markers.includes(item));
|
||||
}
|
||||
|
||||
function isCustomParameter(value: unknown): value is SqlFormatterCustomParameter {
|
||||
if (!isObject(value) || typeof value.regex !== "string" || value.regex.length === 0 || !Object.keys(value).every((key) => key === "regex")) return false;
|
||||
try {
|
||||
new RegExp(`(?:${value.regex})`, "uy");
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isSqlFormatterParamTypes(value: unknown): value is SqlFormatterParamTypes | null {
|
||||
if (value === null) return true;
|
||||
if (!isObject(value)) return false;
|
||||
if (!Object.keys(value).every((key) => ["positional", "numbered", "named", "quoted", "custom"].includes(key))) return false;
|
||||
if (value.positional !== undefined && typeof value.positional !== "boolean") return false;
|
||||
if (value.numbered !== undefined && !isMarkerArray(value.numbered, SQL_FORMATTER_PARAM_TYPE_MARKERS)) return false;
|
||||
if (value.named !== undefined && !isMarkerArray(value.named, SQL_FORMATTER_NAMED_PARAM_TYPE_MARKERS)) return false;
|
||||
if (value.quoted !== undefined && !isMarkerArray(value.quoted, SQL_FORMATTER_NAMED_PARAM_TYPE_MARKERS)) return false;
|
||||
if (value.custom !== undefined && (!Array.isArray(value.custom) || !value.custom.every(isCustomParameter))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function normalizeChoice<T extends readonly string[]>(value: unknown, values: T, fallback: T[number]): T[number] {
|
||||
return typeof value === "string" && values.includes(value) ? value : fallback;
|
||||
}
|
||||
|
|
@ -86,12 +207,181 @@ function normalizeBoolean(value: unknown, fallback: boolean): boolean {
|
|||
return typeof value === "boolean" ? value : fallback;
|
||||
}
|
||||
|
||||
export function normalizeSqlFormatterSettings(value: unknown): SqlFormatterSettings {
|
||||
function normalizeParamTypes(value: unknown, fallback: SqlFormatterParamTypes | null): SqlFormatterParamTypes | null {
|
||||
if (value === null) return null;
|
||||
if (!isSqlFormatterParamTypes(value)) return fallback;
|
||||
return {
|
||||
...(value.positional !== undefined ? { positional: value.positional } : {}),
|
||||
...(value.numbered ? { numbered: [...value.numbered] } : {}),
|
||||
...(value.named ? { named: [...value.named] } : {}),
|
||||
...(value.quoted ? { quoted: [...value.quoted] } : {}),
|
||||
...(value.custom ? { custom: value.custom.map((item) => ({ regex: item.regex })) } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function cloneShortcuts(shortcuts: readonly SqlFormatterEditorShortcut[]): SqlFormatterEditorShortcut[] {
|
||||
return shortcuts.map((shortcut) => ({
|
||||
id: shortcut.id,
|
||||
action: shortcut.action,
|
||||
keys: { ...shortcut.keys },
|
||||
enabled: shortcut.enabled,
|
||||
}));
|
||||
}
|
||||
|
||||
function normalizeShortcutKeys(value: unknown, fallback: SqlFormatterEditorShortcutKeys): SqlFormatterEditorShortcutKeys {
|
||||
const input = isObject(value) ? value : {};
|
||||
return {
|
||||
windows: typeof input.windows === "string" ? input.windows.trim() : fallback.windows,
|
||||
linux: typeof input.linux === "string" ? input.linux.trim() : fallback.linux,
|
||||
macos: typeof input.macos === "string" ? input.macos.trim() : fallback.macos,
|
||||
};
|
||||
}
|
||||
|
||||
function isCompatibleShortcutAction(shortcut: Record<string, unknown>, fallback: SqlFormatterEditorShortcut): boolean {
|
||||
if (shortcut.action === undefined || shortcut.action === fallback.action || shortcut.action === fallback.id) return true;
|
||||
return fallback.id === "replace" && shortcut.action === "openReplacePanel";
|
||||
}
|
||||
|
||||
export function sqlFormatterPlatformFromNavigator(platform = globalThis.navigator?.platform || ""): SqlFormatterPlatform {
|
||||
const normalized = platform.toLowerCase();
|
||||
if (normalized.includes("mac")) return "macos";
|
||||
if (normalized.includes("linux")) return "linux";
|
||||
return "windows";
|
||||
}
|
||||
|
||||
function keyPartToCodeMirror(part: string): string | null {
|
||||
const normalized = part.trim().toLowerCase();
|
||||
if (normalized === "ctrl" || normalized === "control") return "Ctrl";
|
||||
if (normalized === "cmd" || normalized === "command") return "Mod";
|
||||
if (normalized === "option" || normalized === "alt") return "Alt";
|
||||
if (normalized === "shift") return "Shift";
|
||||
if (normalized === "up") return "ArrowUp";
|
||||
if (normalized === "down") return "ArrowDown";
|
||||
if (normalized === "left") return "ArrowLeft";
|
||||
if (normalized === "right") return "ArrowRight";
|
||||
if (normalized === "esc") return "Escape";
|
||||
if (normalized === "enter" || normalized === "return") return "Enter";
|
||||
if (normalized === "del") return "Delete";
|
||||
if (normalized === "space") return "Space";
|
||||
if (normalized === "pageup") return "PageUp";
|
||||
if (normalized === "pagedown") return "PageDown";
|
||||
if (["tab", "escape", "backspace", "delete", "home", "end"].includes(normalized)) return normalized[0].toUpperCase() + normalized.slice(1);
|
||||
if (/^f(?:[1-9]|1\d|2[0-4])$/.test(normalized)) return normalized.toUpperCase();
|
||||
if (/^[a-z0-9]$/.test(normalized)) return normalized;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function sqlFormatterShortcutDisplayKeyToCodeMirrorKey(value: string): string | null {
|
||||
const parts = value
|
||||
.split("+")
|
||||
.map((part) => part.trim())
|
||||
.filter(Boolean);
|
||||
if (!parts.length) return null;
|
||||
|
||||
const converted = parts.map(keyPartToCodeMirror);
|
||||
if (converted.some((part) => !part)) return null;
|
||||
|
||||
const key = converted[converted.length - 1];
|
||||
if (!key || ["Ctrl", "Mod", "Alt", "Shift"].includes(key)) return null;
|
||||
|
||||
const modifiers = converted.slice(0, -1);
|
||||
if (new Set(modifiers).size !== modifiers.length) return null;
|
||||
if (modifiers.some((part) => part && !["Ctrl", "Mod", "Alt", "Shift"].includes(part))) return null;
|
||||
|
||||
return [...modifiers, key].join("-");
|
||||
}
|
||||
|
||||
function shortcutConflictKey(value: string): string | null {
|
||||
return sqlFormatterShortcutDisplayKeyToCodeMirrorKey(value)?.toLowerCase() ?? null;
|
||||
}
|
||||
|
||||
export function validateSqlFormatterEditorSettings(value: unknown): SqlFormatterEditorValidationResult {
|
||||
const settings = normalizeSqlFormatterEditorSettings(value);
|
||||
const platformSet = new Set(settings.platforms);
|
||||
|
||||
for (const shortcut of settings.shortcuts) {
|
||||
if (!shortcut.enabled) continue;
|
||||
for (const platform of settings.platforms) {
|
||||
if (!shortcutConflictKey(shortcut.keys[platform])) return { ok: false, message: `Invalid editor shortcut value: ${shortcut.id}.` };
|
||||
}
|
||||
}
|
||||
|
||||
for (const platform of SQL_FORMATTER_PLATFORM_VALUES) {
|
||||
if (!platformSet.has(platform)) continue;
|
||||
const seen = new Map<string, SqlFormatterEditorShortcutId>();
|
||||
for (const shortcut of settings.shortcuts) {
|
||||
if (!shortcut.enabled) continue;
|
||||
const key = shortcutConflictKey(shortcut.keys[platform]);
|
||||
if (!key) continue;
|
||||
const existing = seen.get(key);
|
||||
if (existing) return { ok: false, message: `Duplicate editor shortcut: ${platform}:${shortcut.keys[platform]}.` };
|
||||
seen.set(key, shortcut.id);
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export function normalizeSqlFormatterEditorSettings(value: unknown): SqlFormatterEditorSettings {
|
||||
const input = isObject(value) ? value : {};
|
||||
const shortcutsInput = Array.isArray(input.shortcuts) ? input.shortcuts.filter(isObject) : [];
|
||||
const platforms = Array.isArray(input.platforms) ? input.platforms.filter((platform): platform is SqlFormatterPlatform => isStringChoice(platform, SQL_FORMATTER_PLATFORM_VALUES)) : [...SQL_FORMATTER_PLATFORM_VALUES];
|
||||
const normalizedPlatforms = platforms.length ? [...new Set(platforms)] : [...SQL_FORMATTER_PLATFORM_VALUES];
|
||||
|
||||
return {
|
||||
scope: SQL_FORMATTER_EDITOR_SCOPE,
|
||||
platforms: normalizedPlatforms,
|
||||
shortcuts: DEFAULT_SQL_FORMATTER_EDITOR_SHORTCUTS.map((fallback) => {
|
||||
const source = shortcutsInput.find((shortcut) => shortcut.id === fallback.id);
|
||||
return {
|
||||
id: fallback.id,
|
||||
action: fallback.action,
|
||||
keys: normalizeShortcutKeys(source?.keys, fallback.keys),
|
||||
enabled: typeof source?.enabled === "boolean" ? source.enabled : fallback.enabled,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function validateEditorConfig(value: unknown): string | null {
|
||||
if (value === undefined) return null;
|
||||
if (!isObject(value)) return "Config editor must be a JSON object.";
|
||||
if (value.scope !== undefined && value.scope !== SQL_FORMATTER_EDITOR_SCOPE) return "Unsupported editor scope.";
|
||||
if (value.platforms !== undefined && (!Array.isArray(value.platforms) || !value.platforms.every((platform) => isStringChoice(platform, SQL_FORMATTER_PLATFORM_VALUES)))) {
|
||||
return "Invalid editor platforms.";
|
||||
}
|
||||
if (value.shortcuts === undefined) return null;
|
||||
if (!Array.isArray(value.shortcuts)) return "Config editor shortcuts must be an array.";
|
||||
|
||||
for (const shortcut of value.shortcuts) {
|
||||
if (!isObject(shortcut) || typeof shortcut.id !== "string") return "Invalid editor shortcut value.";
|
||||
const fallback = DEFAULT_SQL_FORMATTER_EDITOR_SHORTCUTS.find((item) => item.id === shortcut.id);
|
||||
if (!fallback) return `Unknown editor shortcut: ${shortcut.id}.`;
|
||||
if (!isCompatibleShortcutAction(shortcut, fallback)) return `Invalid editor shortcut value: ${shortcut.id}.`;
|
||||
if (shortcut.enabled !== undefined && typeof shortcut.enabled !== "boolean") return `Invalid editor shortcut value: ${shortcut.id}.`;
|
||||
if (shortcut.keys !== undefined) {
|
||||
if (!isObject(shortcut.keys)) return `Invalid editor shortcut value: ${shortcut.id}.`;
|
||||
const keys = Object.entries(shortcut.keys);
|
||||
const hasInvalidKey = keys.some(([platform, key]) => !isStringChoice(platform, SQL_FORMATTER_PLATFORM_VALUES) || typeof key !== "string" || !key.trim());
|
||||
if (hasInvalidKey) {
|
||||
return `Invalid editor shortcut value: ${shortcut.id}.`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const normalized = normalizeSqlFormatterEditorSettings(value);
|
||||
const validation = validateSqlFormatterEditorSettings(normalized);
|
||||
return validation.ok ? null : validation.message;
|
||||
}
|
||||
|
||||
export function sqlFormatterOptionSettings(settings: unknown): SqlFormatterOptionSettings {
|
||||
const input = isObject(settings) ? settings : {};
|
||||
return {
|
||||
keywordCase: normalizeChoice(input.keywordCase, CASE_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.keywordCase),
|
||||
dataTypeCase: normalizeChoice(input.dataTypeCase, CASE_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.dataTypeCase),
|
||||
functionCase: normalizeChoice(input.functionCase, CASE_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.functionCase),
|
||||
identifierCase: normalizeChoice(input.identifierCase, CASE_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.identifierCase),
|
||||
indentStyle: normalizeChoice(input.indentStyle, INDENT_STYLE_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.indentStyle),
|
||||
useTabs: normalizeBoolean(input.useTabs, DEFAULT_SQL_FORMATTER_SETTINGS.useTabs),
|
||||
tabWidth: normalizeNumberChoice(input.tabWidth, TAB_WIDTH_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.tabWidth),
|
||||
logicalOperatorNewline: normalizeChoice(input.logicalOperatorNewline, LOGICAL_OPERATOR_NEWLINE_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.logicalOperatorNewline),
|
||||
|
|
@ -99,14 +389,26 @@ export function normalizeSqlFormatterSettings(value: unknown): SqlFormatterSetti
|
|||
linesBetweenQueries: normalizeNumberChoice(input.linesBetweenQueries, LINES_BETWEEN_QUERIES_VALUES, DEFAULT_SQL_FORMATTER_SETTINGS.linesBetweenQueries),
|
||||
denseOperators: normalizeBoolean(input.denseOperators, DEFAULT_SQL_FORMATTER_SETTINGS.denseOperators),
|
||||
newlineBeforeSemicolon: normalizeBoolean(input.newlineBeforeSemicolon, DEFAULT_SQL_FORMATTER_SETTINGS.newlineBeforeSemicolon),
|
||||
paramTypes: normalizeParamTypes(input.paramTypes, DEFAULT_SQL_FORMATTER_SETTINGS.paramTypes),
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeSqlFormatterSettings(value: unknown): SqlFormatterSettings {
|
||||
const input = isObject(value) ? value : {};
|
||||
const optionSource = isObject(input.options) ? input.options : input;
|
||||
return {
|
||||
...sqlFormatterOptionSettings(optionSource),
|
||||
editor: normalizeSqlFormatterEditorSettings(input.editor),
|
||||
};
|
||||
}
|
||||
|
||||
export function sqlFormatterConfigFile(settings: unknown): SqlFormatterConfigFile {
|
||||
const normalized = normalizeSqlFormatterSettings(settings);
|
||||
return {
|
||||
version: SQL_FORMATTER_CONFIG_VERSION,
|
||||
formatter: SQL_FORMATTER_CONFIG_FORMATTER,
|
||||
options: normalizeSqlFormatterSettings(settings),
|
||||
options: sqlFormatterOptionSettings(normalized),
|
||||
editor: normalizeSqlFormatterEditorSettings(normalized.editor),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -127,16 +429,21 @@ export function parseSqlFormatterConfig(text: string): SqlFormatterConfigParseRe
|
|||
if (parsed.formatter !== SQL_FORMATTER_CONFIG_FORMATTER) return { ok: false, message: "Unsupported formatter." };
|
||||
if (!isObject(parsed.options)) return { ok: false, message: "Config options must be a JSON object." };
|
||||
|
||||
const unknownOption = Object.keys(parsed.options).find((key) => !SQL_FORMATTER_OPTION_KEYS.has(key as keyof SqlFormatterSettings));
|
||||
const unknownOption = Object.keys(parsed.options).find((key) => !SQL_FORMATTER_OPTION_KEYS.has(key as keyof SqlFormatterOptionSettings) && !SQL_FORMATTER_LEGACY_OPTION_KEYS.has(key));
|
||||
if (unknownOption) return { ok: false, message: `Unknown formatter option: ${unknownOption}.` };
|
||||
if ("params" in parsed.options && parsed.options.params !== null) return { ok: false, message: "Unsupported formatter option: params." };
|
||||
|
||||
const invalidOption = Object.entries(parsed.options).find(([key, value]) => {
|
||||
const optionKey = key as keyof SqlFormatterSettings;
|
||||
if (SQL_FORMATTER_LEGACY_OPTION_KEYS.has(key)) return false;
|
||||
const optionKey = key as keyof SqlFormatterOptionSettings;
|
||||
return !SQL_FORMATTER_OPTION_VALIDATORS[optionKey](value);
|
||||
});
|
||||
if (invalidOption) return { ok: false, message: `Invalid formatter option value: ${invalidOption[0]}.` };
|
||||
|
||||
return { ok: true, settings: normalizeSqlFormatterSettings(parsed.options) };
|
||||
const editorError = validateEditorConfig(parsed.editor);
|
||||
if (editorError) return { ok: false, message: editorError };
|
||||
|
||||
return { ok: true, settings: normalizeSqlFormatterSettings({ ...parsed.options, editor: parsed.editor }) };
|
||||
}
|
||||
|
||||
export function syncSqlFormatterConfigDraft(text: string, syncSettings: (settings: SqlFormatterSettings) => void): SqlFormatterConfigParseResult {
|
||||
|
|
@ -146,11 +453,13 @@ export function syncSqlFormatterConfigDraft(text: string, syncSettings: (setting
|
|||
}
|
||||
|
||||
export function sqlFormatterOptions(settings: unknown) {
|
||||
const normalized = normalizeSqlFormatterSettings(settings);
|
||||
const normalized = sqlFormatterOptionSettings(settings);
|
||||
return {
|
||||
keywordCase: normalized.keywordCase,
|
||||
dataTypeCase: normalized.dataTypeCase,
|
||||
functionCase: normalized.functionCase,
|
||||
identifierCase: normalized.identifierCase,
|
||||
indentStyle: normalized.indentStyle,
|
||||
useTabs: normalized.useTabs,
|
||||
tabWidth: normalized.tabWidth,
|
||||
logicalOperatorNewline: normalized.logicalOperatorNewline,
|
||||
|
|
@ -158,5 +467,6 @@ export function sqlFormatterOptions(settings: unknown) {
|
|||
linesBetweenQueries: normalized.linesBetweenQueries,
|
||||
denseOperators: normalized.denseOperators,
|
||||
newlineBeforeSemicolon: normalized.newlineBeforeSemicolon,
|
||||
...(normalized.paramTypes !== null ? { paramTypes: normalized.paramTypes } : {}),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { Command, KeyBinding } from "@codemirror/view";
|
||||
import { normalizeSqlFormatterEditorSettings, sqlFormatterPlatformFromNavigator, sqlFormatterShortcutDisplayKeyToCodeMirrorKey, type SqlFormatterEditorSettings, type SqlFormatterEditorShortcutAction, type SqlFormatterEditorShortcutId } from "@/lib/sqlFormatterConfig";
|
||||
|
||||
export interface SqlFormatterConfigEditorCommands {
|
||||
indentMore: Command;
|
||||
|
|
@ -8,6 +9,9 @@ export interface SqlFormatterConfigEditorCommands {
|
|||
deleteLine: Command;
|
||||
moveLineUp: Command;
|
||||
moveLineDown: Command;
|
||||
undo: Command;
|
||||
redo: Command;
|
||||
selectAll: Command;
|
||||
openSearchPanel: Command;
|
||||
}
|
||||
|
||||
|
|
@ -16,20 +20,75 @@ export interface SqlFormatterConfigEditorActions {
|
|||
formatJson: Command;
|
||||
}
|
||||
|
||||
export function createSqlFormatterConfigKeymap(commands: SqlFormatterConfigEditorCommands, actions: SqlFormatterConfigEditorActions): KeyBinding[] {
|
||||
return [
|
||||
{ key: "Tab", run: commands.indentMore },
|
||||
{ key: "Shift-Tab", run: commands.indentLess },
|
||||
{ key: "Mod-d", run: commands.copyLineDown },
|
||||
{ key: "Shift-Mod-k", run: commands.deleteLine },
|
||||
{ key: "Alt-ArrowUp", run: commands.moveLineUp },
|
||||
{ key: "Alt-ArrowDown", run: commands.moveLineDown },
|
||||
{ key: "Shift-Alt-ArrowUp", run: commands.copyLineUp },
|
||||
{ key: "Shift-Alt-ArrowDown", run: commands.copyLineDown },
|
||||
{ key: "Ctrl-h", mac: "Mod-Alt-f", run: commands.openSearchPanel, preventDefault: true },
|
||||
{ key: "Shift-Alt-f", mac: "Shift-Mod-f", run: actions.formatJson, preventDefault: true },
|
||||
{ key: "Mod-s", run: actions.apply, preventDefault: true },
|
||||
];
|
||||
const shortcutLabelKeys: Record<SqlFormatterEditorShortcutId, string> = {
|
||||
find: "settings.sqlFormatterShortcutFind",
|
||||
replace: "settings.sqlFormatterShortcutReplace",
|
||||
indentMore: "settings.sqlFormatterShortcutIndentMore",
|
||||
indentLess: "settings.sqlFormatterShortcutIndentLess",
|
||||
duplicateLine: "settings.sqlFormatterShortcutDuplicateLine",
|
||||
deleteLine: "settings.sqlFormatterShortcutDeleteLine",
|
||||
moveLineUp: "settings.sqlFormatterShortcutMoveLineUp",
|
||||
moveLineDown: "settings.sqlFormatterShortcutMoveLineDown",
|
||||
copyLineUp: "settings.sqlFormatterShortcutCopyLineUp",
|
||||
copyLineDown: "settings.sqlFormatterShortcutCopyLineDown",
|
||||
undo: "settings.sqlFormatterShortcutUndo",
|
||||
redo: "settings.sqlFormatterShortcutRedo",
|
||||
selectAll: "settings.sqlFormatterShortcutSelectAll",
|
||||
formatJson: "settings.sqlFormatterShortcutFormatJson",
|
||||
applyConfig: "settings.sqlFormatterShortcutApply",
|
||||
};
|
||||
|
||||
export function sqlFormatterConfigShortcutLabelKey(id: SqlFormatterEditorShortcutId): string {
|
||||
return shortcutLabelKeys[id];
|
||||
}
|
||||
|
||||
function commandForAction(commands: SqlFormatterConfigEditorCommands, actions: SqlFormatterConfigEditorActions, action: SqlFormatterEditorShortcutAction): Command {
|
||||
switch (action) {
|
||||
case "indentMore":
|
||||
return commands.indentMore;
|
||||
case "indentLess":
|
||||
return commands.indentLess;
|
||||
case "copyLineDown":
|
||||
return commands.copyLineDown;
|
||||
case "copyLineUp":
|
||||
return commands.copyLineUp;
|
||||
case "deleteLine":
|
||||
return commands.deleteLine;
|
||||
case "moveLineUp":
|
||||
return commands.moveLineUp;
|
||||
case "moveLineDown":
|
||||
return commands.moveLineDown;
|
||||
case "undo":
|
||||
return commands.undo;
|
||||
case "redo":
|
||||
return commands.redo;
|
||||
case "selectAll":
|
||||
return commands.selectAll;
|
||||
case "openSearchPanel":
|
||||
return commands.openSearchPanel;
|
||||
case "formatJson":
|
||||
return actions.formatJson;
|
||||
case "applyJsonDraft":
|
||||
return actions.apply;
|
||||
}
|
||||
}
|
||||
|
||||
export function createSqlFormatterConfigKeymap(commands: SqlFormatterConfigEditorCommands, actions: SqlFormatterConfigEditorActions, editorSettings?: SqlFormatterEditorSettings): KeyBinding[] {
|
||||
const settings = normalizeSqlFormatterEditorSettings(editorSettings);
|
||||
const platforms = new Set(settings.platforms);
|
||||
|
||||
return settings.shortcuts
|
||||
.filter((shortcut) => shortcut.enabled)
|
||||
.map((shortcut) => {
|
||||
const binding: KeyBinding = {
|
||||
run: commandForAction(commands, actions, shortcut.action),
|
||||
preventDefault: true,
|
||||
};
|
||||
if (platforms.has("windows")) binding.win = sqlFormatterShortcutDisplayKeyToCodeMirrorKey(shortcut.keys.windows) ?? undefined;
|
||||
if (platforms.has("linux")) binding.linux = sqlFormatterShortcutDisplayKeyToCodeMirrorKey(shortcut.keys.linux) ?? undefined;
|
||||
if (platforms.has("macos")) binding.mac = sqlFormatterShortcutDisplayKeyToCodeMirrorKey(shortcut.keys.macos) ?? undefined;
|
||||
return binding;
|
||||
});
|
||||
}
|
||||
|
||||
export interface SqlFormatterConfigShortcutRow {
|
||||
|
|
@ -38,37 +97,14 @@ export interface SqlFormatterConfigShortcutRow {
|
|||
shortcut: string;
|
||||
}
|
||||
|
||||
function modLabel(platform = globalThis.navigator?.platform || ""): "Cmd" | "Ctrl" {
|
||||
return platform.toLowerCase().includes("mac") ? "Cmd" : "Ctrl";
|
||||
}
|
||||
export function sqlFormatterConfigShortcutRows(platform = globalThis.navigator?.platform || "", editorSettings?: SqlFormatterEditorSettings): SqlFormatterConfigShortcutRow[] {
|
||||
const platformKey = sqlFormatterPlatformFromNavigator(platform);
|
||||
|
||||
function altLabel(platform = globalThis.navigator?.platform || ""): "Option" | "Alt" {
|
||||
return platform.toLowerCase().includes("mac") ? "Option" : "Alt";
|
||||
}
|
||||
|
||||
export function sqlFormatterConfigShortcutRows(platform = globalThis.navigator?.platform || ""): SqlFormatterConfigShortcutRow[] {
|
||||
const isMac = platform.toLowerCase().includes("mac");
|
||||
const mod = modLabel(platform);
|
||||
const alt = altLabel(platform);
|
||||
|
||||
return [
|
||||
{ id: "find", labelKey: "settings.sqlFormatterShortcutFind", shortcut: `${mod}+F` },
|
||||
{
|
||||
id: "replace",
|
||||
labelKey: "settings.sqlFormatterShortcutReplace",
|
||||
shortcut: isMac ? "Cmd+Option+F" : "Ctrl+H",
|
||||
},
|
||||
{ id: "indentMore", labelKey: "settings.sqlFormatterShortcutIndentMore", shortcut: "Tab" },
|
||||
{ id: "indentLess", labelKey: "settings.sqlFormatterShortcutIndentLess", shortcut: "Shift+Tab" },
|
||||
{ id: "duplicateLine", labelKey: "settings.sqlFormatterShortcutDuplicateLine", shortcut: `${mod}+D` },
|
||||
{ id: "deleteLine", labelKey: "settings.sqlFormatterShortcutDeleteLine", shortcut: `${mod}+Shift+K` },
|
||||
{ id: "moveLine", labelKey: "settings.sqlFormatterShortcutMoveLine", shortcut: `${alt}+Up/Down` },
|
||||
{ id: "copyLine", labelKey: "settings.sqlFormatterShortcutCopyLine", shortcut: `Shift+${alt}+Up/Down` },
|
||||
{
|
||||
id: "formatJson",
|
||||
labelKey: "settings.sqlFormatterShortcutFormatJson",
|
||||
shortcut: isMac ? "Shift+Cmd+F" : "Shift+Alt+F",
|
||||
},
|
||||
{ id: "apply", labelKey: "settings.sqlFormatterShortcutApply", shortcut: `${mod}+S` },
|
||||
];
|
||||
return normalizeSqlFormatterEditorSettings(editorSettings)
|
||||
.shortcuts.filter((shortcut) => shortcut.enabled)
|
||||
.map((shortcut) => ({
|
||||
id: shortcut.id,
|
||||
labelKey: shortcutLabelKeys[shortcut.id],
|
||||
shortcut: shortcut.keys[platformKey],
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
|||
cellDetailDrawerWidth: 320,
|
||||
cellDetailPanelLayout: "bottom",
|
||||
shortcuts: normalizeShortcutSettings(),
|
||||
sqlFormatter: { ...DEFAULT_SQL_FORMATTER_SETTINGS },
|
||||
sqlFormatter: normalizeSqlFormatterSettings(DEFAULT_SQL_FORMATTER_SETTINGS),
|
||||
sidebarActivation: "single",
|
||||
sidebarObjectDisplay: "grouped",
|
||||
autoSelectActiveSidebarNode: false,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
"---设置---",
|
||||
"plugins",
|
||||
"driver-management",
|
||||
"sql-formatter",
|
||||
"keyboard-shortcuts",
|
||||
"connection-import",
|
||||
"config-export",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
"---Settings---",
|
||||
"plugins",
|
||||
"driver-management",
|
||||
"sql-formatter",
|
||||
"keyboard-shortcuts",
|
||||
"connection-import",
|
||||
"config-export",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,225 @@
|
|||
---
|
||||
title: SQL 格式化
|
||||
description: 配置 SQL 格式化选项、导入导出 JSON 配置,并定制 JSON 配置编辑器快捷键。
|
||||
---
|
||||
|
||||
DBX 的 SQL 格式化配置位于**设置 → SQL 格式化**。配置分为两部分:
|
||||
|
||||
- `options`:控制 SQL 格式化结果,例如关键字大小写、缩进、操作符换行和参数类型。
|
||||
- `editor`:控制 SQL 格式化 JSON 配置编辑器中的快捷键。
|
||||
|
||||
## 表单与 JSON
|
||||
|
||||
表单模式适合修改常用选项。JSON 模式适合复制、粘贴、导入或批量修改完整配置。
|
||||
|
||||
JSON 配置合法时,DBX 会把配置同步回表单;配置无效时,会显示具体错误并阻止应用。
|
||||
|
||||
## 快捷键平台
|
||||
|
||||
快捷键配置文件可以同时保存 Windows、Linux 和 macOS 三个平台的按键。
|
||||
|
||||
表单中只显示当前系统的平台列。例如在 macOS 上只显示 macOS 快捷键,避免三端配置同时挤在一个表格里。导入和导出仍会保留完整三端配置,便于在不同系统间迁移。
|
||||
|
||||
## 完整模板 JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"formatter": "sql-formatter",
|
||||
"name": "DBX SQL 格式化配置",
|
||||
"description": "SQL 格式化选项,以及 SQL 格式化 JSON 配置编辑器快捷键模板。",
|
||||
"options": {
|
||||
"keywordCase": "upper",
|
||||
"dataTypeCase": "preserve",
|
||||
"functionCase": "preserve",
|
||||
"identifierCase": "preserve",
|
||||
"indentStyle": "standard",
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"logicalOperatorNewline": "before",
|
||||
"expressionWidth": 80,
|
||||
"linesBetweenQueries": 1,
|
||||
"denseOperators": false,
|
||||
"newlineBeforeSemicolon": false,
|
||||
"paramTypes": null
|
||||
},
|
||||
"editor": {
|
||||
"scope": "sqlFormatterConfigJsonEditor",
|
||||
"platforms": ["windows", "linux", "macos"],
|
||||
"shortcuts": [
|
||||
{
|
||||
"id": "find",
|
||||
"action": "openSearchPanel",
|
||||
"label": "查找",
|
||||
"keys": {
|
||||
"windows": "Ctrl+F",
|
||||
"linux": "Ctrl+F",
|
||||
"macos": "Cmd+F"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "replace",
|
||||
"action": "openSearchPanel",
|
||||
"label": "替换",
|
||||
"keys": {
|
||||
"windows": "Ctrl+H",
|
||||
"linux": "Ctrl+H",
|
||||
"macos": "Cmd+Option+F"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "indentMore",
|
||||
"action": "indentMore",
|
||||
"label": "增加缩进",
|
||||
"keys": {
|
||||
"windows": "Tab",
|
||||
"linux": "Tab",
|
||||
"macos": "Tab"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "indentLess",
|
||||
"action": "indentLess",
|
||||
"label": "减少缩进",
|
||||
"keys": {
|
||||
"windows": "Shift+Tab",
|
||||
"linux": "Shift+Tab",
|
||||
"macos": "Shift+Tab"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "duplicateLine",
|
||||
"action": "copyLineDown",
|
||||
"label": "复制当前行",
|
||||
"keys": {
|
||||
"windows": "Ctrl+D",
|
||||
"linux": "Ctrl+D",
|
||||
"macos": "Cmd+D"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "deleteLine",
|
||||
"action": "deleteLine",
|
||||
"label": "删除当前行",
|
||||
"keys": {
|
||||
"windows": "Ctrl+Shift+K",
|
||||
"linux": "Ctrl+Shift+K",
|
||||
"macos": "Cmd+Shift+K"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "moveLineUp",
|
||||
"action": "moveLineUp",
|
||||
"label": "当前行上移",
|
||||
"keys": {
|
||||
"windows": "Alt+Up",
|
||||
"linux": "Alt+Up",
|
||||
"macos": "Option+Up"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "moveLineDown",
|
||||
"action": "moveLineDown",
|
||||
"label": "当前行下移",
|
||||
"keys": {
|
||||
"windows": "Alt+Down",
|
||||
"linux": "Alt+Down",
|
||||
"macos": "Option+Down"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "copyLineUp",
|
||||
"action": "copyLineUp",
|
||||
"label": "向上复制当前行",
|
||||
"keys": {
|
||||
"windows": "Shift+Alt+Up",
|
||||
"linux": "Shift+Alt+Up",
|
||||
"macos": "Shift+Option+Up"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "copyLineDown",
|
||||
"action": "copyLineDown",
|
||||
"label": "向下复制当前行",
|
||||
"keys": {
|
||||
"windows": "Shift+Alt+Down",
|
||||
"linux": "Shift+Alt+Down",
|
||||
"macos": "Shift+Option+Down"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "undo",
|
||||
"action": "undo",
|
||||
"label": "撤销",
|
||||
"keys": {
|
||||
"windows": "Ctrl+Z",
|
||||
"linux": "Ctrl+Z",
|
||||
"macos": "Cmd+Z"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "redo",
|
||||
"action": "redo",
|
||||
"label": "重做",
|
||||
"keys": {
|
||||
"windows": "Ctrl+Y",
|
||||
"linux": "Ctrl+Shift+Z",
|
||||
"macos": "Cmd+Shift+Z"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "selectAll",
|
||||
"action": "selectAll",
|
||||
"label": "全选",
|
||||
"keys": {
|
||||
"windows": "Ctrl+A",
|
||||
"linux": "Ctrl+A",
|
||||
"macos": "Cmd+A"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "formatJson",
|
||||
"action": "formatJson",
|
||||
"label": "格式化 JSON 配置",
|
||||
"keys": {
|
||||
"windows": "Shift+Alt+F",
|
||||
"linux": "Shift+Alt+F",
|
||||
"macos": "Shift+Cmd+F"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "applyConfig",
|
||||
"action": "applyJsonDraft",
|
||||
"label": "应用 JSON 配置",
|
||||
"keys": {
|
||||
"windows": "Ctrl+S",
|
||||
"linux": "Ctrl+S",
|
||||
"macos": "Cmd+S"
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 兼容说明
|
||||
|
||||
- `version` 当前固定为 `1`。
|
||||
- `formatter` 当前固定为 `sql-formatter`。
|
||||
- `params` 参数替换不会被 SQL 格式化执行;需要参数识别时使用 `paramTypes`。
|
||||
- 旧模板中的 `replace` 动作如果写成 `openReplacePanel`,导入时会自动兼容并归一化。
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
---
|
||||
title: SQL Formatter
|
||||
description: Configure SQL formatting options, import and export JSON config, and customize JSON config editor shortcuts.
|
||||
---
|
||||
|
||||
DBX SQL formatter settings live in **Settings → SQL Formatter**.
|
||||
|
||||
- `options` controls formatting output such as keyword case, indentation, line breaks, and parameter types.
|
||||
- `editor` controls shortcuts used inside the SQL formatter JSON config editor.
|
||||
|
||||
## Form And JSON
|
||||
|
||||
Use the form for common options. Use JSON mode to copy, paste, import, export, or edit the full config.
|
||||
|
||||
When the JSON config is valid, DBX syncs it back to the form. Invalid JSON shows an error and cannot be applied.
|
||||
|
||||
## Shortcut Platform
|
||||
|
||||
The config file can store Windows, Linux, and macOS shortcuts at the same time.
|
||||
|
||||
The form only shows the current system column. For example, macOS shows only macOS shortcuts. Import and export still preserve all platform keys.
|
||||
|
||||
## Full Template JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"formatter": "sql-formatter",
|
||||
"name": "DBX SQL Formatter Config",
|
||||
"description": "SQL formatter options and SQL formatter JSON config editor shortcut template.",
|
||||
"options": {
|
||||
"keywordCase": "upper",
|
||||
"dataTypeCase": "preserve",
|
||||
"functionCase": "preserve",
|
||||
"identifierCase": "preserve",
|
||||
"indentStyle": "standard",
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"logicalOperatorNewline": "before",
|
||||
"expressionWidth": 80,
|
||||
"linesBetweenQueries": 1,
|
||||
"denseOperators": false,
|
||||
"newlineBeforeSemicolon": false,
|
||||
"paramTypes": null
|
||||
},
|
||||
"editor": {
|
||||
"scope": "sqlFormatterConfigJsonEditor",
|
||||
"platforms": ["windows", "linux", "macos"],
|
||||
"shortcuts": [
|
||||
{
|
||||
"id": "find",
|
||||
"action": "openSearchPanel",
|
||||
"label": "Find",
|
||||
"keys": {
|
||||
"windows": "Ctrl+F",
|
||||
"linux": "Ctrl+F",
|
||||
"macos": "Cmd+F"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "replace",
|
||||
"action": "openSearchPanel",
|
||||
"label": "Replace",
|
||||
"keys": {
|
||||
"windows": "Ctrl+H",
|
||||
"linux": "Ctrl+H",
|
||||
"macos": "Cmd+Option+F"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "indentMore",
|
||||
"action": "indentMore",
|
||||
"label": "Indent more",
|
||||
"keys": {
|
||||
"windows": "Tab",
|
||||
"linux": "Tab",
|
||||
"macos": "Tab"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "indentLess",
|
||||
"action": "indentLess",
|
||||
"label": "Indent less",
|
||||
"keys": {
|
||||
"windows": "Shift+Tab",
|
||||
"linux": "Shift+Tab",
|
||||
"macos": "Shift+Tab"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "duplicateLine",
|
||||
"action": "copyLineDown",
|
||||
"label": "Duplicate current line",
|
||||
"keys": {
|
||||
"windows": "Ctrl+D",
|
||||
"linux": "Ctrl+D",
|
||||
"macos": "Cmd+D"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "deleteLine",
|
||||
"action": "deleteLine",
|
||||
"label": "Delete current line",
|
||||
"keys": {
|
||||
"windows": "Ctrl+Shift+K",
|
||||
"linux": "Ctrl+Shift+K",
|
||||
"macos": "Cmd+Shift+K"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "moveLineUp",
|
||||
"action": "moveLineUp",
|
||||
"label": "Move line up",
|
||||
"keys": {
|
||||
"windows": "Alt+Up",
|
||||
"linux": "Alt+Up",
|
||||
"macos": "Option+Up"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "moveLineDown",
|
||||
"action": "moveLineDown",
|
||||
"label": "Move line down",
|
||||
"keys": {
|
||||
"windows": "Alt+Down",
|
||||
"linux": "Alt+Down",
|
||||
"macos": "Option+Down"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "copyLineUp",
|
||||
"action": "copyLineUp",
|
||||
"label": "Copy line up",
|
||||
"keys": {
|
||||
"windows": "Shift+Alt+Up",
|
||||
"linux": "Shift+Alt+Up",
|
||||
"macos": "Shift+Option+Up"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "copyLineDown",
|
||||
"action": "copyLineDown",
|
||||
"label": "Copy line down",
|
||||
"keys": {
|
||||
"windows": "Shift+Alt+Down",
|
||||
"linux": "Shift+Alt+Down",
|
||||
"macos": "Shift+Option+Down"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "undo",
|
||||
"action": "undo",
|
||||
"label": "Undo",
|
||||
"keys": {
|
||||
"windows": "Ctrl+Z",
|
||||
"linux": "Ctrl+Z",
|
||||
"macos": "Cmd+Z"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "redo",
|
||||
"action": "redo",
|
||||
"label": "Redo",
|
||||
"keys": {
|
||||
"windows": "Ctrl+Y",
|
||||
"linux": "Ctrl+Shift+Z",
|
||||
"macos": "Cmd+Shift+Z"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "selectAll",
|
||||
"action": "selectAll",
|
||||
"label": "Select all",
|
||||
"keys": {
|
||||
"windows": "Ctrl+A",
|
||||
"linux": "Ctrl+A",
|
||||
"macos": "Cmd+A"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "formatJson",
|
||||
"action": "formatJson",
|
||||
"label": "Format JSON config",
|
||||
"keys": {
|
||||
"windows": "Shift+Alt+F",
|
||||
"linux": "Shift+Alt+F",
|
||||
"macos": "Shift+Cmd+F"
|
||||
},
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "applyConfig",
|
||||
"action": "applyJsonDraft",
|
||||
"label": "Apply JSON config",
|
||||
"keys": {
|
||||
"windows": "Ctrl+S",
|
||||
"linux": "Ctrl+S",
|
||||
"macos": "Cmd+S"
|
||||
},
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
- `version` is currently `1`.
|
||||
- `formatter` is currently `sql-formatter`.
|
||||
- `params` replacement is not executed by SQL formatting. Use `paramTypes` when parameter recognition is needed.
|
||||
- Legacy templates that use `openReplacePanel` for the `replace` action are accepted and normalized on import.
|
||||
|
|
@ -296,18 +296,7 @@ test("normalizeEditorSettings keeps valid UI scales with two-decimal precision",
|
|||
});
|
||||
|
||||
test("defaults SQL formatter settings", () => {
|
||||
assert.deepEqual(DEFAULT_EDITOR_SETTINGS.sqlFormatter, {
|
||||
keywordCase: "upper",
|
||||
dataTypeCase: "preserve",
|
||||
functionCase: "preserve",
|
||||
useTabs: false,
|
||||
tabWidth: 2,
|
||||
logicalOperatorNewline: "before",
|
||||
expressionWidth: 50,
|
||||
linesBetweenQueries: 1,
|
||||
denseOperators: false,
|
||||
newlineBeforeSemicolon: false,
|
||||
});
|
||||
assert.deepEqual(DEFAULT_EDITOR_SETTINGS.sqlFormatter, DEFAULT_SQL_FORMATTER_SETTINGS);
|
||||
assert.deepEqual(normalizeEditorSettings({}).sqlFormatter, DEFAULT_EDITOR_SETTINGS.sqlFormatter);
|
||||
});
|
||||
|
||||
|
|
@ -328,6 +317,7 @@ test("normalizes saved SQL formatter settings", () => {
|
|||
},
|
||||
} as any).sqlFormatter,
|
||||
{
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
keywordCase: "lower",
|
||||
functionCase: "upper",
|
||||
dataTypeCase: "upper",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { DEFAULT_SQL_FORMATTER_SETTINGS, parseSqlFormatterConfig, serializeSqlFormatterConfig, normalizeSqlFormatterSettings, syncSqlFormatterConfigDraft, sqlFormatterOptions } from "../../apps/desktop/src/lib/sqlFormatterConfig.ts";
|
||||
import { DEFAULT_SQL_FORMATTER_SETTINGS, normalizeSqlFormatterSettings, parseSqlFormatterConfig, serializeSqlFormatterConfig, sqlFormatterOptions, syncSqlFormatterConfigDraft, validateSqlFormatterEditorSettings } from "../../apps/desktop/src/lib/sqlFormatterConfig.ts";
|
||||
|
||||
const { editor: defaultEditorSettings, ...defaultOptionSettings } = DEFAULT_SQL_FORMATTER_SETTINGS;
|
||||
|
||||
test("normalizes empty formatter settings to defaults", () => {
|
||||
assert.deepEqual(normalizeSqlFormatterSettings({}), DEFAULT_SQL_FORMATTER_SETTINGS);
|
||||
|
|
@ -11,6 +13,8 @@ test("keeps valid formatter settings and clamps invalid values", () => {
|
|||
keywordCase: "lower",
|
||||
dataTypeCase: "upper",
|
||||
functionCase: "lower",
|
||||
identifierCase: "upper",
|
||||
indentStyle: "tabularLeft",
|
||||
useTabs: true,
|
||||
tabWidth: 4,
|
||||
logicalOperatorNewline: "after",
|
||||
|
|
@ -18,12 +22,16 @@ test("keeps valid formatter settings and clamps invalid values", () => {
|
|||
linesBetweenQueries: 2,
|
||||
denseOperators: true,
|
||||
newlineBeforeSemicolon: true,
|
||||
paramTypes: { named: [":"], custom: [{ regex: "\\{\\w+\\}" }] },
|
||||
});
|
||||
|
||||
assert.deepEqual(settings, {
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
keywordCase: "lower",
|
||||
dataTypeCase: "upper",
|
||||
functionCase: "lower",
|
||||
identifierCase: "upper",
|
||||
indentStyle: "tabularLeft",
|
||||
useTabs: true,
|
||||
tabWidth: 4,
|
||||
logicalOperatorNewline: "after",
|
||||
|
|
@ -31,6 +39,7 @@ test("keeps valid formatter settings and clamps invalid values", () => {
|
|||
linesBetweenQueries: 2,
|
||||
denseOperators: true,
|
||||
newlineBeforeSemicolon: true,
|
||||
paramTypes: { named: [":"], custom: [{ regex: "\\{\\w+\\}" }] },
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
|
|
@ -38,6 +47,8 @@ test("keeps valid formatter settings and clamps invalid values", () => {
|
|||
keywordCase: "camel",
|
||||
dataTypeCase: "invalid",
|
||||
functionCase: "invalid",
|
||||
identifierCase: "invalid",
|
||||
indentStyle: "wide",
|
||||
useTabs: "yes",
|
||||
tabWidth: 99,
|
||||
logicalOperatorNewline: "middle",
|
||||
|
|
@ -45,8 +56,16 @@ test("keeps valid formatter settings and clamps invalid values", () => {
|
|||
linesBetweenQueries: 9,
|
||||
denseOperators: "true",
|
||||
newlineBeforeSemicolon: "false",
|
||||
paramTypes: { named: ["#"] },
|
||||
editor: { shortcuts: [{ id: "duplicateLine", keys: { windows: "Ctrl+W", linux: "Ctrl+W", macos: "Cmd+W" }, enabled: false }] },
|
||||
}),
|
||||
DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
{
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
editor: {
|
||||
...defaultEditorSettings,
|
||||
shortcuts: defaultEditorSettings.shortcuts.map((shortcut) => (shortcut.id === "duplicateLine" ? { ...shortcut, keys: { windows: "Ctrl+W", linux: "Ctrl+W", macos: "Cmd+W" }, enabled: false } : shortcut)),
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -62,9 +81,10 @@ test("serializes formatter config as a stable versioned envelope", () => {
|
|||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
...defaultOptionSettings,
|
||||
keywordCase: "lower",
|
||||
},
|
||||
editor: defaultEditorSettings,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
|
|
@ -82,6 +102,8 @@ test("parses valid formatter config files", () => {
|
|||
keywordCase: "lower",
|
||||
functionCase: "upper",
|
||||
dataTypeCase: "preserve",
|
||||
identifierCase: "lower",
|
||||
indentStyle: "tabularRight",
|
||||
useTabs: false,
|
||||
tabWidth: 4,
|
||||
logicalOperatorNewline: "after",
|
||||
|
|
@ -89,15 +111,24 @@ test("parses valid formatter config files", () => {
|
|||
linesBetweenQueries: 0,
|
||||
denseOperators: false,
|
||||
newlineBeforeSemicolon: true,
|
||||
paramTypes: { named: [":"], quoted: ["@"], positional: true },
|
||||
},
|
||||
editor: {
|
||||
scope: "sqlFormatterConfigJsonEditor",
|
||||
platforms: ["windows", "macos"],
|
||||
shortcuts: [{ id: "duplicateLine", action: "copyLineDown", keys: { windows: "Ctrl+W", linux: "Ctrl+W", macos: "Cmd+W" }, enabled: true }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.deepEqual(result.settings, {
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
keywordCase: "lower",
|
||||
functionCase: "upper",
|
||||
dataTypeCase: "preserve",
|
||||
identifierCase: "lower",
|
||||
indentStyle: "tabularRight",
|
||||
useTabs: false,
|
||||
tabWidth: 4,
|
||||
logicalOperatorNewline: "after",
|
||||
|
|
@ -105,9 +136,56 @@ test("parses valid formatter config files", () => {
|
|||
linesBetweenQueries: 0,
|
||||
denseOperators: false,
|
||||
newlineBeforeSemicolon: true,
|
||||
paramTypes: { named: [":"], quoted: ["@"], positional: true },
|
||||
editor: {
|
||||
...defaultEditorSettings,
|
||||
platforms: ["windows", "macos"],
|
||||
shortcuts: defaultEditorSettings.shortcuts.map((shortcut) => (shortcut.id === "duplicateLine" ? { ...shortcut, keys: { windows: "Ctrl+W", linux: "Ctrl+W", macos: "Cmd+W" } } : shortcut)),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("parses user-facing editor shortcut actions as known shortcut ids", () => {
|
||||
for (const action of ["replace", "openReplacePanel"]) {
|
||||
const result = parseSqlFormatterConfig(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {},
|
||||
editor: {
|
||||
shortcuts: [{ id: "replace", action, keys: { windows: "Ctrl+R", linux: "Ctrl+R", macos: "Cmd+R" }, enabled: true }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
if (result.ok) {
|
||||
const shortcut = result.settings.editor.shortcuts.find((item) => item.id === "replace");
|
||||
assert.equal(shortcut?.action, "openSearchPanel");
|
||||
assert.deepEqual(shortcut?.keys, { windows: "Ctrl+R", linux: "Ctrl+R", macos: "Cmd+R" });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("parses partial editor shortcut keys and fills missing platforms from defaults", () => {
|
||||
const result = parseSqlFormatterConfig(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {},
|
||||
editor: {
|
||||
shortcuts: [{ id: "replace", keys: { windows: "Ctrl+R" }, enabled: true }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
if (result.ok) {
|
||||
const shortcut = result.settings.editor.shortcuts.find((item) => item.id === "replace");
|
||||
assert.deepEqual(shortcut?.keys, { windows: "Ctrl+R", linux: "Ctrl+H", macos: "Cmd+Option+F" });
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects malformed formatter config files", () => {
|
||||
assert.deepEqual(parseSqlFormatterConfig("{bad json").ok, false);
|
||||
assert.deepEqual(parseSqlFormatterConfig(JSON.stringify({ version: 2, formatter: "sql-formatter", options: {} })).ok, false);
|
||||
|
|
@ -127,6 +205,68 @@ test("rejects invalid known formatter option values when parsing config files",
|
|||
const invalidNumericChoice = parseSqlFormatterConfig(JSON.stringify({ version: 1, formatter: "sql-formatter", options: { tabWidth: 3 } }));
|
||||
assert.equal(invalidNumericChoice.ok, false);
|
||||
if (!invalidNumericChoice.ok) assert.match(invalidNumericChoice.message, /tabWidth/);
|
||||
|
||||
const invalidParams = parseSqlFormatterConfig(JSON.stringify({ version: 1, formatter: "sql-formatter", options: { params: ["42"] } }));
|
||||
assert.equal(invalidParams.ok, false);
|
||||
if (!invalidParams.ok) assert.match(invalidParams.message, /params/);
|
||||
|
||||
const legacyNullParams = parseSqlFormatterConfig(JSON.stringify({ version: 1, formatter: "sql-formatter", options: { params: null } }));
|
||||
assert.equal(legacyNullParams.ok, true);
|
||||
|
||||
const invalidParamTypes = parseSqlFormatterConfig(JSON.stringify({ version: 1, formatter: "sql-formatter", options: { paramTypes: { custom: [{ regex: "" }] } } }));
|
||||
assert.equal(invalidParamTypes.ok, false);
|
||||
if (!invalidParamTypes.ok) assert.match(invalidParamTypes.message, /paramTypes/);
|
||||
});
|
||||
|
||||
test("rejects invalid editor shortcut config", () => {
|
||||
const unknownShortcut = parseSqlFormatterConfig(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {},
|
||||
editor: { shortcuts: [{ id: "unknown", action: "copyLineDown", keys: { windows: "Ctrl+D", linux: "Ctrl+D", macos: "Cmd+D" }, enabled: true }] },
|
||||
}),
|
||||
);
|
||||
assert.equal(unknownShortcut.ok, false);
|
||||
if (!unknownShortcut.ok) assert.match(unknownShortcut.message, /unknown/);
|
||||
|
||||
const invalidKeys = parseSqlFormatterConfig(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {},
|
||||
editor: { shortcuts: [{ id: "duplicateLine", action: "copyLineDown", keys: { windows: "", linux: "Ctrl+D", macos: "Cmd+D" }, enabled: true }] },
|
||||
}),
|
||||
);
|
||||
assert.equal(invalidKeys.ok, false);
|
||||
if (!invalidKeys.ok) assert.match(invalidKeys.message, /duplicateLine/);
|
||||
|
||||
const duplicateKeys = parseSqlFormatterConfig(
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {},
|
||||
editor: {
|
||||
shortcuts: [
|
||||
{ id: "find", action: "openSearchPanel", keys: { windows: "Ctrl+D", linux: "Ctrl+F", macos: "Cmd+F" }, enabled: true },
|
||||
{ id: "duplicateLine", action: "copyLineDown", keys: { windows: "Ctrl+D", linux: "Ctrl+D", macos: "Cmd+D" }, enabled: true },
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
assert.equal(duplicateKeys.ok, false);
|
||||
if (!duplicateKeys.ok) assert.match(duplicateKeys.message, /Duplicate/);
|
||||
});
|
||||
|
||||
test("validates editor shortcut settings outside config import", () => {
|
||||
assert.deepEqual(validateSqlFormatterEditorSettings(DEFAULT_SQL_FORMATTER_SETTINGS.editor), { ok: true });
|
||||
assert.equal(
|
||||
validateSqlFormatterEditorSettings({
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS.editor,
|
||||
shortcuts: DEFAULT_SQL_FORMATTER_SETTINGS.editor.shortcuts.map((shortcut) => (shortcut.id === "duplicateLine" ? { ...shortcut, keys: { ...shortcut.keys, windows: "Ctrl+" } } : shortcut)),
|
||||
}).ok,
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("syncs valid JSON drafts so outer settings apply can persist them", () => {
|
||||
|
|
@ -136,10 +276,11 @@ test("syncs valid JSON drafts so outer settings apply can persist them", () => {
|
|||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
...defaultOptionSettings,
|
||||
keywordCase: "lower",
|
||||
tabWidth: 4,
|
||||
},
|
||||
editor: defaultEditorSettings,
|
||||
}),
|
||||
(settings) => {
|
||||
synced = settings;
|
||||
|
|
@ -161,9 +302,10 @@ test("does not sync invalid JSON drafts", () => {
|
|||
version: 1,
|
||||
formatter: "sql-formatter",
|
||||
options: {
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
...defaultOptionSettings,
|
||||
keywordCase: "camel",
|
||||
},
|
||||
editor: defaultEditorSettings,
|
||||
}),
|
||||
(settings) => {
|
||||
synced = settings;
|
||||
|
|
@ -186,6 +328,8 @@ test("maps DBX formatter settings to sql-formatter options", () => {
|
|||
keywordCase: "lower",
|
||||
dataTypeCase: "preserve",
|
||||
functionCase: "preserve",
|
||||
identifierCase: "preserve",
|
||||
indentStyle: "standard",
|
||||
useTabs: true,
|
||||
tabWidth: 2,
|
||||
logicalOperatorNewline: "before",
|
||||
|
|
@ -195,4 +339,26 @@ test("maps DBX formatter settings to sql-formatter options", () => {
|
|||
newlineBeforeSemicolon: true,
|
||||
},
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
sqlFormatterOptions({
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS,
|
||||
paramTypes: { positional: true },
|
||||
}),
|
||||
{
|
||||
keywordCase: "upper",
|
||||
dataTypeCase: "preserve",
|
||||
functionCase: "preserve",
|
||||
identifierCase: "preserve",
|
||||
indentStyle: "standard",
|
||||
useTabs: false,
|
||||
tabWidth: 2,
|
||||
logicalOperatorNewline: "before",
|
||||
expressionWidth: 50,
|
||||
linesBetweenQueries: 1,
|
||||
denseOperators: false,
|
||||
newlineBeforeSemicolon: false,
|
||||
paramTypes: { positional: true },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { DEFAULT_SQL_FORMATTER_SETTINGS } from "../../apps/desktop/src/lib/sqlFormatterConfig.ts";
|
||||
import { createSqlFormatterConfigKeymap, sqlFormatterConfigShortcutRows } from "../../apps/desktop/src/lib/sqlFormatterConfigEditor.ts";
|
||||
|
||||
const commands = {
|
||||
|
|
@ -10,6 +11,9 @@ const commands = {
|
|||
deleteLine: () => true,
|
||||
moveLineUp: () => true,
|
||||
moveLineDown: () => true,
|
||||
undo: () => true,
|
||||
redo: () => true,
|
||||
selectAll: () => true,
|
||||
openSearchPanel: () => true,
|
||||
};
|
||||
const actions = {
|
||||
|
|
@ -18,39 +22,45 @@ const actions = {
|
|||
};
|
||||
const otherCommand = () => true;
|
||||
|
||||
function createBindings() {
|
||||
return createSqlFormatterConfigKeymap(commands, actions);
|
||||
function createBindings(editorSettings = DEFAULT_SQL_FORMATTER_SETTINGS.editor) {
|
||||
return createSqlFormatterConfigKeymap(commands, actions, editorSettings);
|
||||
}
|
||||
|
||||
test("builds common SQL formatter config editor key bindings", () => {
|
||||
const bindings = createBindings();
|
||||
|
||||
assert.deepEqual(
|
||||
bindings.map((binding) => binding.key),
|
||||
["Tab", "Shift-Tab", "Mod-d", "Shift-Mod-k", "Alt-ArrowUp", "Alt-ArrowDown", "Shift-Alt-ArrowUp", "Shift-Alt-ArrowDown", "Ctrl-h", "Shift-Alt-f", "Mod-s"],
|
||||
bindings.map((binding) => binding.win),
|
||||
["Ctrl-f", "Ctrl-h", "Tab", "Shift-Tab", "Ctrl-d", "Ctrl-Shift-k", "Alt-ArrowUp", "Alt-ArrowDown", "Shift-Alt-ArrowUp", "Shift-Alt-ArrowDown", "Ctrl-z", "Ctrl-y", "Ctrl-a", "Shift-Alt-f", "Ctrl-s"],
|
||||
);
|
||||
assert.deepEqual(
|
||||
bindings.map((binding) => binding.linux),
|
||||
["Ctrl-f", "Ctrl-h", "Tab", "Shift-Tab", "Ctrl-d", "Ctrl-Shift-k", "Alt-ArrowUp", "Alt-ArrowDown", "Shift-Alt-ArrowUp", "Shift-Alt-ArrowDown", "Ctrl-z", "Ctrl-Shift-z", "Ctrl-a", "Shift-Alt-f", "Ctrl-s"],
|
||||
);
|
||||
});
|
||||
|
||||
test("sets search, format, and apply binding details", () => {
|
||||
const bindings = createBindings();
|
||||
const searchBinding = bindings.find((binding) => binding.key === "Ctrl-h");
|
||||
const formatBinding = bindings.find((binding) => binding.key === "Shift-Alt-f");
|
||||
const applyBinding = bindings.find((binding) => binding.key === "Mod-s");
|
||||
const searchBinding = bindings.find((binding) => binding.win === "Ctrl-h");
|
||||
const formatBinding = bindings.find((binding) => binding.win === "Shift-Alt-f");
|
||||
const applyBinding = bindings.find((binding) => binding.win === "Ctrl-s");
|
||||
|
||||
assert.equal(searchBinding?.mac, "Mod-Alt-f");
|
||||
assert.equal(searchBinding?.linux, "Ctrl-h");
|
||||
assert.equal(searchBinding?.preventDefault, true);
|
||||
assert.equal(searchBinding?.run, commands.openSearchPanel);
|
||||
assert.equal(formatBinding?.mac, "Shift-Mod-f");
|
||||
assert.equal(formatBinding?.preventDefault, true);
|
||||
assert.equal(formatBinding?.run, actions.formatJson);
|
||||
assert.equal(applyBinding?.mac, "Mod-s");
|
||||
assert.equal(applyBinding?.preventDefault, true);
|
||||
assert.equal(applyBinding?.run, actions.apply);
|
||||
});
|
||||
|
||||
test("keeps duplicate line before later Mod-d search bindings", () => {
|
||||
const bindings = [...createBindings(), { key: "Mod-d", run: otherCommand }];
|
||||
const duplicateLineIndex = bindings.findIndex((binding) => binding.key === "Mod-d" && binding.run === commands.copyLineDown);
|
||||
const searchKeymapIndex = bindings.findIndex((binding) => binding.key === "Mod-d" && binding.run === otherCommand);
|
||||
test("keeps duplicate line before later Ctrl-d search bindings", () => {
|
||||
const bindings = [...createBindings(), { win: "Ctrl-d", run: otherCommand }];
|
||||
const duplicateLineIndex = bindings.findIndex((binding) => binding.win === "Ctrl-d" && binding.run === commands.copyLineDown);
|
||||
const searchKeymapIndex = bindings.findIndex((binding) => binding.win === "Ctrl-d" && binding.run === otherCommand);
|
||||
|
||||
assert.notEqual(duplicateLineIndex, -1);
|
||||
assert.notEqual(searchKeymapIndex, -1);
|
||||
|
|
@ -58,6 +68,31 @@ test("keeps duplicate line before later Mod-d search bindings", () => {
|
|||
assert.ok(duplicateLineIndex < searchKeymapIndex);
|
||||
});
|
||||
|
||||
test("uses customized shortcut keys and skips disabled shortcuts", () => {
|
||||
const editorSettings = {
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS.editor,
|
||||
shortcuts: DEFAULT_SQL_FORMATTER_SETTINGS.editor.shortcuts.map((shortcut) => {
|
||||
if (shortcut.id === "duplicateLine") return { ...shortcut, keys: { windows: "Ctrl+W", linux: "Ctrl+W", macos: "Cmd+W" } };
|
||||
if (shortcut.id === "deleteLine") return { ...shortcut, enabled: false };
|
||||
return shortcut;
|
||||
}),
|
||||
};
|
||||
|
||||
const bindings = createBindings(editorSettings);
|
||||
assert.equal(bindings.find((binding) => binding.win === "Ctrl-w")?.run, commands.copyLineDown);
|
||||
assert.equal(bindings.find((binding) => binding.linux === "Ctrl-w")?.run, commands.copyLineDown);
|
||||
assert.equal(bindings.some((binding) => binding.win === "Ctrl-Shift-k"), false);
|
||||
});
|
||||
|
||||
test("honors enabled platforms when building key bindings", () => {
|
||||
const bindings = createBindings({
|
||||
...DEFAULT_SQL_FORMATTER_SETTINGS.editor,
|
||||
platforms: ["linux"],
|
||||
});
|
||||
|
||||
assert.equal(bindings.every((binding) => !binding.win && !binding.mac && !!binding.linux), true);
|
||||
});
|
||||
|
||||
test("shows platform-aware shortcut labels", () => {
|
||||
const windowsRows = sqlFormatterConfigShortcutRows("Win32");
|
||||
const macRows = sqlFormatterConfigShortcutRows("MacIntel");
|
||||
|
|
@ -66,6 +101,6 @@ test("shows platform-aware shortcut labels", () => {
|
|||
assert.equal(macRows.find((row) => row.id === "duplicateLine")?.shortcut, "Cmd+D");
|
||||
assert.equal(windowsRows.find((row) => row.id === "formatJson")?.shortcut, "Shift+Alt+F");
|
||||
assert.equal(macRows.find((row) => row.id === "formatJson")?.shortcut, "Shift+Cmd+F");
|
||||
assert.equal(windowsRows.find((row) => row.id === "apply")?.shortcut, "Ctrl+S");
|
||||
assert.equal(macRows.find((row) => row.id === "apply")?.shortcut, "Cmd+S");
|
||||
assert.equal(windowsRows.find((row) => row.id === "applyConfig")?.shortcut, "Ctrl+S");
|
||||
assert.equal(macRows.find((row) => row.id === "applyConfig")?.shortcut, "Cmd+S");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue