fix(edit_file): range mode replaces whole lines (#543) - #547
Open
atlas-from-plumb wants to merge 1 commit into
Open
atlas-from-plumb wants to merge 1 commit into
atlas-from-plumb wants to merge 1 commit into
Conversation
A range edit whose non-empty new_string lacked a trailing newline was glued onto the line after the range, while an empty new_string deleted cleanly. Replacing one line with two this way merged the second new line with the next one and broke the build, with no warning. applyRangeEdit now gives such a new_string the line ending of the text it replaces (\r\n in a CRLF line). A range to EOF in a file with no final newline replaced unterminated text, so it stays unterminated and the file keeps its missing final newline. Append mode follows the same rule, and its separator uses the file's line ending. Both the atomic and the apply_partial paths share applyRangeEdit. The new_string schema text says so; start_line's is trimmed to keep the pinned tools/list payload within budget (+2 bytes net). Closes #543 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
edit_filerange mode (edits: [{start_line, end_line, new_string}]) glued a non-emptynew_stringthat had no trailing newline onto the line after the range. An emptynew_stringdeleted its lines cleanly, so the two cases behaved differently. Replacing one struct field with two this way merged the second new field with the next one and broke the build, and nothing in the response flagged it (#543).Change
internal/tools/range_edit.go: range mode now works in whole lines. A non-emptynew_stringwithout a trailing newline gets the line ending of the text it replaces, so a CRLF line stays\r\n. The EOF exception follows from the same rule. A range that runs to the end of a file with no final newline replaced unterminated text, so the replacement stays unterminated and the file still has no final newline. Append mode (start_line: -1) uses the same rule: a file that ends with a newline keeps one, and a file without one stays that way. The separator added before appending to such a file now uses the file's line ending (\r\nin a CRLF file) rather than a bare\n. Deletion (new_string: "") is unchanged. The atomic path and theapply_partialpath both callapplyRangeEdit, so both are covered. Anchor mode is not touched.internal/tools/edit_file.go: thenew_stringschema text now says range mode replaces whole lines and adds a missing trailing newline except at EOF of a file that has none. Thestart_linetext is shorter so the pinned tools/list payload stays within its budget: 44,960 of 45,000 bytes, 2 more than on main.docs/tools.md: adds a short "Line-range edits" paragraph.CHANGELOG.md: one entry under 0.20.4 → Fixed.Tests
New file
internal/tools/range_edit_newline_test.go:TestApplyRangeEdit_WholeLines: 1→1, 1→2, 2→1 and N→0 lines, each with and without a trailing newline; first line; last line of a file with and without a final newline, including end_line -1 and a capped end_line; CRLF; empty file.TestApplyRangeEdit_AppendWholeLines: append to LF and CRLF files, with and without a final newline.TestEditFile_RangeEdit_IssueRepro: the struct-field case from the issue, run end to end throughExecute.TestEditFile_RangeEdit_BatchAndCRLF: four sequential range edits (replace, grow, delete, append) in one call on an LF file and a CRLF file, run through both the atomic and theapply_partialpaths.With
range_edit.goreverted to origin/main, 22 of the new subtests fail. The cases that pass there are the positive controls: explicit trailing newlines, deletions, and preserving a missing final newline. Ten hand-applied mutants were all killed: no terminator; no CRLF detection; always\n; no empty-string guard; LF-only separator; no empty-file branch; verbatim append; whole-file ending instead of the replaced range's ending; checking for a\r\nsuffix instead of\n; always-CRLF separator.go test ./... -count=1andgo test -tags=integration ./internal/cli/ -count=1both exit 0.golangci-lintis clean.Closes #543
🤖 Generated with Claude Code