FIX: Fix chunk expansion for insertions at end of document #8

Closed
lidorbt-vega wants to merge 1 commit from fix-fromline-boundary-bug into main
lidorbt-vega commented 2026-03-15 03:01:16 +01:00 (Migrated from github.com)

When a change is a pure insertion at the end of a document (fromA == a.length), fromLine falls back to [lineA.from, lineB.from], expanding the chunk to cover the entire last line. This causes the unified merge view to mark unchanged content as part of the diff.

Reproduction

Diff "hello" against "hello\nworld" using unifiedMergeView. The entire first line hello is shown as both deleted and inserted, instead of world being a clean insertion.

Chunk.build(Text.of(["hello"]), Text.of(["hello", "world"]))
// Before fix: chunk covers fromA=0 (entire first line pulled in)
// After fix: chunk covers fromA=6 (only the insertion)

Fix

In fromLine, the condition fromA < a.length && fromB < b.length prevents advancing past the line end when either document has ended. But for insertions at document end, we should still advance if the position is at the end of a non-empty line (not the start of an empty trailing line).

The added check lineA.from < fromA || lineB.from < fromB distinguishes between:

  • Position at the end of a line (lineA.from < fromA) → advance to next line
  • Position at the start of an empty trailing line (lineA.from == fromA) → stay (existing behavior)
When a change is a pure insertion at the end of a document (`fromA == a.length`), `fromLine` falls back to `[lineA.from, lineB.from]`, expanding the chunk to cover the entire last line. This causes the unified merge view to mark unchanged content as part of the diff. ## Reproduction Diff `"hello"` against `"hello\nworld"` using `unifiedMergeView`. The entire first line `hello` is shown as both deleted and inserted, instead of `world` being a clean insertion. ```js Chunk.build(Text.of(["hello"]), Text.of(["hello", "world"])) // Before fix: chunk covers fromA=0 (entire first line pulled in) // After fix: chunk covers fromA=6 (only the insertion) ``` ## Fix In `fromLine`, the condition `fromA < a.length && fromB < b.length` prevents advancing past the line end when either document has ended. But for insertions at document end, we should still advance if the position is at the **end** of a non-empty line (not the start of an empty trailing line). The added check `lineA.from < fromA || lineB.from < fromB` distinguishes between: - Position at the **end** of a line (`lineA.from < fromA`) → advance to next line - Position at the **start** of an empty trailing line (`lineA.from == fromA`) → stay (existing behavior)
marijnh commented 2026-03-15 08:50:33 +01:00 (Migrated from github.com)

This behavior is intentional. Our chunk representation assumes every chunk starts on the start of a line, and chunks that start beyond the end of the document will break some of the other code. If we're considering the newline/eof after a line to be part of the line, the inclusion of that first line in your reproduction case in the chunk even more or less makes sense.

This behavior is intentional. Our chunk representation assumes every chunk starts on the start of a line, and chunks that start beyond the end of the document will break some of the other code. If we're considering the newline/eof after a line to be part of the line, the inclusion of that first line in your reproduction case in the chunk even more or less makes sense.

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
codemirror/merge!8
No description provided.