Skip to content

Prompt climbs one row per keystroke when the cursor row is below the reported terminal height #1205

Description

@bhouse-nexthop

Summary

When the kernel's terminal height is smaller than the real terminal, the prompt moves up one screen row on every keystroke, leaving blank rows below it. It stops once the anchor reaches the row reedline believes is the bottom, so it looks intermittent.

When it happens

The kernel winsize is shorter than the window actually attached:

situation winsize
serial console / IPMI SOL / console server (agetty) 0x0 → reedline assumes 80x24
expect / pexpect wrappers pty default 24x80
window resized where SIGWINCH never reached the pty stale, too short

Repro

stty rows 10     # lie to the kernel; the real window is 50+ rows
seq 1 40         # push the cursor below row 10
<your reedline app>

Type characters slowly. The prompt climbs one row per character until it reaches row 10.

stty rows 0 cols 0 reproduces the serial-console shape (climbs until row 24).

Evidence

Stub app, reedline =0.49.0 / crossterm =0.29.0, driven under a pty whose winsize is set with TIOCSWINSZ and whose CPR query is answered with a chosen row. Recording the MoveTo(0, row) emitted before each paint while typing s,h,o,w,Enter:

kernel rows CPR cursor row MoveTo(0, row) per repaint result
10 36 10, 35, 10, 34, 10, 33, 10, 32, 10, 31, 10, 30 climbs 1 row/keystroke
10 5 5, 5, 5, 5, 5, 5 stable (control)
0 (assumed 24) 40 24, 39, 24, 38, 24, 37, 24, 36, 24, 35, 24, 34 climbs
0 (assumed 24) 20 20, 20, 20, 20, 20, 20 stable (control)

The interleaved 10 / 24 is MoveTo(0, believed_height - 1) from queue_universal_scroll.

Cause

All in src/painting/painter.rs (0.49.0):

  1. L468 — a (0, 0) size becomes (80, 24).
  2. L482initialize_prompt_position guards only equality:
    if new_row == self.screen_height() {
    A new_row greater than screen_height takes the else branch and is stored as the anchor unchanged.
  3. L342remaining_lines() is screen_height - prompt_start_row, saturating, so an anchor at or below the believed height gives 0.
  4. L598-602 — with required_lines (1) >= remaining_lines (0), extra = 1, so it scrolls and decrements the anchor. Next keystroke the anchor is still past the believed height, so it repeats.

Suggested fix

Either, in initialize_prompt_position:

  • Clamp the anchor: new_row.min(self.screen_height().saturating_sub(1)), or
  • Grow screen_height — the CPR row is direct evidence the terminal is at least that tall.

Changing L482's == to >= alone is not enough: the anchor also needs to be brought inside the believed screen, or remaining_lines() stays 0.

Version

reedline 0.49.0, crossterm 0.29.0, Linux.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions