feat(editor): batch-select completion fields via drag

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-09-02 18:07:25 +08:00
1 parent 72dcd8355a
commit fdea607efd
4 files changed
+396 -11

No files matched your search

@@ -571,8 +571,10 @@ let buildSqlSemanticHighlightExtension: (() => import("@codemirror/state").Exten
let codeMirrorSnippetCompletion: typeof import("@codemirror/autocomplete").snippetCompletion;
let codeMirrorCompletionStatus: typeof import("@codemirror/autocomplete").completionStatus | null = null;
let codeMirrorAcceptCompletion: typeof import("@codemirror/autocomplete").acceptCompletion | null = null;
let codeMirrorCurrentCompletions: typeof import("@codemirror/autocomplete").currentCompletions | null = null;
let codeMirrorSelectedCompletionIndex: typeof import("@codemirror/autocomplete").selectedCompletionIndex | null = null;
let codeMirrorSelectedCompletion: typeof import("@codemirror/autocomplete").selectedCompletion | null = null;
let codeMirrorSetSelectedCompletion: typeof import("@codemirror/autocomplete").setSelectedCompletion | null = null;
let codeMirrorMoveCompletionSelection: typeof import("@codemirror/autocomplete").moveCompletionSelection | null = null;
let codeMirrorSelectFirstCompletion: import("@codemirror/view").Command | null = null;
let codeMirrorStartCompletion: typeof import("@codemirror/autocomplete").startCompletion | null = null;
@@ -3605,11 +3607,158 @@ interface BatchColumnSelectionActionItem {
type QueryCompletionOption = Completion & {
dbxBatchColumnSelection?: { sessionKey: string; candidateKey: string };
dbxBatchColumnSelectionAction?: { sessionKey: string };
};
let batchColumnSelectionSession: BatchColumnSelectionSession | null = null;
type BatchColumnSelectionCheckboxMarker = NonNullable<QueryCompletionOption["dbxBatchColumnSelection"]>;
interface BatchColumnSelectionDragState {
view: EditorViewType;
sessionKey: string;
pointerId: number;
anchorCandidateKey: string;
baseSelectedKeys: Set<string>;
selected: boolean;
focusCandidateKey: string;
previousUserSelect: string;
scrollElement: HTMLElement | null;
pointerClientX: number;
pointerClientY: number;
autoScrollFrame: number;
}
const batchColumnSelectionCheckboxMarkers = new WeakMap<HTMLElement, BatchColumnSelectionCheckboxMarker>();
const batchColumnSelectionActionMarkers = new WeakMap<HTMLElement, string>();
const batchColumnSelectionTooltipParents = new WeakMap<EditorViewType, HTMLElement>();
let batchColumnSelectionDragState: BatchColumnSelectionDragState | null = null;
let batchColumnSelectionRefreshCleanup: (() => void) | null = null;
let batchColumnSelectionExpandedRendering = false;
let batchColumnSelectionRenderLimitTimer: number | null = null;
function cancelBatchColumnSelectionRefresh() {
batchColumnSelectionRefreshCleanup?.();
batchColumnSelectionRefreshCleanup = null;
}
function setBatchColumnSelectionExpandedRendering(expanded: boolean) {
if (batchColumnSelectionExpandedRendering === expanded) return;
batchColumnSelectionExpandedRendering = expanded;
if (batchColumnSelectionRenderLimitTimer !== null) window.clearTimeout(batchColumnSelectionRenderLimitTimer);
const currentView = view.value;
if (!currentView || !completionComp || !buildSqlCompletionExtension) return;
batchColumnSelectionRenderLimitTimer = window.setTimeout(() => {
batchColumnSelectionRenderLimitTimer = null;
if (view.value !== currentView || !completionComp || !buildSqlCompletionExtension) return;
currentView.dispatch({ effects: completionComp.reconfigure(buildSqlCompletionExtension()) });
if (expanded && codeMirrorCompletionStatus?.(currentView.state) === "active") codeMirrorStartCompletion?.(currentView);
}, 0);
}
function setBatchColumnSelectionValue(sessionKey: string, candidateKey: string, selected: boolean, checkbox?: HTMLInputElement) {
const session = batchColumnSelectionSession;
if (!session || session.key !== sessionKey || !session.candidates.some((candidate) => candidate.key === candidateKey)) return;
if (selected) session.selectedKeys.add(candidateKey);
else session.selectedKeys.delete(candidateKey);
if (checkbox) checkbox.checked = selected;
}
function updateBatchColumnSelectionActionLabel(view: EditorViewType, sessionKey: string) {
const session = batchColumnSelectionSession;
if (!session || session.key !== sessionKey) return;
const label = t("editor.completion.insertSelectedColumns", { count: session.selectedKeys.size });
const tooltipParent = batchColumnSelectionTooltipParents.get(view) ?? view.dom;
tooltipParent.querySelectorAll<HTMLElement>(".cm-batch-column-selection-action-marker").forEach((marker) => {
if (batchColumnSelectionActionMarkers.get(marker) !== sessionKey) return;
const element = marker.closest("li")?.querySelector<HTMLElement>(".cm-completionLabel");
if (!element) return;
if (element.textContent !== label) element.textContent = label;
});
}
function scheduleBatchColumnSelectionRefresh(view: EditorViewType, sessionKey: string, focusCandidateKey: string, scrollState?: { element: HTMLElement | null; top: number; left: number }) {
cancelBatchColumnSelectionRefresh();
const preservedScroll =
scrollState ??
(() => {
const element =
visibleBatchColumnSelectionCheckboxes(sessionKey)
.map(({ checkbox }) => batchColumnSelectionScrollElement(checkbox))
.find((candidate): candidate is HTMLElement => !!candidate) ?? null;
return { element, top: element?.scrollTop ?? 0, left: element?.scrollLeft ?? 0 };
})();
const restoreScroll = () => {
const currentElement =
visibleBatchColumnSelectionCheckboxes(sessionKey)
.map(({ checkbox }) => batchColumnSelectionScrollElement(checkbox))
.find((candidate): candidate is HTMLElement => !!candidate) ?? preservedScroll.element;
if (!currentElement) return;
currentElement.scrollTop = Math.min(preservedScroll.top, Math.max(0, currentElement.scrollHeight - currentElement.clientHeight));
currentElement.scrollLeft = Math.min(preservedScroll.left, Math.max(0, currentElement.scrollWidth - currentElement.clientWidth));
};
let restoreAttempts = 0;
let restoreFrame = 0;
let restoreTimer = 0;
let restoreStartTimer = 0;
let stopped = false;
const restoreObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(restoreScroll);
const stopRestore = () => {
if (stopped) return;
stopped = true;
restoreObserver?.disconnect();
if (restoreFrame) window.cancelAnimationFrame(restoreFrame);
if (restoreTimer) window.clearTimeout(restoreTimer);
if (restoreStartTimer) window.clearTimeout(restoreStartTimer);
if (batchColumnSelectionRefreshCleanup === stopRestore) batchColumnSelectionRefreshCleanup = null;
};
batchColumnSelectionRefreshCleanup = stopRestore;
const restoreNextFrame = () => {
if (batchColumnSelectionSession?.key !== sessionKey) {
stopRestore();
return;
}
restoreScroll();
restoreAttempts += 1;
if (restoreAttempts < 60) restoreFrame = window.requestAnimationFrame(restoreNextFrame);
else stopRestore();
};
restoreStartTimer = window.setTimeout(() => {
if (stopped || batchColumnSelectionSession?.key !== sessionKey) {
stopRestore();
return;
}
restoreObserver?.observe(document.body, { childList: true, subtree: true });
// Keep CodeMirror's virtualized range anchored to the item where the drag ended.
const focusIndex =
codeMirrorCurrentCompletions?.(view.state).findIndex((completion) => {
const marker = (completion as QueryCompletionOption).dbxBatchColumnSelection;
return marker?.sessionKey === sessionKey && marker.candidateKey === focusCandidateKey;
}) ?? -1;
if (focusIndex >= 0 && codeMirrorSetSelectedCompletion) view.dispatch({ effects: codeMirrorSetSelectedCompletion(focusIndex) });
codeMirrorStartCompletion?.(view);
restoreNextFrame();
restoreTimer = window.setTimeout(stopRestore, 1500);
}, 0);
}
function finishBatchColumnSelectionDrag(refresh = true) {
const state = batchColumnSelectionDragState;
if (!state) return;
const scrollState = state.scrollElement ? { element: state.scrollElement, top: state.scrollElement.scrollTop, left: state.scrollElement.scrollLeft } : undefined;
batchColumnSelectionDragState = null;
if (state.autoScrollFrame) window.cancelAnimationFrame(state.autoScrollFrame);
window.removeEventListener("pointermove", onBatchColumnSelectionPointerMove, true);
window.removeEventListener("pointerup", onBatchColumnSelectionPointerUp, true);
window.removeEventListener("pointercancel", onBatchColumnSelectionPointerCancel, true);
window.removeEventListener("blur", onBatchColumnSelectionPointerCancel, true);
document.body.style.userSelect = state.previousUserSelect;
if (refresh) scheduleBatchColumnSelectionRefresh(state.view, state.sessionKey, state.focusCandidateKey, scrollState);
}
function clearBatchColumnSelectionSession() {
finishBatchColumnSelectionDrag(false);
cancelBatchColumnSelectionRefresh();
setBatchColumnSelectionExpandedRendering(false);
batchColumnSelectionSession = null;
}
@@ -3624,7 +3773,7 @@ function batchColumnSelectionCandidateKey(item: SqlCompletionItem): string {
function prepareBatchColumnSelectionSession(items: SqlCompletionItem[], document: string, from: number, to: number): BatchColumnSelectionSession | null {
const selectableItems = items.filter((item) => item.type === "column" && item.batchSelectionMode && item.apply);
if (selectableItems.length === 0) {
batchColumnSelectionSession = null;
clearBatchColumnSelectionSession();
return null;
}
@@ -3644,9 +3793,11 @@ function prepareBatchColumnSelectionSession(items: SqlCompletionItem[], document
selectedKeys: new Set(),
completionOptions: new Map(),
};
setBatchColumnSelectionExpandedRendering(true);
return batchColumnSelectionSession;
}
setBatchColumnSelectionExpandedRendering(true);
batchColumnSelectionSession.candidates = candidates;
batchColumnSelectionSession.qualifier = selectableItems[0]!.batchSelectionQualifier;
const candidateKeys = new Set(candidates.map((candidate) => candidate.key));
@@ -3675,13 +3826,10 @@ function cacheBatchColumnSelectionOption(marker: QueryCompletionOption["dbxBatch
function toggleBatchColumnSelection(view: EditorViewType, sessionKey: string, candidateKey: string) {
const session = batchColumnSelectionSession;
if (!session || session.key !== sessionKey || !session.candidates.some((candidate) => candidate.key === candidateKey)) return;
if (session.selectedKeys.has(candidateKey)) {
session.selectedKeys.delete(candidateKey);
} else {
session.selectedKeys.add(candidateKey);
}
setBatchColumnSelectionValue(sessionKey, candidateKey, !session.selectedKeys.has(candidateKey));
updateBatchColumnSelectionActionLabel(view, sessionKey);
// Reopen the list so the action row and all virtualized checkboxes reflect the new state.
window.setTimeout(() => codeMirrorStartCompletion?.(view), 0);
scheduleBatchColumnSelectionRefresh(view, sessionKey, candidateKey);
}
function toggleSelectedBatchColumnSelection(view: EditorViewType): boolean {
@@ -3692,6 +3840,143 @@ function toggleSelectedBatchColumnSelection(view: EditorViewType): boolean {
return true;
}
function batchColumnSelectionMarkerAtPoint(clientX: number, clientY: number): { checkbox: HTMLInputElement; marker: BatchColumnSelectionCheckboxMarker } | null {
const target = document.elementFromPoint(clientX, clientY);
if (!(target instanceof HTMLElement)) return null;
const checkbox = target.closest<HTMLInputElement>("input.cm-batch-column-selection-checkbox") ?? target.closest<HTMLElement>("[role='option']")?.querySelector<HTMLInputElement>("input.cm-batch-column-selection-checkbox") ?? null;
if (!checkbox) return null;
const marker = batchColumnSelectionCheckboxMarkers.get(checkbox);
return marker ? { checkbox, marker } : null;
}
function batchColumnSelectionScrollElement(checkbox: HTMLElement): HTMLElement | null {
let current = checkbox.parentElement;
while (current && current !== document.body) {
const style = window.getComputedStyle(current);
if (current.scrollHeight > current.clientHeight && /(auto|scroll|overlay)/.test(style.overflowY)) return current;
current = current.parentElement;
}
return null;
}
function visibleBatchColumnSelectionCheckboxes(sessionKey: string): Array<{ checkbox: HTMLInputElement; marker: BatchColumnSelectionCheckboxMarker }> {
return Array.from(document.querySelectorAll<HTMLInputElement>("input.cm-batch-column-selection-checkbox")).flatMap((checkbox) => {
const marker = batchColumnSelectionCheckboxMarkers.get(checkbox);
return marker?.sessionKey === sessionKey ? [{ checkbox, marker }] : [];
});
}
function updateBatchColumnSelectionAtPoint(state: BatchColumnSelectionDragState, clientX: number, clientY: number) {
const hit = batchColumnSelectionMarkerAtPoint(clientX, clientY);
if (!hit || hit.marker.sessionKey !== state.sessionKey) return;
state.focusCandidateKey = hit.marker.candidateKey;
const session = batchColumnSelectionSession;
if (!session) return;
const visibleCheckboxes = visibleBatchColumnSelectionCheckboxes(state.sessionKey);
const anchorIndex = visibleCheckboxes.findIndex(({ marker }) => marker.candidateKey === state.anchorCandidateKey);
const focusIndex = visibleCheckboxes.findIndex(({ marker }) => marker.candidateKey === hit.marker.candidateKey);
if (anchorIndex < 0 || focusIndex < 0) return;
const nextSelectedKeys = new Set(state.baseSelectedKeys);
const rangeStart = Math.min(anchorIndex, focusIndex);
const rangeEnd = Math.max(anchorIndex, focusIndex);
for (let index = rangeStart; index <= rangeEnd; index++) {
const candidate = visibleCheckboxes[index]?.marker;
if (!candidate) continue;
if (state.selected) nextSelectedKeys.add(candidate.candidateKey);
else nextSelectedKeys.delete(candidate.candidateKey);
}
session.selectedKeys = nextSelectedKeys;
visibleCheckboxes.forEach(({ checkbox, marker }) => {
checkbox.checked = nextSelectedKeys.has(marker.candidateKey);
});
updateBatchColumnSelectionActionLabel(state.view, state.sessionKey);
}
const BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX = 36;
const BATCH_COLUMN_SELECTION_AUTO_SCROLL_MAX_PX = 20;
function runBatchColumnSelectionAutoScroll() {
const state = batchColumnSelectionDragState;
if (!state) return;
state.autoScrollFrame = 0;
const scroller = state.scrollElement;
if (!scroller) return;
const rect = scroller.getBoundingClientRect();
if (state.pointerClientX < rect.left || state.pointerClientX > rect.right) return;
let delta = 0;
if (state.pointerClientY < rect.top + BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX) {
delta = -BATCH_COLUMN_SELECTION_AUTO_SCROLL_MAX_PX * Math.min(1, (rect.top + BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX - state.pointerClientY) / BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX);
} else if (state.pointerClientY > rect.bottom - BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX) {
delta = BATCH_COLUMN_SELECTION_AUTO_SCROLL_MAX_PX * Math.min(1, (state.pointerClientY - (rect.bottom - BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX)) / BATCH_COLUMN_SELECTION_AUTO_SCROLL_EDGE_PX);
}
if (!delta) return;
const previousScrollTop = scroller.scrollTop;
scroller.scrollTop = Math.max(0, Math.min(scroller.scrollHeight - scroller.clientHeight, scroller.scrollTop + delta));
if (scroller.scrollTop === previousScrollTop) return;
updateBatchColumnSelectionAtPoint(state, state.pointerClientX, state.pointerClientY);
state.autoScrollFrame = window.requestAnimationFrame(runBatchColumnSelectionAutoScroll);
}
function scheduleBatchColumnSelectionAutoScroll(state: BatchColumnSelectionDragState) {
if (!state.scrollElement || state.autoScrollFrame) return;
state.autoScrollFrame = window.requestAnimationFrame(runBatchColumnSelectionAutoScroll);
}
function onBatchColumnSelectionPointerMove(event: PointerEvent) {
const state = batchColumnSelectionDragState;
if (!state || event.pointerId !== state.pointerId) return;
if ((event.buttons & 1) === 0) {
finishBatchColumnSelectionDrag();
return;
}
state.pointerClientX = event.clientX;
state.pointerClientY = event.clientY;
updateBatchColumnSelectionAtPoint(state, event.clientX, event.clientY);
scheduleBatchColumnSelectionAutoScroll(state);
}
function onBatchColumnSelectionPointerUp(event: PointerEvent) {
const state = batchColumnSelectionDragState;
if (!state || event.pointerId !== state.pointerId) return;
finishBatchColumnSelectionDrag();
}
function onBatchColumnSelectionPointerCancel() {
finishBatchColumnSelectionDrag();
}
function startBatchColumnSelectionDrag(view: EditorViewType, marker: BatchColumnSelectionCheckboxMarker, checkbox: HTMLInputElement, event: PointerEvent) {
if (event.button !== 0 || !event.isPrimary) return;
finishBatchColumnSelectionDrag(false);
cancelBatchColumnSelectionRefresh();
const session = batchColumnSelectionSession;
if (!session || session.key !== marker.sessionKey) return;
if (!session.candidates.some((candidate) => candidate.key === marker.candidateKey)) return;
const selected = !session.selectedKeys.has(marker.candidateKey);
const baseSelectedKeys = new Set(session.selectedKeys);
setBatchColumnSelectionValue(marker.sessionKey, marker.candidateKey, selected, checkbox);
batchColumnSelectionDragState = {
view,
sessionKey: marker.sessionKey,
pointerId: event.pointerId,
anchorCandidateKey: marker.candidateKey,
baseSelectedKeys,
selected,
focusCandidateKey: marker.candidateKey,
previousUserSelect: document.body.style.userSelect,
scrollElement: batchColumnSelectionScrollElement(checkbox),
pointerClientX: event.clientX,
pointerClientY: event.clientY,
autoScrollFrame: 0,
};
updateBatchColumnSelectionActionLabel(view, marker.sessionKey);
document.body.style.userSelect = "none";
window.addEventListener("pointermove", onBatchColumnSelectionPointerMove, true);
window.addEventListener("pointerup", onBatchColumnSelectionPointerUp, true);
window.addEventListener("pointercancel", onBatchColumnSelectionPointerCancel, true);
window.addEventListener("blur", onBatchColumnSelectionPointerCancel, true);
}
function renderBatchColumnSelectionCheckbox(completion: Completion, _state: import("@codemirror/state").EditorState, currentView: EditorViewType): Node | null {
const marker = (completion as QueryCompletionOption).dbxBatchColumnSelection;
const session = batchColumnSelectionSession;
@@ -3703,12 +3988,17 @@ function renderBatchColumnSelectionCheckbox(completion: Completion, _state: impo
checkbox.checked = session.selectedKeys.has(marker.candidateKey);
checkbox.tabIndex = -1;
checkbox.setAttribute("aria-label", completion.displayLabel ?? completion.label);
batchColumnSelectionCheckboxMarkers.set(checkbox, marker);
checkbox.addEventListener("pointerdown", (event) => {
event.preventDefault();
event.stopPropagation();
startBatchColumnSelectionDrag(currentView, marker, checkbox, event);
});
checkbox.addEventListener("mousedown", (event) => {
// CodeMirror accepts the completion on a list-item mousedown. Keep this
// interaction local to the checkbox so a field can be toggled repeatedly.
event.preventDefault();
event.stopPropagation();
toggleBatchColumnSelection(currentView, marker.sessionKey, marker.candidateKey);
});
checkbox.addEventListener("click", (event) => {
event.preventDefault();
@@ -3717,6 +4007,16 @@ function renderBatchColumnSelectionCheckbox(completion: Completion, _state: impo
return checkbox;
}
function renderBatchColumnSelectionActionMarker(completion: Completion): Node | null {
const action = (completion as QueryCompletionOption).dbxBatchColumnSelectionAction;
if (!action) return null;
const marker = document.createElement("span");
marker.className = "cm-batch-column-selection-action-marker";
marker.hidden = true;
batchColumnSelectionActionMarkers.set(marker, action.sessionKey);
return marker;
}
function applyBatchColumnSelection(view: EditorViewType, item: BatchColumnSelectionActionItem, from: number, to: number) {
const session = batchColumnSelectionSession;
if (!session || session.key !== item.sessionKey || session.document !== view.state.doc.toString()) return;
@@ -3799,7 +4099,7 @@ function isTypedCompletionActivation(explicit: boolean) {
}
function markCompletionAccepted(item: QueryCompletionItem | BatchColumnSelectionActionItem) {
batchColumnSelectionSession = null;
clearBatchColumnSelectionSession();
const shouldContinueCompletion = shouldChainSqlCompletionAfterAccept(item) || (props.databaseType === "sqlserver" && item.type === "keyword" && item.label.toUpperCase() === "USE");
suppressNextSqlCompletionAutoStartUntil = shouldContinueCompletion ? 0 : Date.now() + 750;
completionEpoch++;
@@ -3910,6 +4210,7 @@ function completionOptionForItem(item: QueryCompletionItem | BatchColumnSelectio
if (isBatchColumnSelectionAction(item)) {
return {
...labelPresentation,
dbxBatchColumnSelectionAction: { sessionKey: item.sessionKey },
type: item.type,
detail: item.detail,
boost: item.boost,
@@ -5206,7 +5507,24 @@ onMounted(async () => {
},
{ EditorState, EditorSelection, Compartment, Prec, RangeSet, StateEffect, StateField },
langSql,
{ autocompletion, startCompletion, acceptCompletion, closeBrackets, closeBracketsKeymap, snippetCompletion, completionStatus, completionKeymap, insertCompletionText, nextSnippetField, closeCompletion, moveCompletionSelection, selectedCompletion, selectedCompletionIndex },
{
autocompletion,
startCompletion,
acceptCompletion,
closeBrackets,
closeBracketsKeymap,
snippetCompletion,
completionStatus,
completionKeymap,
insertCompletionText,
nextSnippetField,
closeCompletion,
moveCompletionSelection,
selectedCompletion,
selectedCompletionIndex,
currentCompletions,
setSelectedCompletion,
},
{ copyLineDown, copyLineUp, deleteLine, indentLess, indentMore, insertNewlineKeepIndent, moveLineDown, moveLineUp, redo, selectAll, undo, toggleLineComment, toggleBlockComment, history, defaultKeymap, historyKeymap },
{ bracketMatching, foldGutter, indentOnInput, indentUnit, syntaxHighlighting, defaultHighlightStyle, foldKeymap, toggleFold, ensureSyntaxTree, highlightingFor, syntaxTree },
{ searchKeymap },
@@ -5243,8 +5561,10 @@ onMounted(async () => {
setSqlDiagnosticsEffect = StateEffect.define<SqlSemanticDiagnostic[]>();
codeMirrorCompletionStatus = completionStatus;
codeMirrorAcceptCompletion = acceptCompletion;
codeMirrorCurrentCompletions = currentCompletions;
codeMirrorSelectedCompletion = selectedCompletion;
codeMirrorSelectedCompletionIndex = selectedCompletionIndex;
codeMirrorSetSelectedCompletion = setSelectedCompletion;
codeMirrorMoveCompletionSelection = moveCompletionSelection;
codeMirrorSelectFirstCompletion = moveCompletionSelection(true);
codeMirrorCloseCompletion = closeCompletion;
@@ -5483,7 +5803,13 @@ onMounted(async () => {
defaultKeymap: false,
selectOnOpen: settingsStore.editorSettings.selectFirstCompletionOnOpen,
compareCompletions: (a, b) => compareSqlCompletions(a, b, settingsStore.editorSettings.sortCompletionColumnsAlphabetically),
addToOptions: [{ position: 10, render: renderBatchColumnSelectionCheckbox }],
// Keep normal completion lists virtualized; batch-field selection needs every row in the DOM.
maxRenderedOptions: batchColumnSelectionExpandedRendering ? Number.MAX_SAFE_INTEGER : 100,
optionClass: (completion) => ((completion as QueryCompletionOption).dbxBatchColumnSelectionAction ? "cm-batch-column-selection-action" : ""),
addToOptions: [
{ position: 5, render: renderBatchColumnSelectionActionMarker },
{ position: 10, render: renderBatchColumnSelectionCheckbox },
],
override: [async (context: CompletionContext) => provideSqlCompletions(context)],
});
@@ -6156,6 +6482,7 @@ onMounted(async () => {
});
view.value = new EditorView({ state, parent: editorElement });
batchColumnSelectionTooltipParents.set(view.value, tooltipParent);
postCompositionKeyGuardCleanup = postCompositionKeyGuard.attach(view.value.contentDOM);
registerEditorScrollbarPointerGuard(view.value);
view.value.scrollDOM.addEventListener("scroll", scheduleEditorViewportEmit, {
@@ -6450,6 +6777,8 @@ watch(
);
function pauseQueryEditorBackgroundWork() {
finishBatchColumnSelectionDrag(false);
cancelBatchColumnSelectionRefresh();
flushEditorViewport();
flushEditorSelection();
clearTableNavigationHover();