fix(sdk): normalize read slices after windowing (#3888)

Changes `slice_read_response` to split first and normalize line endings
only across the requested read window.

This avoids rewriting the full file contents for paginated reads while
preserving LF-normalized output for downstream edit and grep behavior.

### Benchmarks

Focused local microbenchmarks compared the original combined branch
against `main` on Python 3.14.2:

- `slice_read_response` on large CRLF content: **2.55x faster** for both
first-window and late-window reads (~60.8% improvement), with **~29.2%
lower peak memory**.
This commit is contained in:
Mason Daugherty
2026-06-11 19:07:07 -04:00
committed by GitHub
parent 2f432ba636
commit 33d900c98b
2 changed files with 13 additions and 7 deletions
+7 -7
View File
@@ -291,15 +291,12 @@ def slice_read_response(
if not content or content.strip() == "":
return content
# Normalize line endings to LF before slicing. State/Store backends may
# carry CRLF or CR content as written; downstream tooling (edit match,
# grep, format) assumes LF.
content = content.replace("\r\n", "\n").replace("\r", "\n")
# `splitlines(keepends=True)` retains each line's terminator, including
# the absence of one on the final line. Joining with `""` therefore
# round-trips the trailing-newline state of the file faithfully —
# required so `edit()` can report EOF-newline mismatches accurately.
# required so `edit()` can report EOF-newline mismatches accurately. It
# also splits on CR / CRLF, so line indexing matches the LF-normalized
# form without first rewriting the whole (potentially huge) string.
lines = content.splitlines(keepends=True)
start_idx = offset
end_idx = min(start_idx + limit, len(lines))
@@ -307,7 +304,10 @@ def slice_read_response(
if start_idx >= len(lines):
return ReadResult(error=f"Line offset {offset} exceeds file length ({len(lines)} lines)")
return "".join(lines[start_idx:end_idx])
# Normalize line endings to LF, but only across the requested window.
# State/Store backends may carry CRLF or CR content as written;
# downstream tooling (edit match, grep, format) assumes LF.
return "".join(lines[start_idx:end_idx]).replace("\r\n", "\n").replace("\r", "\n")
def perform_string_replacement(
@@ -343,6 +343,12 @@ class TestSliceReadResponse:
result = slice_read_response(self._file("a\nb\nc\nd\n"), offset=1, limit=2)
assert result == "b\nc\n"
def test_partial_window_normalizes_crlf(self) -> None:
"""An internal CRLF slice is LF-normalized even though only the window is rewritten."""
result = slice_read_response(self._file("a\r\nb\r\nc\r\nd\r\n"), offset=1, limit=2)
assert result == "b\nc\n"
assert "\r" not in result
def test_partial_window_ending_on_unterminated_last_line(self) -> None:
"""A window covering the last line keeps that line's missing-terminator state."""
result = slice_read_response(self._file("a\nb\nc"), offset=2, limit=1)