Skip to content

Wait for the CPR response before repainting after in_terminal - #2116

Open
aweis89 wants to merge 1 commit into
prompt-toolkit:mainfrom
aweis89:fix/in-terminal-wait-for-cpr
Open

aweis89 wants to merge 1 commit into
prompt-toolkit:mainfrom
aweis89:fix/in-terminal-wait-for-cpr

Conversation

@aweis89

@aweis89 aweis89 commented Oct 2, 2026

Copy link
Copy Markdown

Fixes #2115.

in_terminal requested the cursor position and repainted straight away. The renderer didn't know the space below the cursor yet, so layouts that fill the rest of the terminal were painted right under the new output, then painted again at the bottom once the response arrived. With patch_stdout that's a jump on every flush.

This keeps _running_in_terminal set while waiting for the response with renderer.wait_for_cpr_responses(), then clears it and redraws once. Invalidations that come in during the wait (key presses, refresh timers) are dropped like they are during the body, and the explicit redraw afterwards covers them.

Fallbacks are unchanged:

  • Outputs that don't respond to CPR, and terminals already marked NOT_SUPPORTED, return from the wait immediately.
  • On Windows the height is known synchronously, so nothing is queued and there's nothing to wait for.
  • If the response never arrives, the existing 1 second timeout in wait_for_cpr_responses applies and the app repaints as before.
  • Cancellation or an exception in the body still restores rendering and repaints, via finally.

One trade-off: on a high latency connection, the app now stays erased for one CPR round trip instead of showing a frame in the wrong place for that long and then jumping.

I added tests/test_run_in_terminal.py, which drives an inline app through a pipe input and a Vt100 output that responds to CPR. It checks that the first paint after in_terminal already knows the available height, and that the app still repaints when no response arrives. The first test fails on main.

in_terminal requested the cursor position and repainted right away, before
the response arrived. That paint doesn't know how many rows are free below
the cursor, so the layout gets its preferred height, right below the new
output. When the response arrives the layout is painted again, extended to
the bottom of the terminal. For layouts that fill the remaining space (a
bottom toolbar), every run_in_terminal call, including every patch_stdout
flush, made them jump up and back down.

Keep rendering disabled until the response arrives (or the existing CPR
timeout expires), then paint once.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

in_terminal repaints before the CPR response arrives, so inline layouts jump after every print

1 participant