Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/XTerm.NET.Tests/CursorAndMarginTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,71 @@ public void Hpa_and_vpr_move_the_cursor()
terminal.Write($"{Esc}[2e"); // VPR down 2
Assert.Equal(2, terminal.Buffer.Y);
}

// ------------------------------------------------------------------ pending wrap is a FACT

// SetCursorRaw used to set PendingWrap on EVERY print advance, under a contract that the flag
// was "harmlessly stale" inside the margins because only the boundary column read it. The
// moment CUB and the ICH/DCH/ECH settle step started reading it anywhere, every backward move
// or edit issued right after a print acted one column LEFT of the cursor. On screen that was
// asciiquarium leaving duplicated fragments behind left-moving sprites and eating characters
// from right-moving ones -- rate-dependent only because it needed a print immediately followed
// by a CUB or DCH in the same stream.

[Fact]
public void Delete_right_after_printing_deletes_at_the_cursor_not_one_left()
{
// The 16-byte repro the bug was cornered with: print AB, DCH 1. The cursor sits on the
// cell after B, so the deletion must not touch B.
var terminal = NewTerminal();
terminal.Write($"{Esc}[5;1HAB{Esc}[1Ptail");

Assert.Equal("ABtail", Row(terminal, 4, 6));
}

[Fact]
public void Cursor_back_right_after_printing_counts_from_the_cursor_not_one_left()
{
// Print ABCD, CUB 2 -> the cursor is on C; DCH must eat C, not B.
var terminal = NewTerminal();
terminal.Write($"{Esc}[5;1HABCD{Esc}[2D{Esc}[1P");

Assert.Equal("ABD ", Row(terminal, 4, 4));
}

[Fact]
public void A_wrap_left_pending_on_another_line_does_not_shift_edits_after_a_move()
{
// Fill a line to the last column (a REAL pending wrap), address another line, print, edit.
// The old flag survived the move and the settle step consumed it a screen away.
var terminal = NewTerminal();
terminal.Write($"{Esc}[1;14H{new string('X', 7)}"); // fills row 1 to column 20
terminal.Write($"{Esc}[5;1HAB{Esc}[1Ptail");

Assert.Equal("ABtail", Row(terminal, 4, 6));
}

[Fact]
public void Printing_the_last_column_still_wraps_the_next_character()
{
// The guard for the fix itself: the flag must still be TRUE at the phantom column, or
// autowrap dies. Fill the row exactly; the next character belongs at the start of row 2.
var terminal = NewTerminal();
terminal.Write($"{Esc}[1;1H{new string('X', 20)}Y");

Assert.Equal("Y", terminal.Buffer.Lines[1]![0].Content);
}

[Fact]
public void Insert_at_the_phantom_column_still_acts_on_the_last_column()
{
// What SettleForEditing exists for -- an editor that filled a line and inserted must see
// the last column affected, not nothing. The fix must not regress it.
var terminal = NewTerminal();
terminal.Write($"{Esc}[1;1H{new string('X', 20)}"); // pending wrap at the boundary
terminal.Write($"{Esc}[1@");

Assert.Equal(" ", string.IsNullOrEmpty(terminal.Buffer.Lines[0]![19].Content) ? " " : terminal.Buffer.Lines[0]![19].Content);
Assert.Equal("X", terminal.Buffer.Lines[0]![18].Content);
}
}
6 changes: 5 additions & 1 deletion src/XTerm.NET/Buffer/TerminalBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,11 @@ public void SetCursorRaw(int x, int y)
{
_x = x;
_y = y;
PendingWrap = true;
// TRUE only at the phantom column. Setting it on every raw advance relied on "only the
// boundary column reads it" -- and the moment CUB and SettleForEditing started reading
// it away from the boundary, every backward move or edit right after a print acted one
// column left of the cursor. The flag now states a fact instead of trusting its readers.
PendingWrap = x > _scrollRight;
}

public string PrintViewport()
Expand Down
Loading