Skip to content

fix(semdiff): keep an edit at the end of the file in one replace hunk - #9

Merged
sujal-maheshwari2004 merged 1 commit into
mainfrom
fix/5-semdiff-last-line
Oct 18, 2026
Merged

sujal-maheshwari2004 merged 1 commit into
mainfrom
fix/5-semdiff-last-line

Conversation

@sujal-maheshwari2004

Copy link
Copy Markdown
Contributor

What was wrong

In changes(), the inner hunk loop stops when either side runs out. When that happened mid-hunk (editing the last line of a template with no trailing newline, or replacing the last N lines with M lines), the hunk was closed at that point, and the rest of the other side became a separate trailing hunk. One replace came out as a delete plus an insert, each with an empty side, so both had point delta 1.

Before, with the real Analyze on a 3-line prompt whose last line changed from "Always answer in English." to "Always answer in French.":

change={OldStart:2 OldEnd:3 NewStart:2 NewEnd:2} pointDelta=1.000 class="localized tweak"
change={OldStart:3 OldEnd:3 NewStart:2 NewEnd:3} pointDelta=1.000 class="structural"

After:

change={OldStart:2 OldEnd:3 NewStart:2 NewEnd:3} pointDelta=0.250 class="structural"

The fix

After the inner loop, if either side is exhausted, extend the hunk to the end of both sides (i, j = n, m). Nothing left can match, so the remainder belongs to the same contiguous hunk. The existing trailing check still covers hunks that start after the last matched line.

Note: a single replace at the very end of a file can still be classified structural. The down window hits the file edge at radius 2 with delta equal to the point delta, so the classifier counts it as persisting to the boundary. That is existing classifier behavior for edge-of-file edits and is not changed here. This PR only fixes the hunk split.

Tests

New TestEditAtEndIsOneReplace covers editing the last line, the only line, last line -> two lines, and last two lines -> one line.

Before the fix (all 4 fail):

--- FAIL: TestEditAtEndIsOneReplace/edit_last_line
    changes = [{OldStart:2 OldEnd:3 NewStart:2 NewEnd:2} {OldStart:3 OldEnd:3 NewStart:2 NewEnd:3}], want [{OldStart:2 OldEnd:3 NewStart:2 NewEnd:3}]

After:

$ GOWORK=off go build ./... && go vet ./... && go test -count=1 ./...
ok  github.com/priompt/proto/semdiff
ok  github.com/priompt/proto/validate

Fixes #5

🤖 Generated with Claude Code

When the LCS walk exhausted one side mid-hunk (e.g. editing the last
line of a template with no trailing newline), changes() closed the hunk
early and emitted the remainder as a separate trailing hunk: a delete
plus an insert, each with an empty side and point delta 1. Extend the
hunk to the end of both sides when either runs out, since nothing left
can match.

Fixes #5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sujal-maheshwari2004
sujal-maheshwari2004 merged commit f508efc into main Oct 18, 2026
1 check passed
@sujal-maheshwari2004
sujal-maheshwari2004 deleted the fix/5-semdiff-last-line branch October 18, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

semdiff: editing the last line without a trailing newline reports delete + insert

1 participant