Make CSI 3 J discard the scrollback instead of erasing the screen - #15
Merged
Conversation
Mode 3 shared mode 2's body:
case 2: // Erase all
case 3: // Erase scrollback (extension)
for (int i = 0; i < _terminal.Rows; i++)
_buffer.Lines[_buffer.YBase + i]?.Fill(emptyCell);
That erases the VISIBLE screen and never touches the scrollback, which is the
opposite of what mode 3 asks for. The comment said "erase scrollback" and the
code did not. They are complements rather than variations: a caller wanting
both sends 2 and 3.
Reported downstream as Iciclecreek.Avalonia.Terminal#28 — `cls` in cmd.exe
left the history scrollable with the mouse wheel, unlike conhost and Windows
Terminal. Confirmed by capturing what ConPTY actually emits for `cls`:
<ESC>[?25l <ESC>[H ...25x <ESC>[K<CR><LF>... <ESC>[3J <ESC>[2;1H
It never sends CSI 2 J. It clears the screen itself, line by line, and then
sends CSI 3 J for the history — so with mode 3 doing nothing, the history
survived. That also explains the reporter's odd follow-up, that `cls` DID
clear a fresh terminal but not one that had run `dir` first: with no
scrollback yet, clearing the screen looks like clearing everything.
Discarding rather than blanking is the point. Blanked lines are still there
and still scrollable, so the history would remain reachable even after being
thrown away.
TerminalBuffer.ClearScrollback trims the lines above the screen and moves
_yBase and _yDisp with them. That second part is load-bearing: they are
absolute indices into the line list, so trimming without adjusting them leaves
the visible screen pointing at an offset that no longer exists and the next
write walks off the end with an IndexOutOfRangeException.
Four tests. Three fail against the previous behaviour; the fourth
(LeavesTheBufferWritable) passes either way and is deliberate — it guards the
new implementation's own risk, the stale-index crash, rather than reproducing
the original defect.
607 tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132tgWvNwhLt1HprKFSTrsP
Merged
pull Bot
referenced
this pull request
in IvanJosipovic/XTerm.NET
Aug 22, 2026
1.0.15 is already published and predates the CSI 3 J fix in #15, so the fix needs its own release to reach consumers. That fix makes CSI 3 J discard the scrollback instead of erasing the visible screen, which is what cmd.exe's `cls` relies on under ConPTY. Verified end to end against a real shell: after `cls` the buffer goes from 1025 lines with BaseY 1000 down to 25 lines with BaseY 0, where before the fix both were unchanged. Downstream, Iciclecreek.Avalonia.Terminal#28 is waiting on this — it pins 1.0.14, so it will move two versions when it picks this up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0132tgWvNwhLt1HprKFSTrsP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported downstream as Iciclecreek.Avalonia.Terminal#28:
clsincmd.exeleaves the history scrollable with the mouse wheel, unlike conhost and Windows Terminal.The bug
Mode 3 shared mode 2's body:
That erases the visible screen and never touches the scrollback — the opposite of what mode 3 asks for. The comment said "erase scrollback"; the code did not. The two modes are complements rather than variations, so a caller wanting both sends 2 and 3.
Why that surfaced as a
clsbugCaptured from ConPTY, this is what
clsactually emits:It never sends
CSI 2 J. It clears the screen itself, line by line withESC[K, and then sendsCSI 3 Jfor the history. With mode 3 doing nothing, the history survived.That also explains the reporter's odd follow-up — that
clsdid clear a fresh terminal but not one that had rundirfirst. With no scrollback yet, clearing the screen looks exactly like clearing everything.The fix
TerminalBuffer.ClearScrollback()trims the lines above the visible screen and moves_yBaseand_yDispwith them.Discarding rather than blanking is the point. Blanked lines are still present and still scrollable, so the history would remain reachable even after the terminal had been told to throw it away.
Moving the indices is load-bearing.
_yBaseand_yDispare absolute indices into the line list, so trimming without adjusting them leaves the visible screen pointing at an offset that no longer exists — the next write walks off the end:I hit that while probing whether a downstream workaround was possible, which is also how I concluded the fix belonged here rather than in the consumer.
Tests
Four, alongside the existing
EraseInDisplaycases. Three fail against the previous behaviour; the fourth,LeavesTheBufferWritable, passes either way and is deliberate — it guards this implementation's own risk, the stale-index crash, rather than reproducing the original defect.607 tests green.
Version left at 1.0.15 — bumping looks like something you do deliberately at release time.
🤖 Generated with Claude Code