fix: clear wrapped lines when redrawing a prompt - #190
Conversation
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 41 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (10)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit abd4fe1. Configure here.
Greptile SummaryAdds physical terminal-row accounting for wrapped prompt frames, updates affected widgets to use it, corrects Input cursor-offset and Spinner clearing behavior from previous feedback, and adds shared redraw regression tests. Confidence Score: 5/5The PR appears safe to merge. No blocking failures remain in the fixes associated with the previous review threads. Important Files Changed
Reviews (2): Last reviewed commit: "fix: keep input and spinner row math con..." | Re-trigger Greptile |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Any prompt whose rendered frame is wider than the terminal duplicates itself on every keypress.
Widgets clear the previous frame with
Term::clear_last_lines(self.height), which works in physical rows, butheightwas computed asoutput.lines().count() - 1— logical lines. A line wider than the terminal wraps into rows the clear never erases, so the next frame draws below the leftovers and the prompt stacks.Found via jdx/aube#1107, where a
Selectofpackage.jsonscripts (labels likebuild: tsc -p tsconfig.build.json && rollup -c …) scrambled on every arrow key. Driving that picker through a pty at 80 columns, two ↓ presses, replayed through a terminal emulator:The same frames at 200 columns are perfectly clean, which is what pins it on wrapping rather than anything terminal-specific.
The fix
A shared
height::rendered_height(output, width)counts the physical rows a frame occupies: for each line,measure_text_width(line).div_ceil(width), so ANSI codes don't inflate the count and a line exactlywidthwide still counts as one row (terminals defer the wrap).Applied to every widget that redraws via
clear_last_lines:Confirm,Dialog,Input,List,Select,Spinner.One subtlety worth flagging: the helper splits on
'\n'and drops the final segment rather than usinglines(). What we want is the rows above the cursor, and the cursor rests on whatever follows the last newline — the trailing color reset, or nothing.lines()swallows a trailing empty segment, so it would lose a row for any widget whose output doesn't end in that reset.split('\n')handles both endings identically.Not in this PR
MultiSelectis untouched. It doesn't useclear_last_lines—reposition_and_writerewrites line by line and clears each withclear_line, which erases one physical row. Making it wrap-aware means more than swapping the count (a shrinking wrapped line leaves a tail row thatclear_linewon't reach), so it deserves its own change rather than a partial one bolted onto this.Tests
rendered_height: the trailing-fragment handling, exact-width lines, multi-row wrapping, ANSI-aware measurement, and zero width.Selectregression test that drives three real redraw cycles through a capturedTerm, replays the bytes through vt100, and asserts the screen holds one copy of the prompt. It fails with logical-line counting (3 copies) and passes with physical rows.\n→\r\n) before feeding the emulator, since a real TTY does that and the row arithmetic is meaningless without it.Termcapture harness moved out ofmultiselect.rsintotest.rsso both suites share it.Select::run's render-and-record step is now a smalldraw()method, so the regression test exercises the same code path instead of a copy of it.cargo test --libis green (37 passing) andcargo clippy --all-targets -- -D warningsis clean.Verified end to end by building aube against this branch: the picker is stable at 80 columns and at 34, where the wrapping help footer alone used to retrigger the stacking.
This PR was generated by Claude.
Note
Medium Risk
Touches core TTY redraw/clear paths across multiple widgets; behavior is well-tested but incorrect row math would cause visible glitches or cursor misplacement.
Overview
Fixes prompts duplicating on every keypress when rendered text is wider than the terminal. Redraw logic used logical line count for
clear_last_lines, but terminals clear physical rows, so wrapped lines were left on screen.Adds
height::rendered_height(andrendered_rowsfor frames without a trailing newline) using terminal width andmeasure_text_widthso ANSI and wrapping are counted correctly. Confirm, Dialog, Input, List, Select, and Spinner now record frame height this way; Input also computesinput_line_offsetin rows for caret placement when titles wrap.Spinner clears by walking up from the cursor row instead of
clear_last_lines, since the cursor sits on the last row of the frame. Select centralizes redraw indraw()for tests.Adds unit tests for height math, Input offset behavior, a Select vt100 regression for stacked frames, and moves the unix Term capture harness to
test.rsfor reuse. MultiSelect is unchanged.Reviewed by Cursor Bugbot for commit d99347b. Bugbot is set up for automated code reviews on this repo. Configure here.