Skip to content

fix: retain reader cursor tracking on status resize - #2326

Merged
gnodet merged 2 commits into
jline:masterfrom
snowyukitty:fix/status-resize-prompt
Oct 31, 2026
Merged

gnodet merged 2 commits into
jline:masterfrom
snowyukitty:fix/status-resize-prompt

Conversation

@snowyukitty

@snowyukitty snowyukitty commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Resizing a terminal with active Status lines recreates the reader's Display, losing the cursor offset into the existing prompt and input. The next redisplay starts from the current cursor and can leave a second copy of the prompt.

Resize the existing display to preserve its cursor offset, then invalidate its rendered lines so redisplay also repaints any prompt rows cleared by Status.resize(). Serialize signal handling with the existing reader lock so resize/reset cannot mutate a display while the input thread is rendering it. Use that lock instead of the separate object monitor to avoid reversing lock order for in-band resize callbacks. The no-status, empty-status and unchanged-grid rendering paths retain their existing behavior.

Add native virtual-terminal regressions for width/height changes where the prompt stays in the usable scroll region, status borders, prompt rows inside the cleanup area, wrapped input, and continued editing. A separate readLine test exercises the installed WINCH handler and accepts further input after resizing.

Existing limitation: shrinking a full screen far enough that the prompt falls into the new status rows can still corrupt the status area. This patch does not address that pre-existing case.

Validation (Linux, JDK 25):

MVX_USE_SYSTEM_JAVA=true ./mvx mvn -B --no-transfer-progress \
  -Dnisse.jgit.dynamicVersion=4.4.8-SNAPSHOT -pl reader -am clean verify

The duplicate-prompt, misplaced-prompt and concurrent-resize cases were also checked against unchanged upstream; unchanged-grid handling is a passing control. Testing uses JLine's virtual terminal; physical-terminal and cross-platform runs were not performed. Width-change behavior uses the same terminal-reflow assumption as the existing no-status path. Non-reflowing terminals with wrapped input are not covered; width-change tests use unwrapped input and the wrapped-input test changes height.

Refs #2319

Summary by CodeRabbit

  • Bug Fixes
    • Improved terminal resize handling, including when status rows are active, so the display and input remain usable after a resize.
    • Prevented display updates from racing with ongoing reader activity.
  • Tests
    • Added coverage for resize behavior across terminal sizes, wrapped input, status rows, and unchanged dimensions, including continued input after a resize.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 32b1d4ba-6a5a-4c11-bce9-763f1eff4fda
📥 Commits

Reviewing files that changed from the base of the PR and between 623f0f8 and 12591bb.

📒 Files selected for processing: 1
  • reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java
🚧 Files skipped from review as they are similar to previous changes: 1
  • reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

The WINCH handler now acquires and releases the reader lock explicitly. When terminal dimensions change and status rows are active, it resizes and resets the existing display, resizes the status area, and redisplays. New tests cover resize rendering, lock behavior, and continued input.

Changes: Terminal resize handling

Layer / File(s) Summary
Signal locking and resize handling
reader/src/main/java/org/jline/reader/impl/LineReaderImpl.java
handleSignal now uses explicit locking. When dimensions change with active status rows, it resizes and resets the existing display, resizes the status area, and redisplays.
Status-row resize and redisplay validation
reader/src/test/java/org/jline/reader/impl/LineReaderResizeTest.java
Tests cover varied terminal sizes, borders, unchanged dimensions, wrapped input, display content, lock waiting, and input during a WINCH-triggered redisplay.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: gnodet-bot

Merge Risk: ⚪ Minimal · up to 12591

The resize change has no identified merge-blocking issue; it appears ready to merge after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: preserving reader cursor tracking during status-row terminal resizing.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. No issues found.

The core fix is correct: replacing doDisplay() (which created a new Display object, discarding cursor tracking) with display.resize(size) + display.reset() preserves the cursor offset via reflow while forcing a full repaint — exactly what's needed.

The synchronized → ReentrantLock migration is the right call. The old code used synchronized(this) while all other concurrency went through the lock field, leaving a window where WINCH could mutate display while a widget was executing. The new code uses the same lock as the rest of the reader.

Test suite is comprehensive: 6 geometry variants, prompt-in-cleanup-area edge case, wrapped input, a full readLine integration test with post-resize input acceptance, and a lock-contention test that directly verifies mutual exclusion. Pre-fix validation: the assertStatusResize tests check that -- user -- appears exactly once after WINCH — the old doDisplay() path would have produced a duplicate prompt, so these tests would have failed before the fix.

One minor suggestion: the handleSignal Javadoc (which is protected and thus part of the subclass API) could gain an @implNote documenting the locking contract:

 * @implNote This method acquires the reader's {@code lock} to serialize
 *           with the input thread. Subclasses that override should do the same.

Not a blocker.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@gnodet gnodet added the bug label Oct 22, 2026
@sonarqubecloud

Copy link
Copy Markdown

@gnodet
gnodet merged commit f18c793 into jline:master Oct 31, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants