fix(editor): recover truncated large SQL pastes

This commit is contained in:
zipg authored and GitHub committed 2026-07-22 12:23:04 +08:00
1 parent d16ba82d5a
commit 010e588d64
3 files changed
+129

No files matched your search

@@ -3,6 +3,7 @@ import { ref, onMounted, onBeforeUnmount, onActivated, onDeactivated, watch, sha
import { CaseLower, CaseUpper, Code2, FileCode, Pencil, PencilRuler, Play, Copy, List, Search, Sparkles, Table2, TextSelect, Trash2 } from "@lucide/vue";
import { useI18n } from "vue-i18n";
import type { CompletionContext } from "@codemirror/autocomplete";
import { Transaction } from "@codemirror/state";
import type { EditorView as EditorViewType } from "@codemirror/view";
import { search as cmSearch } from "@codemirror/search";
import EditorSearchPanel from "./EditorSearchPanel.vue";
@@ -68,12 +69,14 @@ import { focusEditorView } from "@/lib/editor/queryEditorFocus";
import { createDbxCodeMirrorSqlDialect } from "@/lib/editor/codemirrorSqlDialect";
import { sqlSemanticTableNameSpansForSyntaxTree } from "@/lib/editor/codemirrorSqlSemanticHighlight";
import { startsQueryEditorRectangularSelection } from "@/lib/editor/queryEditorPointerSelection";
import { LARGE_PASTE_HISTORY_USER_EVENT, normalizeQueryEditorPasteText, recoverableNativePasteSuffix, shouldRecoverLargeTauriPaste } from "@/lib/editor/queryEditorLargePaste";
import type { StatementExecutionMarker } from "@/lib/tabs/tabPresentation";
import { isSchemaAware, isSingleDatabase, supportsSqlInListPaste } from "@/lib/database/databaseFeatureSupport";
import { metadataSchemaForConnection } from "@/lib/database/jdbcDialect";
import { usesLocalOnlyEditorCompletionMetadata, usesOnDemandOnlyEditorColumnMetadata } from "@/lib/metadata/completionMetadataPolicy";
import { queryContextObjectActions, queryContextObjectRoute, queryTableCandidateAtSqlPosition, resolveQueryContextCandidateDatabase, resolveQueryContextObjectTarget, type QueryContextObjectAction } from "@/lib/sql/queryCursorTableTarget";
import * as api from "@/lib/backend/api";
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
import {
areSqlSemanticDiagnosticsEqual,
buildSqlParserErrorDiagnostic,
@@ -1003,6 +1006,44 @@ async function pasteClipboardAsSqlInCondition(): Promise<boolean> {
return true;
}
function recoverLargeTauriPaste(event: ClipboardEvent, currentView: EditorViewType): boolean {
const eventText = event.clipboardData?.getData("text/plain") ?? "";
if (props.readOnly || currentView.state.selection.ranges.length !== 1 || !shouldRecoverLargeTauriPaste(eventText, isTauriRuntime())) return false;
event.preventDefault();
const selection = currentView.state.selection.main;
const insertedText = normalizeQueryEditorPasteText(eventText);
const insertedFrom = selection.from;
const insertedTo = insertedFrom + insertedText.length;
const pasteStartedAt = Date.now();
currentView.dispatch({
changes: { from: selection.from, to: selection.to, insert: insertedText },
selection: { anchor: insertedTo },
scrollIntoView: true,
// CodeMirror only joins input.type history events; keep one timestamp so delayed recovery is one undo step.
annotations: Transaction.time.of(pasteStartedAt),
userEvent: LARGE_PASTE_HISTORY_USER_EVENT,
});
void readTextFromClipboard()
.then((nativeText) => {
const suffix = recoverableNativePasteSuffix(eventText, nativeText);
if (!suffix || props.readOnly || view.value !== currentView) return;
if (currentView.state.doc.sliceString(insertedFrom, insertedTo) !== insertedText) return;
const currentSelection = currentView.state.selection.main;
const selectionRemainedAtPasteEnd = currentSelection.empty && currentSelection.head === insertedTo;
currentView.dispatch({
changes: { from: insertedTo, insert: suffix },
...(selectionRemainedAtPasteEnd ? { selection: { anchor: insertedTo + suffix.length } } : {}),
scrollIntoView: selectionRemainedAtPasteEnd,
annotations: Transaction.time.of(pasteStartedAt),
userEvent: LARGE_PASTE_HISTORY_USER_EVENT,
});
})
.catch(() => {});
return true;
}
function deleteEmptyLines() {
const currentView = view.value;
if (!currentView || props.readOnly) return;
@@ -3567,6 +3608,9 @@ onMounted(async () => {
}),
),
EditorView.domEventHandlers({
paste(event, currentView) {
return recoverLargeTauriPaste(event, currentView);
},
dragover(event) {
if (props.readOnly || !hasDroppedTableReference(event)) return false;
event.preventDefault();