Skip to content

Commit 572f9ae

Browse files
fix: align Terminal.Clear() with upstream xterm.js behavior
Clear() now copies the cursor line to position 0, resets buffer length, and sets Y to 0 — matching upstream src/headless/Terminal.ts clear(). Previously, Clear() left the cursor row unchanged and only trimmed scrollback, resulting in stale lines remaining in the viewport. Fixes #34 Co-authored-by: Ona <no-reply@ona.com>
1 parent 172c883 commit 572f9ae

2 files changed

Lines changed: 57 additions & 18 deletions

File tree

terminal.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -373,23 +373,19 @@ func (t *Terminal) Clear() {
373373
return
374374
}
375375

376-
// Clear rows above the cursor.
377-
for i := buf.YBase + buf.Y - 1; i >= buf.YBase; i-- {
378-
line := buf.Lines.Get(i)
379-
if line != nil && line.GetTrimmedLength() != 0 {
380-
break
381-
}
382-
if i > buf.YBase+buf.Y-1 {
383-
continue
384-
}
385-
buf.Lines.Set(i, buf.GetBlankLine(nil, false))
386-
}
376+
// Copy the current cursor line to position 0.
377+
buf.Lines.Set(0, buf.Lines.Get(buf.YBase+buf.Y))
378+
buf.Lines.SetLength(1)
387379

388-
// Trim scrollback.
389-
buf.Lines.TrimStart(buf.YBase)
390-
buf.YBase = 0
380+
// Reset scroll and cursor positions.
391381
buf.YDisp = 0
392-
t.bufferService.IsUserScrolling = false
382+
buf.YBase = 0
383+
buf.Y = 0
384+
385+
// Fill remaining viewport rows with blank lines.
386+
for i := 1; i < t.bufferService.Rows; i++ {
387+
buf.Lines.Push(buf.GetBlankLine(nil, false))
388+
}
393389

394390
t.OnScrollEmitter.Fire(buf.YDisp)
395391
}

terminal_test.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -982,9 +982,9 @@ func TestTerminalClear(t *testing.T) {
982982
t.Parallel()
983983

984984
type Expectation struct {
985+
Y int
985986
YBase int
986987
YDisp int
987-
IsUserScrolling bool
988988
}
989989

990990
term := newTestTerminal(80, 5)
@@ -996,20 +996,63 @@ func TestTerminalClear(t *testing.T) {
996996
term.Clear()
997997

998998
got := Expectation{
999+
Y: term.Buffer().Y,
9991000
YBase: term.Buffer().YBase,
10001001
YDisp: term.Buffer().YDisp,
1001-
IsUserScrolling: term.bufferService.IsUserScrolling,
10021002
}
10031003
expected := Expectation{
1004+
Y: 0,
10041005
YBase: 0,
10051006
YDisp: 0,
1006-
IsUserScrolling: false,
10071007
}
10081008
if diff := cmp.Diff(expected, got); diff != "" {
10091009
t.Errorf("(-want +got):\n%s", diff)
10101010
}
10111011
}
10121012

1013+
func TestTerminalClearMovesCurrentLineToTop(t *testing.T) {
1014+
t.Parallel()
1015+
1016+
term := New(WithCols(10), WithRows(5), WithScrollback(100))
1017+
for i := 0; i < 10; i++ {
1018+
term.WriteString(fmt.Sprintf("line%d\r\n", i))
1019+
}
1020+
term.WriteString("current")
1021+
1022+
term.Clear()
1023+
1024+
buf := term.Buffer()
1025+
if buf.Y != 0 {
1026+
t.Errorf("Y = %d, want 0", buf.Y)
1027+
}
1028+
1029+
line0 := buf.Lines.Get(0)
1030+
if line0 == nil {
1031+
t.Fatal("line 0 is nil")
1032+
}
1033+
text := line0.TranslateToString(true, 0, -1)
1034+
if text != "current" {
1035+
t.Errorf("line 0 text = %q, want %q", text, "current")
1036+
}
1037+
1038+
// Remaining viewport rows should be blank.
1039+
for i := 1; i < 5; i++ {
1040+
line := buf.Lines.Get(i)
1041+
if line == nil {
1042+
t.Fatalf("line %d is nil", i)
1043+
}
1044+
trimmed := line.GetTrimmedLength()
1045+
if trimmed != 0 {
1046+
t.Errorf("line %d trimmed length = %d, want 0", i, trimmed)
1047+
}
1048+
}
1049+
1050+
// Buffer length should equal the number of rows (no scrollback).
1051+
if buf.Lines.Length() != 5 {
1052+
t.Errorf("buffer length = %d, want 5", buf.Lines.Length())
1053+
}
1054+
}
1055+
10131056
func TestTerminalClearEmptyTerminal(t *testing.T) {
10141057
t.Parallel()
10151058

0 commit comments

Comments
 (0)