This commit is contained in:
Timothy Jaeryang Baek 2026-10-14 14:46:44 +04:00
parent a5abeb41ad
commit 0a245d3dfe
2 changed files with 47 additions and 3 deletions

View file

@ -14,7 +14,12 @@
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, clearLineAlignment } from './diffEditorAlignment';
import {
lineAlignment,
alignChangedLines,
clearLineAlignment,
pairedLinePosition
} from './diffEditorAlignment';
import {
copyChunk,
selectedChunk,
@ -222,10 +227,32 @@
(alignmentDocuments[0] !== documents[0] ||
alignmentDocuments[1] !== documents[1] ||
geometry !== alignmentGeometry);
const resized = alignmentDocuments && geometry !== alignmentGeometry;
alignmentDocuments = documents;
alignmentGeometry = geometry;
if (!reset || !clearLineAlignment(view.a, view.b))
alignChangedLines(view.a, view.b, view.chunks);
if (resized) {
// Reflow can leave one viewport entirely inside an old EOF spacer.
// Measure both panes around the row the reader was looking at.
const top = Math.max(0, node.getBoundingClientRect().top - view.a.documentTop);
const position = Math.min(
view.a.lineBlockAtHeight(top).from,
view.a.state.doc.length
);
const other = pairedLinePosition(
view.a.state.doc,
view.b.state.doc,
view.chunks,
position
);
view.a.dispatch({
effects: EditorView.scrollIntoView(position, { y: 'start', yMargin: 0 })
});
view.b.dispatch({
effects: EditorView.scrollIntoView(other, { y: 'start', yMargin: 0 })
});
}
view.reconfigure({});
}
});

View file

@ -1,4 +1,4 @@
import { StateEffect, StateField } from '@codemirror/state';
import { StateEffect, StateField, type Text } from '@codemirror/state';
import {
BlockType,
Decoration,
@ -8,6 +8,21 @@ import {
} from '@codemirror/view';
import type { Chunk } from '@codemirror/merge';
export function pairedLinePosition(a: Text, b: Text, chunks: readonly Chunk[], position: number) {
let offset = 0;
for (const chunk of chunks) {
if (position < chunk.fromA) break;
if (position < chunk.toA) {
const row = a.lineAt(position).number - a.lineAt(chunk.fromA).number;
const start = b.lineAt(Math.min(chunk.fromB, b.length)).number;
const end = b.lineAt(Math.min(Math.max(chunk.fromB, chunk.toB - 1), b.length)).number;
return b.line(Math.min(start + row, end)).from;
}
offset = chunk.toB - chunk.toA;
}
return Math.max(0, Math.min(position + offset, b.length));
}
class RowSpacer extends WidgetType {
constructor(readonly height: number) {
super();
@ -65,7 +80,9 @@ export function alignChangedLines(a: EditorView, b: EditorView, chunks: readonly
const block = view.lineBlockAt(pos);
const text = Array.isArray(block.type)
? block.type.find((part) => part.type === BlockType.Text && part.from === pos)
: block.type === BlockType.Text ? block : null;
: block.type === BlockType.Text
? block
: null;
return text?.from === pos ? text.top : null;
};
const align = (posA: number, posB: number) => {