feat(editor): retain multiple query result runs

This commit is contained in:
zhangyc authored and GitHub committed 2026-07-27 15:06:28 +08:00
1 parent fddc0fdf50
commit 7f20b03177
26 files changed
+846 -109

No files matched your search

@@ -134,6 +134,7 @@ const emit = defineEmits<{
cursorChange: [pos: number];
formatError: [message: string];
execute: [source: SqlExecutionOverride];
executeInNewResultTab: [source: SqlExecutionOverride];
save: [];
clickTable: [target: SqlObjectNavigationTarget];
viewTableData: [target: SqlObjectNavigationTarget];
@@ -562,6 +563,15 @@ function performNormalTab(view: EditorViewType): boolean {
interface RequestExecuteOptions {
ignoreSelection?: boolean;
bypassPicker?: boolean;
openInNewResultTab?: boolean;
}
function emitExecutionRequest(source: SqlExecutionOverride, openInNewResultTab = false) {
if (openInNewResultTab) {
emit("executeInNewResultTab", source);
} else {
emit("execute", source);
}
}
function requestExecute(options: RequestExecuteOptions = {}) {
@@ -570,15 +580,19 @@ function requestExecute(options: RequestExecuteOptions = {}) {
return requestExecuteFromView(currentView, currentView.state.selection.main.head, options);
}
function requestExecuteInNewResultTab() {
return requestExecute({ bypassPicker: true, openInNewResultTab: true });
}
function requestExecuteFromView(currentView: EditorViewType, cursorPos: number, options: RequestExecuteOptions = {}) {
const selection = currentView.state.selection.main;
if (!options.ignoreSelection && !selection.empty) {
// Has manual selection → execute directly, skip picker.
emit("execute", sqlExecutionSnapshotFromView(currentView));
emitExecutionRequest(sqlExecutionSnapshotFromView(currentView), options.openInNewResultTab);
return true;
}
if (!supportsExecutionTargetPicker(props.databaseType)) {
emit("execute", sqlExecutionSnapshotFromView(currentView));
emitExecutionRequest(sqlExecutionSnapshotFromView(currentView), options.openInNewResultTab);
return true;
}
// No selection → resolve the execution target, optionally via the picker.
@@ -590,7 +604,7 @@ function requestExecuteFromView(currentView: EditorViewType, cursorPos: number,
if (options.bypassPicker || !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);
emitExecutionRequest(candidate.sql, options.openInNewResultTab);
return true;
}
closePicker();
@@ -963,6 +977,12 @@ function executeFromContextMenu() {
focusEditor();
}
function executeInNewResultTabFromContextMenu() {
if (!canExecuteContextSql.value) return;
requestExecuteInNewResultTab();
focusEditor();
}
async function copySelectedSqlFromContextMenu() {
if (!canCopySelectedSql.value) return;
try {
@@ -1230,6 +1250,13 @@ const contextMenuItems = computed<ContextMenuItem[]>(() => {
icon: Play,
shortcut: shortcuts.executeSql,
},
{
label: t("settings.shortcutExecuteSqlInNewResultTab"),
action: executeInNewResultTabFromContextMenu,
disabled: !canExecuteContextSql.value,
icon: Play,
shortcut: shortcuts.executeSqlInNewResultTab,
},
]),
...queryContextObjectActions(contextObjectTarget.value?.type).map(contextObjectMenuItem),
{ label: "", separator: true },
@@ -1299,6 +1326,7 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view")
// Keep the shortcut on the shared execution-mode path (selection priority + configured cursor/all target),
// but bypass the picker so the keyboard shortcut always executes directly instead of popping a dialog.
const executeBindings = props.hideExecutionControls ? [] : binding(shortcuts.executeSql, () => requestExecute({ bypassPicker: true }));
const executeInNewResultTabBindings = props.hideExecutionControls ? [] : binding(shortcuts.executeSqlInNewResultTab, requestExecuteInNewResultTab);
return [
Prec?.high(
codeMirrorKeymap.of([
@@ -1309,6 +1337,7 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view")
},
...binding(shortcuts.find, openSearch),
...binding(shortcuts.replace, openReplace),
...executeInNewResultTabBindings,
...executeBindings,
...binding(shortcuts.saveSql, () => {
emit("save");
@@ -4358,6 +4387,7 @@ defineExpose({
openReplace,
scrollCursorIntoView,
requestExecute,
requestExecuteInNewResultTab,
pasteClipboardAsSqlInCondition,
previewStatementRange,
refreshCompletionCache,