feat(editor): add SQL block comment action
This commit is contained in:
1 parent
34a4f5d78a
commit
e18222628d
14 files changed
+127
-3
No files matched your search
@@ -120,7 +120,7 @@ import { LARGE_PASTE_HISTORY_USER_EVENT, normalizeQueryEditorPasteText, recovera
|
||||
import { computePasteCaretResyncTarget } from "@/lib/editor/queryEditorPasteCaretResync";
|
||||
import { extendQueryEditorSelection, runQueryEditorAltExtendSelection } from "@/lib/editor/queryEditorExtendSelection";
|
||||
import type { StatementExecutionMarker } from "@/lib/tabs/tabPresentation";
|
||||
import { isSchemaAware, isSingleDatabase, supportsDatabaseNameCompletion, supportsDatabaseSchemaQualifier, supportsSqlInListPaste } from "@/lib/database/databaseFeatureSupport";
|
||||
import { isSchemaAware, isSingleDatabase, supportsDatabaseNameCompletion, supportsDatabaseSchemaQualifier, supportsQueryEditorBlockComments, supportsSqlInListPaste } from "@/lib/database/databaseFeatureSupport";
|
||||
import { metadataSchemaForConnection, sqlSnippetDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { usesLocalOnlyEditorCompletionMetadata, usesOnDemandOnlyEditorColumnMetadata } from "@/lib/metadata/completionMetadataPolicy";
|
||||
import { loadTableMetadata } from "@/lib/metadata/tableMetadataCache";
|
||||
@@ -548,6 +548,7 @@ let codeMirrorRedo: typeof import("@codemirror/commands").redo | null = null;
|
||||
let codeMirrorSelectAll: typeof import("@codemirror/commands").selectAll | null = null;
|
||||
let codeMirrorInsertNewlineKeepIndent: typeof import("@codemirror/commands").insertNewlineKeepIndent | null = null;
|
||||
let codeMirrorToggleLineComment: typeof import("@codemirror/commands").toggleLineComment | null = null;
|
||||
let codeMirrorToggleBlockComment: typeof import("@codemirror/commands").toggleBlockComment | null = null;
|
||||
let codeMirrorToggleFold: typeof import("@codemirror/language").toggleFold | null = null;
|
||||
let pendingCompletionTabTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let setSqlDiagnosticsEffect: import("@codemirror/state").StateEffectType<SqlSemanticDiagnostic[]> | null = null;
|
||||
@@ -1401,6 +1402,13 @@ function toggleCommentFromContextMenu() {
|
||||
focusEditor();
|
||||
}
|
||||
|
||||
function toggleBlockCommentFromContextMenu() {
|
||||
const currentView = view.value;
|
||||
if (!currentView || props.readOnly || !supportsQueryEditorBlockComments(props.databaseType)) return;
|
||||
codeMirrorToggleBlockComment?.(currentView);
|
||||
focusEditor();
|
||||
}
|
||||
|
||||
function selectAllSqlFromContextMenu() {
|
||||
const currentView = view.value;
|
||||
if (!currentView) return;
|
||||
@@ -1709,6 +1717,13 @@ const contextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
icon: MessageSquareText,
|
||||
shortcut: shortcuts.toggleLineComment,
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.blockCommentSelection"),
|
||||
action: toggleBlockCommentFromContextMenu,
|
||||
disabled: props.readOnly || !canCopySelectedSql.value || !supportsQueryEditorBlockComments(props.databaseType),
|
||||
icon: MessageSquareText,
|
||||
shortcut: shortcuts.toggleBlockComment,
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.formatSelectionSql"),
|
||||
action: () => void formatCurrentSql(),
|
||||
@@ -1880,6 +1895,10 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view")
|
||||
...binding(shortcuts.uppercaseSelection, () => convertSelectedSqlCase("upper")),
|
||||
...binding(shortcuts.lowercaseSelection, () => convertSelectedSqlCase("lower")),
|
||||
...binding(shortcuts.toggleLineComment, (view) => codeMirrorToggleLineComment?.(view) ?? false),
|
||||
...binding(shortcuts.toggleBlockComment, (view) => {
|
||||
if (!supportsQueryEditorBlockComments(props.databaseType)) return false;
|
||||
return codeMirrorToggleBlockComment?.(view) ?? false;
|
||||
}),
|
||||
...binding(shortcuts.toggleFold, (view) => codeMirrorToggleFold?.(view) ?? false),
|
||||
...binding(shortcuts.exPasteSqlInCondition, () => {
|
||||
if (!supportsSqlInListPaste(props.databaseType)) return false;
|
||||
@@ -4412,7 +4431,7 @@ onMounted(async () => {
|
||||
{ EditorState, EditorSelection, Compartment, Prec, RangeSet, StateEffect, StateField },
|
||||
langSql,
|
||||
{ autocompletion, startCompletion, acceptCompletion, closeBrackets, closeBracketsKeymap, snippetCompletion, completionStatus, completionKeymap, insertCompletionText, nextSnippetField, closeCompletion },
|
||||
{ copyLineDown, copyLineUp, deleteLine, indentLess, indentMore, insertNewlineKeepIndent, moveLineDown, moveLineUp, redo, selectAll, undo, toggleLineComment, history, defaultKeymap, historyKeymap },
|
||||
{ copyLineDown, copyLineUp, deleteLine, indentLess, indentMore, insertNewlineKeepIndent, moveLineDown, moveLineUp, redo, selectAll, undo, toggleLineComment, toggleBlockComment, history, defaultKeymap, historyKeymap },
|
||||
{ bracketMatching, foldGutter, indentOnInput, indentUnit, syntaxHighlighting, defaultHighlightStyle, foldKeymap, toggleFold, ensureSyntaxTree },
|
||||
{ searchKeymap },
|
||||
] = await Promise.all([import("@codemirror/view"), import("@codemirror/state"), import("@codemirror/lang-sql"), import("@codemirror/autocomplete"), import("@codemirror/commands"), import("@codemirror/language"), import("@codemirror/search")]);
|
||||
@@ -4461,6 +4480,7 @@ onMounted(async () => {
|
||||
codeMirrorSelectAll = selectAll;
|
||||
codeMirrorInsertNewlineKeepIndent = insertNewlineKeepIndent;
|
||||
codeMirrorToggleLineComment = toggleLineComment;
|
||||
codeMirrorToggleBlockComment = toggleBlockComment;
|
||||
codeMirrorToggleFold = toggleFold;
|
||||
codeMirrorIndentUnit = indentUnit;
|
||||
window.addEventListener("keyup", clearTableNavigationHoverOnModifierRelease);
|
||||
@@ -4856,7 +4876,13 @@ onMounted(async () => {
|
||||
activeLineHighlighter,
|
||||
// Vim must be mounted before DBX/default keymaps so normal-mode keys are handled first.
|
||||
vimModeComp.of(vimModeExtension(initialSettings.vimModeEnabled)),
|
||||
keymap.of([...defaultKeymap, ...searchKeymapWithoutModD(searchKeymap), ...historyKeymap, ...foldKeymap, ...completionKeymap]),
|
||||
keymap.of([
|
||||
...defaultKeymap.filter((item) => item.run !== toggleBlockComment),
|
||||
...searchKeymapWithoutModD(searchKeymap),
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
]),
|
||||
sqlLanguageComp.of(buildSqlLanguageExtension()),
|
||||
sqlSemanticHighlightComp.of(buildSqlSemanticHighlightExtension()),
|
||||
tooltips({ parent: tooltipParent }),
|
||||
|
||||
@@ -1277,6 +1277,7 @@ export default {
|
||||
exportQueryResultTo: "Export current query result to {format}",
|
||||
copySelection: "Copy selection",
|
||||
commentSelection: "Comment selection",
|
||||
blockCommentSelection: "Block comment selection",
|
||||
formatSelectionSql: "Format selected SQL",
|
||||
compressSelectionSql: "Compress selected SQL",
|
||||
cutSelection: "Cut selection",
|
||||
@@ -6047,6 +6048,7 @@ export default {
|
||||
shortcutFormatSql: "Format SQL",
|
||||
shortcutExpandSelectStar: "Expand * to columns",
|
||||
shortcutToggleLineComment: "Toggle line comment",
|
||||
shortcutToggleBlockComment: "Toggle block comment",
|
||||
shortcutIndentMore: "Indent more",
|
||||
shortcutIndentLess: "Indent less",
|
||||
shortcutInsertLineBelow: "Insert line below",
|
||||
|
||||
@@ -1255,6 +1255,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "Exportar el conjunto de resultados de la consulta actual a {format}",
|
||||
copySelection: "Copiar seleccion",
|
||||
commentSelection: "Comentar selección",
|
||||
blockCommentSelection: "Comentario de bloque en la selección",
|
||||
formatSelectionSql: "Formatear SQL seleccionado",
|
||||
compressSelectionSql: "Comprimir SQL seleccionado",
|
||||
sendToAi: "Enviar a IA",
|
||||
@@ -5748,6 +5749,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "Formatear SQL",
|
||||
shortcutExpandSelectStar: "Expandir * a columnas",
|
||||
shortcutToggleLineComment: "Alternar comentario de línea",
|
||||
shortcutToggleBlockComment: "Alternar comentario de bloque",
|
||||
shortcutIndentMore: "Aumentar sangría",
|
||||
shortcutIndentLess: "Reducir sangría",
|
||||
shortcutInsertLineBelow: "Insertar una línea debajo",
|
||||
|
||||
@@ -1253,6 +1253,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "Esporta il set di risultati della query corrente in {format}",
|
||||
copySelection: "Copia selezione",
|
||||
commentSelection: "Commenta selezione",
|
||||
blockCommentSelection: "Commenta selezione a blocco",
|
||||
formatSelectionSql: "Formatta SQL selezionato",
|
||||
compressSelectionSql: "Comprimi SQL selezionato",
|
||||
sendToAi: "Invia ad AI",
|
||||
@@ -5748,6 +5749,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "Formatta SQL",
|
||||
shortcutExpandSelectStar: "Espandi * in colonne",
|
||||
shortcutToggleLineComment: "Attiva/disattiva commento di riga",
|
||||
shortcutToggleBlockComment: "Attiva/disattiva commento a blocco",
|
||||
shortcutIndentMore: "Aumenta rientro",
|
||||
shortcutIndentLess: "Riduci rientro",
|
||||
shortcutInsertLineBelow: "Inserisci una riga sotto",
|
||||
|
||||
@@ -1273,6 +1273,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "現在のクエリ結果セットを {format} にエクスポート",
|
||||
copySelection: "選択範囲をコピー",
|
||||
commentSelection: "選択範囲をコメント化",
|
||||
blockCommentSelection: "選択範囲をブロックコメント化",
|
||||
formatSelectionSql: "選択した SQL をフォーマット",
|
||||
compressSelectionSql: "選択した SQL を圧縮",
|
||||
sendToAi: "AIに送信",
|
||||
@@ -5763,6 +5764,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "SQLをフォーマット",
|
||||
shortcutExpandSelectStar: "* を列に展開",
|
||||
shortcutToggleLineComment: "行コメントを切り替え",
|
||||
shortcutToggleBlockComment: "ブロックコメントを切り替え",
|
||||
shortcutIndentMore: "インデントを増やす",
|
||||
shortcutIndentLess: "インデントを減らす",
|
||||
shortcutInsertLineBelow: "下に行を挿入",
|
||||
|
||||
@@ -1164,6 +1164,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "현재 쿼리 결과를 {format}(으)로 내보내기",
|
||||
copySelection: "선택 영역 복사",
|
||||
commentSelection: "선택 항목 주석 처리",
|
||||
blockCommentSelection: "선택 항목 블록 주석 처리",
|
||||
formatSelectionSql: "선택한 SQL 서식 지정",
|
||||
compressSelectionSql: "선택한 SQL 압축",
|
||||
cutSelection: "선택 영역 잘라내기",
|
||||
@@ -5506,6 +5507,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "SQL 서식",
|
||||
shortcutExpandSelectStar: "*을(를) 열로 확장",
|
||||
shortcutToggleLineComment: "줄 주석 전환",
|
||||
shortcutToggleBlockComment: "블록 주석 전환",
|
||||
shortcutIndentMore: "들여쓰기 늘리기",
|
||||
shortcutIndentLess: "들여쓰기 줄이기",
|
||||
shortcutInsertLineBelow: "아래에 줄 삽입",
|
||||
|
||||
@@ -1254,6 +1254,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "Exportar conjunto de resultados da consulta atual para {format}",
|
||||
copySelection: "Copiar seleção",
|
||||
commentSelection: "Comentar seleção",
|
||||
blockCommentSelection: "Comentário de bloco na seleção",
|
||||
formatSelectionSql: "Formatar SQL selecionado",
|
||||
compressSelectionSql: "Compactar SQL selecionado",
|
||||
sendToAi: "Enviar para IA",
|
||||
@@ -5750,6 +5751,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "Formatar SQL",
|
||||
shortcutExpandSelectStar: "Expandir * em colunas",
|
||||
shortcutToggleLineComment: "Alternar comentário de linha",
|
||||
shortcutToggleBlockComment: "Alternar comentário de bloco",
|
||||
shortcutIndentMore: "Aumentar recuo",
|
||||
shortcutIndentLess: "Reduzir recuo",
|
||||
shortcutInsertLineBelow: "Inserir uma linha abaixo",
|
||||
|
||||
@@ -1201,6 +1201,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "导出当前查询结果集到 {format}",
|
||||
copySelection: "复制选中内容",
|
||||
commentSelection: "注释选中内容",
|
||||
blockCommentSelection: "块注释选中内容",
|
||||
formatSelectionSql: "格式化选中SQL",
|
||||
compressSelectionSql: "压缩选中SQL",
|
||||
cutSelection: "剪切选中内容",
|
||||
@@ -6033,6 +6034,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "格式化 SQL",
|
||||
shortcutExpandSelectStar: "将 * 展开为字段",
|
||||
shortcutToggleLineComment: "切换行注释",
|
||||
shortcutToggleBlockComment: "切换块注释",
|
||||
shortcutIndentMore: "增加缩进",
|
||||
shortcutIndentLess: "减少缩进",
|
||||
shortcutInsertLineBelow: "在下方新增一行",
|
||||
|
||||
@@ -1253,6 +1253,7 @@ export default withEnglishFallback({
|
||||
exportQueryResultTo: "匯出目前查詢結果集到 {format}",
|
||||
copySelection: "複製選取內容",
|
||||
commentSelection: "註解選取內容",
|
||||
blockCommentSelection: "區塊註解選取內容",
|
||||
formatSelectionSql: "格式化選取的 SQL",
|
||||
compressSelectionSql: "壓縮選取的 SQL",
|
||||
sendToAi: "傳送至 AI",
|
||||
@@ -5074,6 +5075,7 @@ export default withEnglishFallback({
|
||||
shortcutFormatSql: "格式化 SQL",
|
||||
shortcutExpandSelectStar: "將 * 展開為欄位",
|
||||
shortcutToggleLineComment: "切換行註解",
|
||||
shortcutToggleBlockComment: "切換區塊註解",
|
||||
shortcutIndentMore: "增加縮排",
|
||||
shortcutIndentLess: "減少縮排",
|
||||
shortcutInsertLineBelow: "在下方新增一行",
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
supportsDatabaseNameCompletion,
|
||||
supportsDatabaseSchemaQualifier,
|
||||
supportsQueryTargetDatabaseListing,
|
||||
supportsQueryEditorBlockComments,
|
||||
supportsSqlInListPaste,
|
||||
supportsTableImport,
|
||||
supportsTransaction,
|
||||
@@ -123,6 +124,21 @@ describe("supportsSqlInListPaste", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("supportsQueryEditorBlockComments", () => {
|
||||
it("allows block comments in generic and SQL editors", () => {
|
||||
expect(supportsQueryEditorBlockComments(undefined)).toBe(true);
|
||||
expect(supportsQueryEditorBlockComments("mysql")).toBe(true);
|
||||
expect(supportsQueryEditorBlockComments("postgres")).toBe(true);
|
||||
expect(supportsQueryEditorBlockComments("sqlserver")).toBe(true);
|
||||
});
|
||||
|
||||
it("hides block comments in non-SQL editors", () => {
|
||||
expect(supportsQueryEditorBlockComments("redis")).toBe(false);
|
||||
expect(supportsQueryEditorBlockComments("mongodb")).toBe(false);
|
||||
expect(supportsQueryEditorBlockComments("elasticsearch")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("supportsTableImport", () => {
|
||||
it("enables OceanBase Oracle table import", () => {
|
||||
expect(supportsTableImport("oceanbase-oracle")).toBe(true);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { toggleBlockComment } from "@codemirror/commands";
|
||||
import { sql } from "@codemirror/lang-sql";
|
||||
import { EditorSelection, EditorState, type Transaction } from "@codemirror/state";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const queryEditorSource = readFileSync(new URL("../../../components/editor/QueryEditor.vue", import.meta.url), "utf8");
|
||||
|
||||
function runToggleBlockComment(doc: string, selection: EditorSelection | { anchor: number; head?: number }) {
|
||||
let state = EditorState.create({ doc, selection, extensions: [sql()] });
|
||||
const dispatch = vi.fn((transaction: Transaction) => {
|
||||
state = transaction.state;
|
||||
});
|
||||
const handled = toggleBlockComment({
|
||||
get state() {
|
||||
return state;
|
||||
},
|
||||
dispatch,
|
||||
} as never);
|
||||
|
||||
return { dispatch, handled, state };
|
||||
}
|
||||
|
||||
describe("QueryEditor block comment", () => {
|
||||
it("wires toggleBlockComment into the right-click context menu, the keymap, and the editor module load", () => {
|
||||
expect(queryEditorSource).toContain('let codeMirrorToggleBlockComment: typeof import("@codemirror/commands").toggleBlockComment | null = null;');
|
||||
expect(queryEditorSource).toContain("codeMirrorToggleBlockComment?.(currentView);");
|
||||
expect(queryEditorSource).toContain('label: t("editor.contextMenu.blockCommentSelection")');
|
||||
expect(queryEditorSource).toContain("shortcut: shortcuts.toggleBlockComment");
|
||||
expect(queryEditorSource).toContain("...binding(shortcuts.toggleBlockComment, (view) => {");
|
||||
expect(queryEditorSource).toContain("!supportsQueryEditorBlockComments(props.databaseType)");
|
||||
expect(queryEditorSource).toContain("defaultKeymap.filter((item) => item.run !== toggleBlockComment)");
|
||||
expect(queryEditorSource).toContain("codeMirrorToggleBlockComment = toggleBlockComment;");
|
||||
});
|
||||
|
||||
it("wraps the selected SQL in a block comment", () => {
|
||||
const doc = "SELECT id FROM users;";
|
||||
const selection = EditorSelection.range(doc.indexOf("id"), doc.indexOf("id") + "id".length);
|
||||
const result = runToggleBlockComment(doc, selection);
|
||||
|
||||
expect(result.handled).toBe(true);
|
||||
expect(result.state.doc.toString()).toBe("SELECT /* id */ FROM users;");
|
||||
});
|
||||
|
||||
it("removes an existing block comment around the selection", () => {
|
||||
const doc = "SELECT /* id */ FROM users;";
|
||||
const selection = EditorSelection.range(doc.indexOf("/*"), doc.indexOf("*/") + "*/".length);
|
||||
const result = runToggleBlockComment(doc, selection);
|
||||
|
||||
expect(result.state.doc.toString()).toBe("SELECT id FROM users;");
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,7 @@ describe("shortcutRegistry editor actions", () => {
|
||||
const formatterEditorActionIds: ShortcutActionId[] = [
|
||||
"formatSql",
|
||||
"toggleLineComment",
|
||||
"toggleBlockComment",
|
||||
"indentMore",
|
||||
"indentLess",
|
||||
"joinLines",
|
||||
@@ -126,6 +127,7 @@ describe("shortcutRegistry editor actions", () => {
|
||||
expect(shortcuts.executeSql).toBe("Mod+Shift+Enter");
|
||||
expect(shortcuts.formatSql).toBe("Shift+Mod+F");
|
||||
expect(shortcuts.toggleLineComment).toBe("Mod+/");
|
||||
expect(shortcuts.toggleBlockComment).toBe("Shift+Alt+A");
|
||||
expect(shortcuts.indentMore).toBe("");
|
||||
expect(shortcuts.indentLess).toBe("Shift+Tab");
|
||||
expect(shortcuts.joinLines).toBe("Mod+J");
|
||||
|
||||
@@ -131,6 +131,11 @@ export function supportsSqlInListPaste(dbType?: DatabaseType): boolean {
|
||||
return supportsSqlFileExecution(dbType) && !NON_SQL_IN_LIST_PASTE_TYPES.has(dbType);
|
||||
}
|
||||
|
||||
export function supportsQueryEditorBlockComments(dbType?: DatabaseType): boolean {
|
||||
if (!dbType) return true;
|
||||
return supportsSqlFileExecution(dbType);
|
||||
}
|
||||
|
||||
export function supportsSchemaDiagram(dbType?: DatabaseType): boolean {
|
||||
return supportsDatabaseFeature(dbType, "diagram");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export type ShortcutActionId =
|
||||
| "formatSql"
|
||||
| "expandSelectStar"
|
||||
| "toggleLineComment"
|
||||
| "toggleBlockComment"
|
||||
| "saveSql"
|
||||
| "acceptCompletion"
|
||||
| "indentMore"
|
||||
@@ -119,6 +120,12 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [
|
||||
scope: "editor",
|
||||
defaultShortcut: "Mod+/",
|
||||
},
|
||||
{
|
||||
id: "toggleBlockComment",
|
||||
labelKey: "settings.shortcutToggleBlockComment",
|
||||
scope: "editor",
|
||||
defaultShortcut: "Shift+Alt+A",
|
||||
},
|
||||
{
|
||||
id: "saveSql",
|
||||
labelKey: "settings.shortcutSaveSql",
|
||||
|
||||
Reference in new issue
Block a user