Skip to content

Fix VT formatter cursor restoration after margins - #191

Merged
lawrencecchen merged 11 commits into
mainfrom
feat-vt-formatter-cursor-replay
Aug 10, 2026
Merged

lawrencecchen merged 11 commits into
mainfrom
feat-vt-formatter-cursor-replay

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Aug 8, 2026 •

Copy link
Copy Markdown

Fix VT formatter cursor restoration after scrolling-region and tabstop state.

The first commit adds a regression test. The second commit restores the cursor after terminal-wide state and converts CUP coordinates for origin mode.

Verification will run through the cmux exact-head hosted workflow on Blacksmith macOS and Linux, plus hosted Windows. No local Zig or Rust command was run.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Restore the cursor after emitting scrolling margins, origin mode, and tabstop state in the VT formatter. Prevents cursor jumps, preserves pending-wrap, and keeps saved cursors intact during replay, including when origin/scrolling region are omitted and when the cursor is outside origin margins.

  • Bug Fixes
    • Restore the cursor after terminal-wide state; use CUP with coordinates derived from emitted origin/margins and update pin_map.
    • Clamp outside-origin CUP coordinates to the margin’s top-left instead of using DECSC/DECRC, preserving user-saved cursors. Add regression tests for margins+origin, outside-origin clamping, tabstop-only, omitted origin, and omitted scrolling region.

Written for commit 533c27a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved terminal screen formatting to preserve cursor position during replay.
    • Correctly maintains cursor placement when scrolling margins and origin mode are active.
    • Preserves cursor placement after tab-related terminal output, including when scrolling regions are not configured.
    • Fixed cursor restoration for positions outside the active scrolling region.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

TerminalFormatter.format now restores the cursor after terminal-state output. It adjusts CUP coordinates for origin mode and scrolling margins, maps the emitted bytes, and adds replay tests for margin and tabstop cases.

Changes

Terminal formatting state

Layer / File(s) Summary
Cursor restoration and replay validation
src/terminal/formatter.zig
TerminalFormatter.format emits mapped cursor restoration sequences. The implementation handles origin mode, scrolling margins, omitted margins, and cursor positions outside the active region. Replay tests cover these cases and tabstop restoration without scrolling-region output. Palette pin-map spacing changes without behavior changes.

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

