Skip to content

fix(core): preserve missing final newline in apply_patch - #59154

Open
benjaminkincses wants to merge 1 commit into
anomalyco:devfrom
benjaminkincses:apply-patch-newline
Open

benjaminkincses wants to merge 1 commit into
anomalyco:devfrom
benjaminkincses:apply-patch-newline

Conversation

@benjaminkincses

Copy link
Copy Markdown

Issue for this PR

Closes #45912

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

derive() in packages/util/src/patch.ts always appended a trailing newline to the patched content, even when the patch never touched the end of the file. A file without a final newline gained one, and the reported diff showed a change nobody asked for.

The fix tracks whether any replacement reaches the end of the file. If none does, the result keeps the original ending: no final newline stays no final newline, a trailing blank line stays, CRLF endings stay as they are. If a replacement does rewrite the last line, the ending keeps the normalized behavior it has today, so the existing tests for that case do not change.

Same direction as #51803 proposed for the inverse case (a dropped trailing blank line), applied here to the missing final newline.

How did you verify your code works?

New tests in packages/core/test/patch.test.ts cover the issue repro, a mid-file edit on a file without a final newline, a file ending in a blank line, CRLF with no final newline, and a patch that rewrites the last line.

Before: derive("f", [{ oldLines: ["line1"], newLines: ["LINE1"] }], "line1\nline2\nline3") returned "LINE1\nline2\nline3\n".
After: it returns "LINE1\nline2\nline3".

Ran bun run script/test.ts test/patch.test.ts test/tool-patch.test.ts in packages/core: 91 pass, 0 fail. bun typecheck clean in packages/core and packages/util. Full packages/util suite: 135 pass, 0 fail.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified the fix. With the simulated model in the TUI, I ran a patch that only edits the first line of a file with no final newline. On the base the file gained a trailing newline and the diff showed the last line changed. With this PR the file keeps its original ending and only the edited line changes. Calling derive() directly also showed that a file ending in a blank line now keeps it, CRLF endings are preserved, and a patch that rewrites the last line still adds the final newline as before. The patch tests in packages/core pass (91/91) and packages/util type-checks clean.

Public API: unchanged.

Nothing blocks a merge. Two notes:

  • #56128 is open and rewrites the same lines in derive(), always adding the final newline back. The two conflict, so only one direction can land.
  • A patch that rewrites the last line of a file without a final newline still adds one, so the .editorconfig case raised in the issue thread is only partly addressed. That seems a reasonable trade-off, since the patch format has no way to say "no newline at end of file".

Before

After

This branch has not been deployed

No deployments
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.

apply_patch forces a trailing newline onto files that lacked one

1 participant