Skip to content

Add resize reflow for the normal buffer#12

Open
buchmiet wants to merge 1 commit into
tomlm:mainfrom
buchmiet:feature/reflow
Open

Add resize reflow for the normal buffer#12
buchmiet wants to merge 1 commit into
tomlm:mainfrom
buchmiet:feature/reflow

Conversation

@buchmiet

Copy link
Copy Markdown

Resize reflow for the normal buffer

Problem

XTerm.NET truncated every line independently on column shrink. Growing the window back did not restore wrapped content — long lines stayed cut at the narrowest width reached. Scrollback lines were lost silently when the buffer was at capacity and row count shrank.

Semantics

Ported from xterm.js 5.5.0 (MIT):

  • Shrink cols: logical lines longer than the new width re-wrap onto additional IsWrapped rows; no content is lost.
  • Grow cols: wrapped row groups merge upward; empty rows are removed.
  • Normal buffer only (hasScrollback: true). The alternate buffer uses explicit hasScrollback: false and keeps truncation behaviour.
  • Column resize only triggers reflow. Row resize keeps existing push/clamp semantics.
  • Wrapped groups containing a non-Normal LineAttribute (DECDWL/DECDHL) are excluded from reflow.
  • Trimmed(n) is raised when reflow removes rows from the top (selection anchoring).
  • SavedCursorState.Y is adjusted on trim, matching xterm.js savedY handling.

The cursor line is deliberately not reflowed

Wrapped groups containing the cursor row are skipped in both directions, matching xterm.js — the running application is expected to redraw its current line on SIGWINCH.

This is worth knowing before testing by hand, because it is the first thing a reviewer is likely to try. Writing a long line and resizing immediately, with no application attached to redraw, leaves that line truncated while every other line round-trips intact. Moving the cursor off the line first (a trailing newline) shows the reflow working as intended: a 69-character line at 60 columns survives a 60 → 30 → 60 round trip byte for byte.

Provenance

Faithful port of:

  • src/common/buffer/BufferReflow.ts
  • Reflow orchestration in src/common/buffer/Buffer.ts (resize, _reflow, _reflowLarger, _reflowSmaller, _reflowLargerAdjustViewport)
  • Tests from BufferReflow.test.ts and reflow describe blocks in Buffer.test.ts

Copyright (c) The xterm.js authors, MIT License.

Latent bug fix (independent)

When Length == MaxLength and rows shrink, the old code called CircularList.Resize first, which kept the oldest entries and discarded the live screen bottom (including the cursor line). Resize now trims from the top (TrimStart + Trimmed) before shrinking capacity.

Test coverage

  • BufferReflowTests.cs — pure-function port (reflowSmallerGetNewLineLengths, wide chars, null tail cells)
  • BufferTests.cs — integration reflow cases (wrap/unwrap, wide chars in both directions, cursor, Trimmed, LineAttribute skip, alt buffer, capacity-shrink regression, wrapped group ending at the last buffer row)
  • All 624 tests pass (589 pre-existing plus the reflow suite).

ConPTY note

xterm.js disables reflow under windowsMode / windowsPty because ConPTY re-wraps the viewport. This port is unconditional — scrollback truncation is the visible defect either way, and non-ConPTY streams (SSH) benefit most. A windowsPty-style toggle may be worth a follow-up; not included here.

Files changed

  • src/XTerm.NET/Buffer/BufferReflow.cs (new)
  • src/XTerm.NET/Buffer/TerminalBuffer.csResize restructure, reflow orchestration, hasScrollback constructor flag
  • src/XTerm.NET/Buffer/BufferLine.csGetWidth, HasContent, ReplaceCells; GetTrimmedLength wide-char fix
  • src/XTerm.NET/Buffer/CircularList.csSetLength for reflow batching
  • src/XTerm.NET/Terminal.cs — alt buffer hasScrollback: false
  • src/XTerm.NET.Tests/Buffer/BufferReflowTests.cs (new)
  • src/XTerm.NET.Tests/Buffer/BufferTests.cs — reflow integration tests
  • FIXES.md — entry for this change

@buchmiet
buchmiet force-pushed the feature/reflow branch 2 times, most recently from 4c889d5 to 6fc6366 Compare July 23, 2026 23:15
Ports xterm.js 5.5.0 resize reflow (BufferReflow.ts and the reflow
orchestration in Buffer.ts) into XTerm.NET.

Shrinking the column count now re-wraps long logical lines onto
additional IsWrapped rows instead of truncating them; growing merges
wrapped groups back upward. The alternate buffer is excluded via an
explicit hasScrollback: false flag, matching xterm.js, so full-screen
applications keep repainting themselves.

Also fixes an independent latent bug: when the buffer was at capacity
and the row count shrank, CircularList.Resize ran before any trimming
and kept the oldest lines, silently discarding the bottom of the live
screen including the cursor row. Resize now trims from the top
(raising Trimmed) before shrinking capacity.

Ported from xterm.js, (c) The xterm.js authors, MIT.
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