Skip to content

Commit

Permalink
Enable limited scrollback support
Browse files Browse the repository at this point in the history
  • Loading branch information
mgazza committed Oct 8, 2024
1 parent 23350e9 commit 611b620
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
32 changes: 32 additions & 0 deletions escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var escapes = map[rune]func(*Terminal, string){
'P': escapeDeleteChars,
'r': escapeSetScrollArea,
's': escapeSaveCursor,
'S': escapeScrollUp,
'u': escapeRestoreCursor,
'i': escapePrinterMode,
}
Expand Down Expand Up @@ -363,6 +364,37 @@ func escapeSetScrollArea(t *Terminal, msg string) {
t.scrollBottom = end
}

func escapeScrollUp(t *Terminal, msg string) {
lines, _ := strconv.Atoi(msg)
if lines == 0 {
lines = 1
}

// Ensure we are within the scrollable area
if t.cursorRow < t.scrollTop || t.cursorRow > t.scrollBottom {
return
}

// Calculate new cursor position after scrolling
newCursorRow := t.cursorRow - lines

// Make sure we don't scroll above the scroll top
if newCursorRow < t.scrollTop {
newCursorRow = t.scrollTop
}

// Move cursor to the new position
t.moveCursor(newCursorRow, t.cursorCol)

// Perform the actual scrolling action
for i := t.scrollTop; i <= t.scrollBottom-lines; i++ {
t.content.SetRow(i, t.content.Row(i+lines))
}
for i := t.scrollBottom - lines + 1; i <= t.scrollBottom; i++ {
t.content.SetRow(i, widget.TextGridRow{}) // Clear the last lines
}
}

func trimLeftZeros(s string) string {
if s == "" {
return s
Expand Down
43 changes: 43 additions & 0 deletions escape_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package terminal

import (
"strconv"
"testing"

"fyne.io/fyne/v2"
Expand All @@ -18,6 +19,48 @@ func TestClearScreen(t *testing.T) {
assert.Equal(t, "", term.content.Text())
}

// test clearing the screen by using "scrollback"
// this is a method tmux uses to "clear the screen"
func TestScrollBack(t *testing.T) {
// Step 1: Setup a new terminal instance
term := New()
term.debug = true
term.config.Columns = 80 // 80 columns (standard terminal width)
term.config.Rows = 5 // Doesn't matter

// Step 2: Populate the entire screen with lines using cursor movement
for i := 1; i <= 40; i++ {
lineText := "Line " + strconv.Itoa(i)
// Move the cursor to the beginning of each line using the escape sequence \x1b[{row};{col}H
escapeMoveCursor := "\x1b[" + strconv.Itoa(i) + ";1H"
term.handleOutput([]byte(escapeMoveCursor + lineText))
}

// Step 3: Set up the scroll region and scroll content away
term.handleOutput([]byte("\x1b[1;47r")) // Set scroll region from lines 1 to 47
term.handleOutput([]byte("\x1b[2;47r")) // Set scroll region again (redundant in most cases)
term.handleOutput([]byte("\x1b[46S")) // Scroll up by 46 lines (this should move almost all content out of view)

// Step 4: Additional escape sequences to clear the screen
term.handleOutput([]byte("\x1b[1;1H")) // Move cursor to the top-left corner
term.handleOutput([]byte("\x1b[K")) // Clear the current line
term.handleOutput([]byte("\x1b[1;48r")) // Restore scroll region to the full screen
term.handleOutput([]byte("\x1b[1;1H")) // Move cursor to top-left again
term.handleOutput([]byte("\x1b(B")) // Reset character set
term.handleOutput([]byte("\x1b[m")) // Reset all attributes

// Step 5: Check the final content of the terminal
expectedContent := "" // After scrolling and clearing, the visible area should be empty
for i := 0; i < 46; i++ {
expectedContent += "\n" // Each row should be an empty line
}

assert.Equal(t, 0, term.cursorRow)
assert.Equal(t, 0, term.cursorCol)

assert.Equal(t, expectedContent, term.content.Text())
}

func TestInsertDeleteChars(t *testing.T) {
term := New()
term.config.Columns = 5
Expand Down

0 comments on commit 611b620

Please sign in to comment.