Skip to content

Commit

Permalink
feat: handle line and screen cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
caarlos0 committed Nov 8, 2024
1 parent be08e9d commit 2db7587
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 45 deletions.
174 changes: 129 additions & 45 deletions vt/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
"github.com/charmbracelet/x/cellbuf"
)

var spaceCell = cellbuf.Cell{
Content: " ",
Width: 1,
}

// handleCsi handles a CSI escape sequences.
func (t *Terminal) handleCsi(seq []byte) {
// params := t.parser.Params[:t.parser.ParamsLen]
Expand All @@ -15,13 +20,135 @@ func (t *Terminal) handleCsi(seq []byte) {
case 'A', 'B', 'C', 'D', 'E', 'F', 'H':
t.handleCursor()
case 'm': // SGR - Select Graphic Rendition
handleSgr(t.parser, &t.scr.cur.Pen)
t.handleSgr()
case 'J':
t.handleScreen()
case 'K', 'L', 'M', 'S', 'T':
t.handleLine()
}
}

func (t *Terminal) handleScreen() {
var count int
if t.parser.ParamsLen > 0 {
count = ansi.Param(t.parser.Params[0]).Param()
}

scr := t.scr
cur := scr.Cursor()
w, h := scr.Width(), scr.Height()
x, y := cur.Pos.X, cur.Pos.Y

cmd := ansi.Cmd(t.parser.Cmd)
switch cmd.Command() {
case 'J':
switch count {
case 0: // Erase screen below (including cursor)
for i := y; i < h; i++ {
for j := 0; j < w; j++ {
if i == y && j < x {
continue
}
t.scr.SetCell(j, i, spaceCell)
}
}
case 1: // Erase screen above (including cursor)
for i := 0; i <= y; i++ {
for j := 0; j < w; j++ {
if i == y && j > x {
break
}
t.scr.SetCell(j, i, spaceCell)
}
}
case 2: // erase screen
t.scr = NewScreen(w, h)
case 3: // erase display
t.scr = NewScreen(w, h)
}
}
}

func (t *Terminal) handleLine() {
var count int
if t.parser.ParamsLen > 0 {
count = ansi.Param(t.parser.Params[0]).Param()
}

cmd := ansi.Cmd(t.parser.Cmd)
switch cmd.Command() {
case 'K':
cur := t.scr.Cursor()
x, y := cur.Pos.X, cur.Pos.Y
w := t.scr.Width()
switch count {
case 0: // Erase from cursor to end of line
for i := x; i < w; i++ {
t.scr.SetCell(i, y, cellbuf.Cell{})
}
case 1: // Erase from start of line to cursor
for i := 0; i <= x; i++ {
t.scr.SetCell(i, y, cellbuf.Cell{})
}
case 2: // Erase entire line
for i := 0; i < w; i++ {
t.scr.SetCell(i, y, cellbuf.Cell{})
}
}
case 'L': // TODO: insert n blank lines
case 'M': // TODO: delete n lines
case 'S': // TODO: scroll up n lines
case 'T': // TODO: scroll down n lines
}
}

func (t *Terminal) handleCursor() {
p := t.parser
width, height := t.scr.Width(), t.scr.Height()
cmd := ansi.Cmd(p.Cmd)
n := 1
if p.ParamsLen > 0 {
n = int(p.Params[0])
}

x, y := t.scr.cur.Pos.X, t.scr.cur.Pos.Y
switch cmd.Command() {
case 'A':
// CUU - Cursor Up
y = max(0, y-n)
case 'B':
// CUD - Cursor Down
y = min(height-1, y+n)
case 'C':
// CUF - Cursor Forward
x = min(width-1, x+n)
case 'D':
// CUB - Cursor Back
x = max(0, x-n)
case 'E':
// Cursor next line
y = min(height-1, y+n)
x = 0
case 'F':
// Cursor previous line
y = max(0, y-n)
x = 0
case 'H':
// Set cursor position
if p.ParamsLen >= 2 {
y = min(height-1, max(0, int(p.Params[0])-1))
x = min(width-1, max(0, int(p.Params[1])-1))
} else {
x, y = 0, 0
}
}
t.scr.moveCursor(x, y)
}

// handleSgr handles SGR escape sequences.
// handleSgr handles Select Graphic Rendition (SGR) escape sequences.
func handleSgr(p *ansi.Parser, pen *Style) {
func (t *Terminal) handleSgr() {
p, pen := t.parser, t.scr.cur.Pen
if p.ParamsLen == 0 {
pen.Reset()
return
Expand Down Expand Up @@ -144,49 +271,6 @@ func readColor(idxp *int, params []int) (c ansi.Color) {
return
}

func (t *Terminal) handleCursor() {
p := t.parser
width, height := t.scr.Width(), t.scr.Height()
cmd := ansi.Cmd(p.Cmd)
n := 1
if p.ParamsLen > 0 {
n = int(p.Params[0])
}

x, y := t.scr.cur.Pos.X, t.scr.cur.Pos.Y
switch cmd.Command() {
case 'A':
// CUU - Cursor Up
y = max(0, y-n)
case 'B':
// CUD - Cursor Down
y = min(height-1, y+n)
case 'C':
// CUF - Cursor Forward
x = min(width-1, x+n)
case 'D':
// CUB - Cursor Back
x = max(0, x-n)
case 'E':
// Cursor next line
y = min(height-1, y+n)
x = 0
case 'F':
// Cursor previous line
y = max(0, y-n)
x = 0
case 'H':
// Set cursor position
if p.ParamsLen >= 2 {
y = min(height-1, max(0, int(p.Params[0])-1))
x = min(width-1, max(0, int(p.Params[1])-1))
} else {
x, y = 0, 0
}
}
t.scr.moveCursor(x, y)
}

func max(a, b int) int {
if a > b {
return a
Expand Down
3 changes: 3 additions & 0 deletions vt/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ func (t *Terminal) Write(p []byte) (n int, err error) {
state = newState
p = p[m:]
n += m

// x, y := t.Cursor().Pos.X, t.Cursor().Pos.Y
// fmt.Printf("%q: %d %d\n", seq, x, y)
}

return
Expand Down

0 comments on commit 2db7587

Please sign in to comment.