feat(editor): add SQL editor context menu shortcuts

* feat(editor): add SQL editor context menu shortcuts

* chore(i18n): autofill new translations

---------

Co-authored-by: faithererer <faithererer@users.noreply.github.com>
Co-authored-by: dbx-i18n-bot <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-07-05 19:38:51 +08:00
1 parent d502b4ce6c
commit 8b035d0989
17 files changed
+207 -108

No files matched your search

@@ -68,6 +68,7 @@ import {
} from "@/lib/backend/api";
import { eventToShortcut } from "@/lib/editor/keyboardShortcuts";
import { SHORTCUT_DEFINITIONS, findShortcutConflict, normalizeShortcutSettings, type ShortcutActionId } from "@/lib/editor/shortcutRegistry";
import { formatShortcutDisplay } from "@/lib/editor/shortcutDisplay";
import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebar/sidebarTableNameDisplay";
import { normalizeSqlFormatterSettings, type SqlFormatterSettings } from "@/lib/sql/sqlFormatterConfig";
import { EMPTY_TABLE_COLUMN_TEMPLATE_DATA_TYPE, parseTableColumnTemplateFields, TABLE_COLUMN_TEMPLATE_DATABASE_TYPES } from "@/lib/table/tableColumnTemplates";
@@ -590,7 +591,26 @@ const shortcutConflicts = computed(() =>
return conflict ? [definition.id] : [];
}),
);
const formatterEditorShortcutIds: ShortcutActionId[] = ["formatSql", "find", "replace", "saveSql", "acceptCompletion", "indentMore", "indentLess", "duplicateLine", "deleteLine", "moveLineUp", "moveLineDown", "copyLineUp", "copyLineDown", "undo", "redo", "selectAll"];
const formatterEditorShortcutIds: ShortcutActionId[] = [
"formatSql",
"find",
"replace",
"saveSql",
"acceptCompletion",
"indentMore",
"indentLess",
"duplicateLine",
"deleteLine",
"moveLineUp",
"moveLineDown",
"copyLineUp",
"copyLineDown",
"undo",
"redo",
"selectAll",
"uppercaseSelection",
"lowercaseSelection",
];
const formatterEditorShortcutDefinitions = computed(() => formatterEditorShortcutIds.map((id) => SHORTCUT_DEFINITIONS.find((definition) => definition.id === id)).filter((definition): definition is (typeof SHORTCUT_DEFINITIONS)[number] => !!definition));
const hasShortcutConflicts = computed(() => shortcutConflicts.value.length > 0);
const shortcutsChanged = computed(() => JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts));
@@ -1082,29 +1102,7 @@ function onShortcutKeydown(actionId: ShortcutActionId, event: KeyboardEvent) {
}
function formatShortcutPill(shortcut: string): string {
if (!shortcut) return "—";
const isMac = globalThis.navigator?.platform?.toLowerCase().includes("mac") ?? false;
return shortcut
.split("+")
.filter(Boolean)
.map((part) => {
if (part === "Mod") return isMac ? "⌘" : "Ctrl";
if (part === "Meta") return isMac ? "⌘" : "Meta";
if (part === "Shift") return isMac ? "⇧" : "Shift";
if (part === "Alt") return isMac ? "⌥" : "Alt";
if (part === "Control" || part === "Ctrl") return isMac ? "⌃" : "Ctrl";
if (part === "Enter") return "↵";
if (part === "Backspace") return "⌫";
if (part === "Delete") return isMac ? "⌦" : "Del";
if (part === "Escape") return "Esc";
if (part === "ArrowUp") return "↑";
if (part === "ArrowDown") return "↓";
if (part === "ArrowLeft") return "←";
if (part === "ArrowRight") return "→";
if (part === " ") return "Space";
return part.length === 1 ? part.toUpperCase() : part;
})
.join(isMac ? " " : " + ");
return formatShortcutDisplay(shortcut);
}
const shortcutPressShortcutLabel = computed(() => t("settings.shortcutPressShortcut"));
@@ -566,10 +566,10 @@ function selectAllSqlFromContextMenu() {
focusEditor();
}
function convertSelectedSqlCaseFromContextMenu(mode: SelectionCaseMode) {
function convertSelectedSqlCase(mode: SelectionCaseMode): boolean {
const currentView = view.value;
const EditorSelection = codeMirrorEditorSelection;
if (!currentView || !EditorSelection || !canCopySelectedSql.value) return;
if (!currentView || !EditorSelection) return false;
const state = currentView.state;
const transaction = state.changeByRange((range) => {
@@ -585,8 +585,10 @@ function convertSelectedSqlCaseFromContextMenu(mode: SelectionCaseMode) {
if (!transaction.changes.empty) {
currentView.dispatch({ ...transaction, scrollIntoView: true, userEvent: "input" });
focusEditor();
return true;
}
focusEditor();
return false;
}
function openTableFromContextMenu() {
@@ -637,52 +639,59 @@ function selectSqlLineFromGutter(currentView: EditorViewType, line: { from: numb
return true;
}
const contextMenuItems = computed<ContextMenuItem[]>(() => [
{
label: executeContextMenuLabel.value,
action: executeFromContextMenu,
disabled: !canExecuteContextSql.value,
icon: Play,
},
{
label: t("contextMenu.viewData"),
action: openTableFromContextMenu,
disabled: !contextTableName.value,
icon: Table2,
},
{
label: t("contextMenu.editStructure"),
action: editTableStructureFromContextMenu,
disabled: !contextTableName.value,
icon: PencilRuler,
},
{
label: t("contextMenu.viewDdl"),
action: openTableDdlFromContextMenu,
disabled: !contextTableName.value,
icon: FileCode,
},
{ label: "", separator: true },
{
label: t("editor.contextMenu.copySelection"),
action: copySelectedSqlFromContextMenu,
disabled: !canCopySelectedSql.value,
icon: Copy,
},
{
label: t("editor.contextMenu.uppercaseSelection"),
action: () => convertSelectedSqlCaseFromContextMenu("upper"),
disabled: !canCopySelectedSql.value,
icon: CaseUpper,
},
{
label: t("editor.contextMenu.lowercaseSelection"),
action: () => convertSelectedSqlCaseFromContextMenu("lower"),
disabled: !canCopySelectedSql.value,
icon: CaseLower,
},
{ label: t("editor.contextMenu.selectAll"), action: selectAllSqlFromContextMenu, icon: TextSelect },
]);
const contextMenuItems = computed<ContextMenuItem[]>(() => {
const shortcuts = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);
return [
{
label: executeContextMenuLabel.value,
action: executeFromContextMenu,
disabled: !canExecuteContextSql.value,
icon: Play,
shortcut: shortcuts.executeSql,
},
{
label: t("contextMenu.viewData"),
action: openTableFromContextMenu,
disabled: !contextTableName.value,
icon: Table2,
},
{
label: t("contextMenu.editStructure"),
action: editTableStructureFromContextMenu,
disabled: !contextTableName.value,
icon: PencilRuler,
},
{
label: t("contextMenu.viewDdl"),
action: openTableDdlFromContextMenu,
disabled: !contextTableName.value,
icon: FileCode,
},
{ label: "", separator: true },
{
label: t("editor.contextMenu.copySelection"),
action: copySelectedSqlFromContextMenu,
disabled: !canCopySelectedSql.value,
icon: Copy,
shortcut: "Mod+C",
},
{
label: t("editor.contextMenu.uppercaseSelection"),
action: () => convertSelectedSqlCase("upper"),
disabled: !canCopySelectedSql.value,
icon: CaseUpper,
shortcut: shortcuts.uppercaseSelection,
},
{
label: t("editor.contextMenu.lowercaseSelection"),
action: () => convertSelectedSqlCase("lower"),
disabled: !canCopySelectedSql.value,
icon: CaseLower,
shortcut: shortcuts.lowercaseSelection,
},
{ label: t("editor.contextMenu.selectAll"), action: selectAllSqlFromContextMenu, icon: TextSelect, shortcut: shortcuts.selectAll },
];
});
function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view"))["keymap"]) {
const shortcuts = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);
@@ -718,6 +727,8 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view")
...binding(shortcuts.undo, (view) => codeMirrorUndo?.(view) ?? false),
...binding(shortcuts.redo, (view) => codeMirrorRedo?.(view) ?? false),
...binding(shortcuts.selectAll, (view) => codeMirrorSelectAll?.(view) ?? false),
...binding(shortcuts.uppercaseSelection, () => convertSelectedSqlCase("upper")),
...binding(shortcuts.lowercaseSelection, () => convertSelectedSqlCase("lower")),
]),
) ?? [],
codeMirrorKeymap.of(