refactor: improve chunk acceptance and rejection logic in CodeMirrorDiffUtils

- Simplified the acceptAllChunks and rejectAllChunks functions to handle document changes in a more robust manner.
- Updated logic to merge changes in a single operation, addressing potential RangeErrors with empty originals.
- Enhanced the CodeMirrorDiffView to conditionally apply themes based on the original document's content.
This commit is contained in:
iliya 2026-03-01 17:58:37 +02:00
parent f4f02d5536
commit cd4cb8e10b
2 changed files with 14 additions and 19 deletions

View file

@ -42,15 +42,13 @@ export function acceptAllChunks(view: EditorView): boolean {
if (!result || result.chunks.length === 0) return false; if (!result || result.chunks.length === 0) return false;
const orig = getOriginalDoc(view.state); const orig = getOriginalDoc(view.state);
const specs: ChangeSpec[] = []; // More robust than per-chunk: merge chunk ranges can be inconsistent for "empty" originals
for (const chunk of result.chunks) { // (e.g. whitespace-only or reconstruction edge cases), which can throw RangeError.
specs.push({ // Accept-all semantics are simply: make original equal to current modified doc.
from: chunk.fromA, const changes = ChangeSet.of(
to: chunk.toA, [{ from: 0, to: orig.length, insert: view.state.doc.toString() }],
insert: view.state.doc.sliceString(chunk.fromB, chunk.toB), orig.length
}); );
}
const changes = ChangeSet.of(specs, orig.length);
view.dispatch({ view.dispatch({
effects: updateOriginalDoc.of({ doc: changes.apply(orig), changes }), effects: updateOriginalDoc.of({ doc: changes.apply(orig), changes }),
}); });
@ -63,15 +61,11 @@ export function rejectAllChunks(view: EditorView): boolean {
if (!result || result.chunks.length === 0) return false; if (!result || result.chunks.length === 0) return false;
const orig = getOriginalDoc(view.state); const orig = getOriginalDoc(view.state);
const specs: ChangeSpec[] = []; // Same robustness principle as acceptAllChunks: reject-all semantics are simply
for (const chunk of result.chunks) { // "restore the current doc to the original baseline" in one edit.
specs.push({ view.dispatch({
from: chunk.fromB, changes: [{ from: 0, to: view.state.doc.length, insert: orig.toString() }],
to: chunk.toB, });
insert: orig.sliceString(chunk.fromA, chunk.toA),
});
}
view.dispatch({ changes: specs });
return true; return true;
} }

View file

@ -381,10 +381,11 @@ export const CodeMirrorDiffView = ({
); );
const buildExtensions = useCallback(() => { const buildExtensions = useCallback(() => {
const isEffectivelyEmptyOriginal = original.trim().length === 0;
const extensions: Extension[] = [ const extensions: Extension[] = [
baseEditorTheme, baseEditorTheme,
diffSpecificTheme, diffSpecificTheme,
...(original.length === 0 ? [emptyOriginalOverrideTheme] : []), ...(isEffectivelyEmptyOriginal ? [emptyOriginalOverrideTheme] : []),
lineNumbers(), lineNumbers(),
syntaxHighlighting(oneDarkHighlightStyle), syntaxHighlighting(oneDarkHighlightStyle),
EditorView.editable.of(!readOnly), EditorView.editable.of(!readOnly),