feat(editor): execute single SQL directly without picker
* fix(ui): toast 消息支持换行显示 - 添加 max-w-3xl 限制最大宽度 - 使用 whitespace-pre-wrap 允许自动换行 - 添加 break-words 防止长单词溢出 * feat(editor): 单条 SQL 直接执行 --------- Co-authored-by: t8y2 <1156263951@qq.com> Co-authored-by: staff <staff@qimaos-MacBook-Pro.local>
This commit is contained in:
parent
628fadf517
commit
dac97b83a6
|
|
@ -10,7 +10,7 @@ import SqlExecutionTargetPicker from "./SqlExecutionTargetPicker.vue";
|
|||
import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
|
||||
import { copyToClipboard } from "@/lib/clipboard";
|
||||
import { resolveExecutableSql, type SqlExecutionSnapshot, type SqlExecutionOverride, type SqlExecutionCandidate } from "@/lib/sqlExecutionTarget";
|
||||
import { buildExecutionCandidates, supportsExecutionTargetPicker } from "@/lib/sqlStatementRanges";
|
||||
import { buildExecutionCandidates, hasMultipleExecutionTargets, supportsExecutionTargetPicker } from "@/lib/sqlStatementRanges";
|
||||
import { formatSqlText, type SqlFormatDialect } from "@/lib/sqlFormatter";
|
||||
import { formatMongoShellText } from "@/lib/mongoFormatter";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
|
|
@ -319,7 +319,7 @@ function requestExecute() {
|
|||
const cursorPos = selection.head;
|
||||
const candidates = buildExecutionCandidates(doc, cursorPos, props.databaseType);
|
||||
if (candidates.length === 0) return false;
|
||||
if (!settingsStore.editorSettings.showExecutionTargetPicker) {
|
||||
if (!settingsStore.editorSettings.showExecutionTargetPicker || !hasMultipleExecutionTargets(doc, props.databaseType)) {
|
||||
const preferredKind = settingsStore.editorSettings.executeMode === "current" ? "cursor" : "all";
|
||||
const candidate = candidates.find((item) => item.kind === preferredKind) ?? candidates[0];
|
||||
emit("execute", candidate.sql);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { buildExecutionCandidates, fullSqlRange, splitSqlStatementRanges, statementRangeAtCursor, supportsExecutionTargetPicker } from "../sqlStatementRanges";
|
||||
import { buildExecutionCandidates, fullSqlRange, hasMultipleExecutionTargets, splitSqlStatementRanges, statementRangeAtCursor, supportsExecutionTargetPicker } from "../sqlStatementRanges";
|
||||
|
||||
function indexOf(sql: string, needle: string, occurrence = 1): number {
|
||||
let from = 0;
|
||||
|
|
@ -285,6 +285,25 @@ describe("buildExecutionCandidates", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("hasMultipleExecutionTargets", () => {
|
||||
it("returns false for a single SQL statement", () => {
|
||||
expect(hasMultipleExecutionTargets("SELECT 1;")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for multiple SQL statements", () => {
|
||||
expect(hasMultipleExecutionTargets("SELECT 1;\nSELECT 2;")).toBe(true);
|
||||
});
|
||||
|
||||
it("ignores comments when counting SQL statements", () => {
|
||||
expect(hasMultipleExecutionTargets("-- check one thing\nSELECT 1;")).toBe(false);
|
||||
});
|
||||
|
||||
it("counts executable Redis command lines", () => {
|
||||
expect(hasMultipleExecutionTargets("GET user:1", "redis")).toBe(false);
|
||||
expect(hasMultipleExecutionTargets("GET user:1\n# comment\nDEL user:2", "redis")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("supportsExecutionTargetPicker", () => {
|
||||
it("enables the picker for SQL database connections and Redis", () => {
|
||||
expect(supportsExecutionTargetPicker("mysql")).toBe(true);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ export function supportsExecutionTargetPicker(databaseType?: DatabaseType): bool
|
|||
return !!databaseType && (databaseType === "redis" || !NON_SQL_EXECUTION_TARGET_TYPES.has(databaseType));
|
||||
}
|
||||
|
||||
export function hasMultipleExecutionTargets(sql: string, databaseType?: DatabaseType): boolean {
|
||||
if (databaseType === "redis") {
|
||||
return redisExecutableCommandCount(sql) > 1;
|
||||
}
|
||||
return splitSqlStatementRanges(sql).length > 1;
|
||||
}
|
||||
|
||||
interface RawStatement {
|
||||
/** Start offset (inclusive) of whitespace that can still target this statement. */
|
||||
hitFrom: number;
|
||||
|
|
@ -825,6 +832,17 @@ function candidateFromRange(range: SqlTextRange, kind: SqlExecutionCandidate["ki
|
|||
};
|
||||
}
|
||||
|
||||
function redisExecutableCommandCount(sql: string): number {
|
||||
let count = 0;
|
||||
for (const line of sql.split(/\r?\n/)) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#")) continue;
|
||||
count += 1;
|
||||
if (count > 1) return count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function redisCommandRangeAtCursor(sql: string, cursorPos: number): SqlTextRange | null {
|
||||
const pos = clampCursor(sql, cursorPos);
|
||||
if (isCursorOnBlankLine(sql, pos)) return null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue