feat(editor): SQL 编辑器选区大小写转换

Co-authored-by: staff <staff@qimaos-MacBook-Pro.local>
This commit is contained in:
zipg
2026-07-02 10:55:43 +08:00
committed by GitHub
co-authored by staff
parent 3e686d71fe
commit de74629c6f
8 changed files with 55 additions and 2 deletions
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, onActivated, onDeactivated, watch, shallowRef, computed, nextTick } from "vue";
import { FileCode, Play, Copy, Table2, TextSelect } from "@lucide/vue";
import { CaseLower, CaseUpper, FileCode, Play, Copy, Table2, TextSelect } from "@lucide/vue";
import { useI18n } from "vue-i18n";
import type { CompletionContext } from "@codemirror/autocomplete";
import type { EditorView as EditorViewType } from "@codemirror/view";
@@ -187,6 +187,7 @@ interface EditorGestureEvent extends Event {
let editorViewModule: typeof import("@codemirror/view") | null = null;
let codeMirrorPrec: typeof import("@codemirror/state").Prec | null = null;
let codeMirrorEditorSelection: typeof import("@codemirror/state").EditorSelection | null = null;
let fontThemeComp: import("@codemirror/state").Compartment | null = null;
let codeMirrorTheme: import("@codemirror/state").Compartment | null = null;
let wordWrapComp: import("@codemirror/state").Compartment | null = null;
@@ -228,6 +229,8 @@ let editorIsActive = true;
let tableReferenceDropListenerRegistered = false;
let imeCompositionActive = false;
let pendingImeModelEmit = false;
type SelectionCaseMode = "upper" | "lower";
let executableStatementRangeCache: ExecutableStatementRangeCache | null = null;
let editorScrollbarPointerCleanup: (() => void) | null = null;
const EDITOR_SCROLLBAR_POINTER_GUTTER_PX = 18;
@@ -549,6 +552,29 @@ function selectAllSqlFromContextMenu() {
focusEditor();
}
function convertSelectedSqlCaseFromContextMenu(mode: SelectionCaseMode) {
const currentView = view.value;
const EditorSelection = codeMirrorEditorSelection;
if (!currentView || !EditorSelection || !canCopySelectedSql.value) return;
const state = currentView.state;
const transaction = state.changeByRange((range) => {
if (range.empty) return { range };
const selectedText = state.sliceDoc(range.from, range.to);
const convertedText = mode === "upper" ? selectedText.toUpperCase() : selectedText.toLowerCase();
return {
changes: { from: range.from, to: range.to, insert: convertedText },
range: EditorSelection.range(range.from, range.from + convertedText.length),
};
});
if (!transaction.changes.empty) {
currentView.dispatch({ ...transaction, scrollIntoView: true, userEvent: "input" });
}
focusEditor();
}
function openTableFromContextMenu() {
if (!contextTableName.value) return;
emit("viewTableData", contextTableName.value);
@@ -615,6 +641,18 @@ const contextMenuItems = computed<ContextMenuItem[]>(() => [
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 },
]);
@@ -2022,7 +2060,7 @@ onMounted(async () => {
const [
{ EditorView, keymap, rectangularSelection, hoverTooltip, showTooltip, Decoration, tooltips, gutter, GutterMarker, lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, crosshairCursor, ViewPlugin },
{ EditorState, Compartment, Prec, StateEffect, StateField },
{ EditorState, EditorSelection, Compartment, Prec, StateEffect, StateField },
langSql,
{ autocompletion, startCompletion, acceptCompletion, closeBrackets, closeBracketsKeymap, snippetCompletion, completionStatus, completionKeymap, insertCompletionText, nextSnippetField },
{ copyLineDown, copyLineUp, deleteLine, indentLess, indentMore, insertNewlineKeepIndent, moveLineDown, moveLineUp, redo, selectAll, undo, history, defaultKeymap, historyKeymap },
@@ -2031,6 +2069,7 @@ onMounted(async () => {
] = 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")]);
editorViewModule = { EditorView, keymap, rectangularSelection } as typeof import("@codemirror/view");
codeMirrorPrec = Prec;
codeMirrorEditorSelection = EditorSelection;
codeMirrorSnippetCompletion = snippetCompletion;
fontThemeComp = new Compartment();
codeMirrorTheme = new Compartment();
+2
View File
@@ -465,6 +465,8 @@ export default {
executeSelection: "Execute selection",
executeCurrent: "Execute SQL",
copySelection: "Copy selection",
uppercaseSelection: "Convert to uppercase",
lowercaseSelection: "Convert to lowercase",
selectAll: "Select all",
},
search: {
+2
View File
@@ -462,6 +462,8 @@ export default withEnglishFallback({
executeSelection: "Ejecutar seleccion",
executeCurrent: "Ejecutar SQL",
copySelection: "Copiar seleccion",
uppercaseSelection: "Convertir a mayusculas",
lowercaseSelection: "Convertir a minusculas",
selectAll: "Seleccionar todo",
},
search: {
+2
View File
@@ -466,6 +466,8 @@ export default withEnglishFallback({
executeSelection: "Esegui selezione",
executeCurrent: "Esegui SQL",
copySelection: "Copia selezione",
uppercaseSelection: "Converti in maiuscolo",
lowercaseSelection: "Converti in minuscolo",
selectAll: "Seleziona tutto",
},
search: {
+2
View File
@@ -460,6 +460,8 @@ export default withEnglishFallback({
executeSelection: "選択範囲を実行",
executeCurrent: "SQLを実行",
copySelection: "選択範囲をコピー",
uppercaseSelection: "大文字に変換",
lowercaseSelection: "小文字に変換",
selectAll: "すべて選択",
},
search: {
+2
View File
@@ -474,6 +474,8 @@ export default withEnglishFallback({
executeSelection: "Executar seleção",
executeCurrent: "Executar SQL",
copySelection: "Copiar seleção",
uppercaseSelection: "Converter para maiúsculas",
lowercaseSelection: "Converter para minúsculas",
selectAll: "Selecionar tudo",
},
search: {
+2
View File
@@ -468,6 +468,8 @@ export default withEnglishFallback({
executeSelection: "执行选中 SQL",
executeCurrent: "执行 SQL",
copySelection: "复制选中内容",
uppercaseSelection: "转为大写",
lowercaseSelection: "转为小写",
selectAll: "全选",
},
search: {
+2
View File
@@ -462,6 +462,8 @@ export default withEnglishFallback({
executeSelection: "執行選取 SQL",
executeCurrent: "執行 SQL",
copySelection: "複製選取內容",
uppercaseSelection: "轉為大寫",
lowercaseSelection: "轉為小寫",
selectAll: "全選",
},
search: {