diff --git a/src/lib/components/common/DiffEditorModal.svelte b/src/lib/components/common/DiffEditorModal.svelte index 6f14bc331a..03e61df49e 100644 --- a/src/lib/components/common/DiffEditorModal.svelte +++ b/src/lib/components/common/DiffEditorModal.svelte @@ -3,7 +3,7 @@ import { getContext } from 'svelte'; import { copyToClipboard } from '$lib/utils'; import { basicSetup, EditorView } from 'codemirror'; - import { Compartment } from '@codemirror/state'; + import { Compartment, type Text } from '@codemirror/state'; import { keymap, type ViewUpdate } from '@codemirror/view'; import { indentWithTab, isolateHistory } from '@codemirror/commands'; import { LanguageDescription, indentUnit } from '@codemirror/language'; @@ -14,7 +14,7 @@ import ConfirmDialog from './ConfirmDialog.svelte'; import FileCodeEditor from '$lib/components/chat/FileNav/FileCodeEditor.svelte'; import XMark from '$lib/components/icons/XMark.svelte'; - import { lineAlignment, alignChangedLines } from './diffEditorAlignment'; + import { lineAlignment, alignChangedLines, clearLineAlignment } from './diffEditorAlignment'; import { copyChunk, selectedChunk, @@ -181,6 +181,8 @@ let view: MergeView | null = null; let observer: MutationObserver | undefined; let alignmentFrame = 0; + let alignmentDocuments: readonly [Text, Text] | null = null; + let alignmentGeometry = ''; const themes = [new Compartment(), new Compartment()]; const language = [new Compartment(), new Compartment()]; const path = session?.path; @@ -208,7 +210,22 @@ alignmentFrame = requestAnimationFrame(() => { alignmentFrame = 0; if (!disposed && view) { - alignChangedLines(view.a, view.b, view.chunks); + const documents = [view.a.state.doc, view.b.state.doc] as const; + const geometry = [ + view.a.contentDOM.clientWidth, + view.b.contentDOM.clientWidth, + view.a.defaultLineHeight, + view.b.defaultLineHeight + ].join(':'); + const reset = + alignmentDocuments && + (alignmentDocuments[0] !== documents[0] || + alignmentDocuments[1] !== documents[1] || + geometry !== alignmentGeometry); + alignmentDocuments = documents; + alignmentGeometry = geometry; + if (!reset || !clearLineAlignment(view.a, view.b)) + alignChangedLines(view.a, view.b, view.chunks); view.reconfigure({}); } }); diff --git a/src/lib/components/common/diffEditorAlignment.ts b/src/lib/components/common/diffEditorAlignment.ts index 80e5d568a4..3fb30c1a6b 100644 --- a/src/lib/components/common/diffEditorAlignment.ts +++ b/src/lib/components/common/diffEditorAlignment.ts @@ -30,11 +30,22 @@ export const lineAlignment = StateField.define({ create: () => Decoration.none, update: (value, transaction) => { for (const effect of transaction.effects) if (effect.is(setSpacers)) return effect.value; - return value.map(transaction.changes); + return transaction.docChanged ? Decoration.none : value; }, provide: (field) => EditorView.decorations.from(field) }); +export function clearLineAlignment(a: EditorView, b: EditorView) { + let changed = false; + for (const view of [a, b]) { + if (view.state.field(lineAlignment).size) { + view.dispatch({ effects: setSpacers.of(Decoration.none) }); + changed = true; + } + } + return changed; +} + // Only remeasure paired rows present in both virtualized viewports. Recomputing // offscreen spacers from estimated heights would move already aligned content. export function alignChangedLines(a: EditorView, b: EditorView, chunks: readonly Chunk[]) {