Repository navigation
Conversation
… half smart_truncate kept non-important lines only while kept < max_lines/2 and broke out of the loop at max_lines-1, so an explicit --max-lines 4 on plain text returned 2 lines. The budget is an explicit request (rtk read --max-lines N, and the hook's head -N mapping) — important lines still claim it first, the earliest remaining lines now fill it to exactly N. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes core::filter::smart_truncate so --max-lines N returns exactly N content lines (plus the single [N more lines] marker), instead of stopping early (often at N/2) for plain text—matching the expected behavior for the hook’s head -N mapping.
Changes:
- Reworks
smart_truncateline selection to (1) reserve budget for “important” lines first, then (2) fill remaining budget with earliest remaining lines in original order. - Adds an explicit
max_lines == 0fast-path to avoid underflow and return only the marker. - Updates/extends unit tests to assert the exact budget fill and preserve the
kept + omitted == totalinvariant.
Review findings (should address)
- Performance/memory regression risk: the new approach allocates
keep: Vec<bool>sized tolines.len()and does an additional full pass to fill budget. Given this is in a core filtering path, consider tracking kept indices in aVec<usize>(capacitymax_lines) and emitting in-order (e.g., sort indices, then iterate once), or using a compact bitset, to avoid an extra full-length allocation. - Test robustness: multiple tests identify the marker line via
!l.contains("more lines"). That can accidentally drop legitimate content lines containing that substring and make the test pass incorrectly. Prefer matching the marker more precisely (e.g.,l.starts_with('[') && l.ends_with(" more lines]")) or asserting the last line matches the marker format and excluding only the last line.
|
#3941 (merged 2026-09-13) made the head and tail rewrites faithful ( One optional simplification, not a blocker: the |
Summary
smart_truncatekept non-important lines only whilekept < max_lines / 2and broke the loop atmax_lines - 1, so--max-lines 4on plain text returned 2 lines (the hook mapshead -4to exactly this call)max_lines: important lines (signatures, imports,pub/export, braces) claim it first — unchanged priority — and the earliest remaining lines fill whatever is left, in original file order[N more lines]still satisfieskept + N == totalFixes #2961
Implementation notes
max_lines == 0returns just the marker instead of underflowingmax_lines - 1Test plan
test_smart_truncate_fills_budget_exactly— 10 plain lines,--max-lines 4→line1..line4+[6 more lines](was 2 lines)test_smart_truncate_important_lines_keep_priority— important lines still win the budget, remainder filled in file ordertest_smart_truncate_no_annotations/test_smart_truncate_overflow_count_exactfor the new (correct) kept counts; the invariant they assert (kept + reported == total) is unchangedrtk read ten.txt --max-lines 4→ 4 lines +[6 more lines]cargo fmt --check && cargo clippy --all-targets && cargo test— 2441 passed, 0 failed, no new warnings