Suggested reviewers: mitchellh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: fixing VT formatter cursor restoration after margin changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-vt-formatter-cursor-replay

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/terminal/formatter.zig`:
- Around line 297-325: Move the entire cursor restore and pin-map block guarded
by self.extra.screen.cursor out of the palette-specific path and place it after
all terminal-wide state emission within if (self.opts.emit == .vt). Preserve the
scrolling-region/origin coordinate calculations and pin-map updates, while
ensuring the final CUP sequence is emitted for VT output even when palette
output is disabled and never for .plain or .html output.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 032f256d-3e4d-4376-9546-1920cbb6241b

📥 Commits

Reviewing files that changed from the base of the PR and between c0de068 and 9e49174.

📒 Files selected for processing (1)
  • src/terminal/formatter.zig

Comment thread src/terminal/formatter.zig Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e49174beb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/terminal/formatter.zig Outdated
// Scrolling regions and tabstop restoration move the cursor after
// ScreenFormatter emits it. Restore the cursor after all terminal
// state, with CUP coordinates relative to the origin margins.
if (self.extra.screen.cursor) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Move cursor restoration after terminal-wide state

This restoration is nested inside the palette block and runs before ScreenFormatter, scrolling-region, and tabstop output. When extra.palette is false—as in the added regression test—it is never emitted; when the palette is enabled, subsequent cursor-moving sequences overwrite it anyway. Consequently, formatting with cursor plus margins or tabstops still produces the wrong final cursor position, so this block needs to execute at the end of VT terminal-state emission.

Useful? React with 👍 / 👎.

@lawrencecchen
lawrencecchen force-pushed the feat-vt-formatter-cursor-replay branch from 54592d0 to 85a4fb9 Compare August 8, 2026 05:17
@lawrencecchen
lawrencecchen force-pushed the feat-vt-formatter-cursor-replay branch from 85a4fb9 to d6fdb42 Compare August 8, 2026 05:18

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6fdb42e1b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/terminal/formatter.zig Outdated
Comment on lines +427 to +429
const origin = self.terminal.modes.get(.origin);
const row = if (origin) cursor.y - region.top else cursor.y;
const col = if (origin) cursor.x - region.left else cursor.x;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use relative CUP only when emitting its origin context

When a caller enables screen.cursor without both modes and scrolling_region, a source terminal with origin mode and nonzero margins produces margin-relative coordinates here even though the output does not recreate that coordinate system. Replaying such a partial snapshot on a fresh terminal therefore places the cursor too far up and left, and this final CUP overrides the earlier absolute CUP emitted by ScreenFormatter; only convert to relative coordinates when the corresponding origin mode and margins are also emitted.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/terminal/formatter.zig (1)

421-425: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Preserve deferred wrap state.

The final CUP restores only coordinates. If the source cursor is pending wrap at the right margin, CUP clears that state. The next printable byte then overwrites the last cell instead of wrapping.

Use one shared cursor-restoration path that preserves wrap-pending behavior. Add a replay test that writes one printable byte after formatting a cursor at the right margin.

Also applies to: 447-492

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/terminal/formatter.zig` around lines 421 - 425, Update
formatCursorRestore and the final cursor-restoration flow in ScreenFormatter to
preserve a source cursor’s pending-wrap state when restoring right-margin
coordinates, rather than relying on CUP alone. Consolidate restoration through
one shared path so scrolling-region and tabstop restoration use the same
behavior. Add a replay test that formats a cursor at the right margin, writes
one printable byte, and verifies it wraps instead of overwriting the last cell.
🧹 Nitpick comments (1)
src/terminal/formatter.zig (1)

5277-5453: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add replay coverage for emitted horizontal margins.

The test at Line 5296 sets only vertical margins. The test at Line 5388 configures horizontal margins, but it omits mode emission, so replay does not enable left-right margin mode.

Add a case that emits ?69h, configures DECSLRM, enables origin mode, and verifies cursor restoration both inside and outside the horizontal margins.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/terminal/formatter.zig` around lines 5277 - 5453, Extend the
horizontal-margin replay coverage in the terminal formatter tests, using the
existing cursor restoration cases as a guide. Add a scenario that emits DECSLRM
mode (?69h), configures left/right margins, enables origin mode, and verifies
replay preserves the cursor both within and outside those horizontal margins.
Ensure formatter output includes the relevant modes and scrolling-region state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/terminal/formatter.zig`:
- Around line 421-425: Update formatCursorRestore and the final
cursor-restoration flow in ScreenFormatter to preserve a source cursor’s
pending-wrap state when restoring right-margin coordinates, rather than relying
on CUP alone. Consolidate restoration through one shared path so
scrolling-region and tabstop restoration use the same behavior. Add a replay
test that formats a cursor at the right margin, writes one printable byte, and
verifies it wraps instead of overwriting the last cell.

---

Nitpick comments:
In `@src/terminal/formatter.zig`:
- Around line 5277-5453: Extend the horizontal-margin replay coverage in the
terminal formatter tests, using the existing cursor restoration cases as a
guide. Add a scenario that emits DECSLRM mode (?69h), configures left/right
margins, enables origin mode, and verifies replay preserves the cursor both
within and outside those horizontal margins. Ensure formatter output includes
the relevant modes and scrolling-region state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d43e5679-1d74-48b7-b06f-f7641c1a65d1

📥 Commits

Reviewing files that changed from the base of the PR and between 6fd6762 and 9c80575.

📒 Files selected for processing (1)
  • src/terminal/formatter.zig

@lawrencecchen
lawrencecchen merged commit f76c132 into main Aug 10, 2026
153 checks passed
@lawrencecchen
lawrencecchen deleted the feat-vt-formatter-cursor-replay branch August 10, 2026 01:23
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.

1 participant