feat(editor): add SQL context menu actions
This commit is contained in:
1 parent
5b40487753
commit
b406c0a52c
11 files changed
+64
-4
No files matched your search
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, onActivated, onDeactivated, watch, shallowRef, computed, nextTick } from "vue";
|
||||
import { CaseLower, CaseUpper, ClipboardPaste, Code2, Download, FileCode, Pencil, PencilRuler, Play, Copy, List, Scissors, Search, Sparkles, Table2, TextSelect, Trash2 } from "@lucide/vue";
|
||||
import { AlignLeft, CaseLower, CaseUpper, ClipboardPaste, Code2, Download, FileCode, MessageSquareText, Minimize2, Pencil, PencilRuler, Play, Copy, List, Scissors, Search, Sparkles, Table2, TextSelect, Trash2 } from "@lucide/vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { CompletionContext } from "@codemirror/autocomplete";
|
||||
import { Transaction, StateEffect } from "@codemirror/state";
|
||||
@@ -18,7 +18,7 @@ import { executableStatementRangeAtCursor, executableStatementRangeCacheForDoc,
|
||||
import { currentStatementFrameRangeTo } from "@/lib/sql/currentStatementFrame";
|
||||
import { expandToSqlStatementWindow } from "@/lib/sql/insertValueHints";
|
||||
import { insertValueHintColumnNames } from "@/lib/sql/insertValueHintColumns";
|
||||
import { formatSqlForEditing, compressSqlText, type SqlFormatDialect } from "@/lib/sql/sqlFormatter";
|
||||
import { canFormatSqlForDatabaseType, formatSqlForEditing, compressSqlText, type SqlFormatDialect } from "@/lib/sql/sqlFormatter";
|
||||
import { detectAndFormatStructured } from "@/lib/sql/autoFormat";
|
||||
import { enabledSqlParameterSyntaxes, resolveSqlVariableSyntaxToggles } from "@/lib/sql/sqlVariableSyntax";
|
||||
import { blankLineDeletionChanges, replaceSelectedEditorText } from "@/lib/editor/queryEditorTextEdits";
|
||||
@@ -1371,6 +1371,13 @@ async function pasteClipboardSqlFromContextMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCommentFromContextMenu() {
|
||||
const currentView = view.value;
|
||||
if (!currentView || props.readOnly) return;
|
||||
codeMirrorToggleLineComment?.(currentView);
|
||||
focusEditor();
|
||||
}
|
||||
|
||||
function selectAllSqlFromContextMenu() {
|
||||
const currentView = view.value;
|
||||
if (!currentView) return;
|
||||
@@ -1657,6 +1664,26 @@ const contextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
shortcut: shortcuts.expandSelectStar,
|
||||
},
|
||||
{ label: "", separator: true },
|
||||
{
|
||||
label: t("editor.contextMenu.commentSelection"),
|
||||
action: toggleCommentFromContextMenu,
|
||||
disabled: props.readOnly || !canCopySelectedSql.value,
|
||||
icon: MessageSquareText,
|
||||
shortcut: shortcuts.toggleLineComment,
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.formatSelectionSql"),
|
||||
action: () => void formatCurrentSql(),
|
||||
disabled: props.readOnly || !canCopySelectedSql.value || !canFormatSqlForDatabaseType(props.databaseType),
|
||||
icon: AlignLeft,
|
||||
shortcut: shortcuts.formatSql,
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.compressSelectionSql"),
|
||||
action: compressCurrentSql,
|
||||
disabled: props.readOnly || !canCopySelectedSql.value,
|
||||
icon: Minimize2,
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.copySelection"),
|
||||
action: copySelectedSqlFromContextMenu,
|
||||
@@ -2888,7 +2915,7 @@ function scheduleSemanticDiagnostics(delay = 500, options: { preserveOutsideRang
|
||||
|
||||
async function formatCurrentSql() {
|
||||
if (props.readOnly) return;
|
||||
if (props.databaseType === "victoriametrics") return;
|
||||
if (!canFormatSqlForDatabaseType(props.databaseType)) return;
|
||||
const currentView = view.value;
|
||||
if (!currentView) return;
|
||||
|
||||
|
||||
@@ -1264,6 +1264,9 @@ export default {
|
||||
export: "Export",
|
||||
exportQueryResultTo: "Export current query result to {format}",
|
||||
copySelection: "Copy selection",
|
||||
commentSelection: "Comment selection",
|
||||
formatSelectionSql: "Format selected SQL",
|
||||
compressSelectionSql: "Compress selected SQL",
|
||||
cutSelection: "Cut selection",
|
||||
pasteFromClipboard: "Paste",
|
||||
pasteClipboardReadFailed: "Failed to read clipboard: {message}",
|
||||
|
||||
@@ -1241,6 +1241,9 @@ export default withEnglishFallback({
|
||||
export: "Exportar",
|
||||
exportQueryResultTo: "Exportar el conjunto de resultados de la consulta actual a {format}",
|
||||
copySelection: "Copiar seleccion",
|
||||
commentSelection: "Comentar selección",
|
||||
formatSelectionSql: "Formatear SQL seleccionado",
|
||||
compressSelectionSql: "Comprimir SQL seleccionado",
|
||||
sendToAi: "Enviar a IA",
|
||||
uppercaseSelection: "Convertir a mayusculas",
|
||||
lowercaseSelection: "Convertir a minusculas",
|
||||
|
||||
@@ -1239,6 +1239,9 @@ export default withEnglishFallback({
|
||||
export: "Esporta",
|
||||
exportQueryResultTo: "Esporta il set di risultati della query corrente in {format}",
|
||||
copySelection: "Copia selezione",
|
||||
commentSelection: "Commenta selezione",
|
||||
formatSelectionSql: "Formatta SQL selezionato",
|
||||
compressSelectionSql: "Comprimi SQL selezionato",
|
||||
sendToAi: "Invia ad AI",
|
||||
uppercaseSelection: "Converti in maiuscolo",
|
||||
lowercaseSelection: "Converti in minuscolo",
|
||||
|
||||
@@ -1259,6 +1259,9 @@ export default withEnglishFallback({
|
||||
export: "エクスポート",
|
||||
exportQueryResultTo: "現在のクエリ結果セットを {format} にエクスポート",
|
||||
copySelection: "選択範囲をコピー",
|
||||
commentSelection: "選択範囲をコメント化",
|
||||
formatSelectionSql: "選択した SQL をフォーマット",
|
||||
compressSelectionSql: "選択した SQL を圧縮",
|
||||
sendToAi: "AIに送信",
|
||||
uppercaseSelection: "大文字に変換",
|
||||
lowercaseSelection: "小文字に変換",
|
||||
|
||||
@@ -1155,6 +1155,9 @@ export default withEnglishFallback({
|
||||
export: "내보내기",
|
||||
exportQueryResultTo: "현재 쿼리 결과를 {format}(으)로 내보내기",
|
||||
copySelection: "선택 영역 복사",
|
||||
commentSelection: "선택 항목 주석 처리",
|
||||
formatSelectionSql: "선택한 SQL 서식 지정",
|
||||
compressSelectionSql: "선택한 SQL 압축",
|
||||
cutSelection: "선택 영역 잘라내기",
|
||||
pasteFromClipboard: "붙여넣기",
|
||||
pasteClipboardReadFailed: "클립보드 읽기 실패: {message}",
|
||||
|
||||
@@ -1240,6 +1240,9 @@ export default withEnglishFallback({
|
||||
export: "Exportar",
|
||||
exportQueryResultTo: "Exportar conjunto de resultados da consulta atual para {format}",
|
||||
copySelection: "Copiar seleção",
|
||||
commentSelection: "Comentar seleção",
|
||||
formatSelectionSql: "Formatar SQL selecionado",
|
||||
compressSelectionSql: "Compactar SQL selecionado",
|
||||
sendToAi: "Enviar para IA",
|
||||
uppercaseSelection: "Converter para maiúsculas",
|
||||
lowercaseSelection: "Converter para minúsculas",
|
||||
|
||||
@@ -1188,6 +1188,9 @@ export default withEnglishFallback({
|
||||
export: "导出",
|
||||
exportQueryResultTo: "导出当前查询结果集到 {format}",
|
||||
copySelection: "复制选中内容",
|
||||
commentSelection: "注释选中内容",
|
||||
formatSelectionSql: "格式化选中SQL",
|
||||
compressSelectionSql: "压缩选中SQL",
|
||||
cutSelection: "剪切选中内容",
|
||||
pasteFromClipboard: "粘贴",
|
||||
pasteClipboardReadFailed: "读取剪贴板失败:{message}",
|
||||
|
||||
@@ -1239,6 +1239,9 @@ export default withEnglishFallback({
|
||||
export: "匯出",
|
||||
exportQueryResultTo: "匯出目前查詢結果集到 {format}",
|
||||
copySelection: "複製選取內容",
|
||||
commentSelection: "註解選取內容",
|
||||
formatSelectionSql: "格式化選取的 SQL",
|
||||
compressSelectionSql: "壓縮選取的 SQL",
|
||||
sendToAi: "傳送至 AI",
|
||||
uppercaseSelection: "轉為大寫",
|
||||
lowercaseSelection: "轉為小寫",
|
||||
|
||||
+6
-1
@@ -1,7 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { formatSqlForDisplay, formatSqlForEditing, formatSqlText, MAX_SQL_FORMAT_CHARS, sqlFormatDialectForDbType, UnsupportedStructuredInputError } from "@/lib/sql/sqlFormatter";
|
||||
import { canFormatSqlForDatabaseType, formatSqlForDisplay, formatSqlForEditing, formatSqlText, MAX_SQL_FORMAT_CHARS, sqlFormatDialectForDbType, UnsupportedStructuredInputError } from "@/lib/sql/sqlFormatter";
|
||||
|
||||
describe("sqlFormatter", () => {
|
||||
it("disables SQL formatting for VictoriaMetrics queries", () => {
|
||||
expect(canFormatSqlForDatabaseType("victoriametrics")).toBe(false);
|
||||
expect(canFormatSqlForDatabaseType("mysql")).toBe(true);
|
||||
});
|
||||
|
||||
it("maps PostgreSQL-compatible database types to the postgres formatter dialect", () => {
|
||||
for (const dbType of ["postgres", "kwdb", "gaussdb", "opengauss", "questdb", "kingbase", "highgo", "vastbase", "redshift"]) {
|
||||
expect(sqlFormatDialectForDbType(dbType)).toBe("postgres");
|
||||
|
||||
@@ -5,6 +5,10 @@ export type SqlFormatDialect = "mysql" | "postgres" | "sqlite" | "sqlserver" | "
|
||||
|
||||
export const MAX_SQL_FORMAT_CHARS = 1_000_000;
|
||||
|
||||
export function canFormatSqlForDatabaseType(dbType: string | null | undefined): boolean {
|
||||
return dbType !== "victoriametrics";
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown by {@link formatSqlText} when the input is XML-looking and must never
|
||||
* be run through the SQL formatter. sql-formatter silently rewrites well-formed
|
||||
|
||||
Reference in new issue
Block a user