From af001ce1bc5c0080ea9c22a011a1496e8e24888b Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Thu, 6 Feb 2025 15:55:15 -0500 Subject: [PATCH] feat(cellbuf): append runes to cell without changing width and accept escape sequences --- cellbuf/cell.go | 13 +++++++++++++ cellbuf/screen.go | 39 +++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/cellbuf/cell.go b/cellbuf/cell.go index 368f63db..a8a57654 100644 --- a/cellbuf/cell.go +++ b/cellbuf/cell.go @@ -34,6 +34,19 @@ type Cell struct { Rune rune } +// Append appends runes to the cell without changing the width. This is useful +// when we want to use the cell to store escape sequences or other runes that +// don't affect the width of the cell. +func (c *Cell) Append(r ...rune) { + for i, r := range r { + if i == 0 && c.Rune == 0 { + c.Rune = r + continue + } + c.Comb = append(c.Comb, r) + } +} + // String returns the string content of the cell excluding any styles, links, // and escape sequences. func (c Cell) String() string { diff --git a/cellbuf/screen.go b/cellbuf/screen.go index 19eb3c10..647ea943 100644 --- a/cellbuf/screen.go +++ b/cellbuf/screen.go @@ -1520,21 +1520,15 @@ func (s *Screen) printString(x, y int, bounds Rectangle, str string, truncate bo } } + var cell Cell var state byte for len(str) > 0 { seq, width, n, newState := s.method.DecodeSequenceInString(str, state, p) - var cell *Cell switch width { case 1, 2, 3, 4: // wide cells can go up to 4 cells wide - cell = &Cell{Width: width} - for i, r := range seq { - if i == 0 { - cell.Rune = r - } else { - cell.Comb = append(cell.Comb, r) - } - } + cell.Width += width + cell.Append([]rune(seq)...) if !truncate && x+cell.Width > bounds.Max.X { // Wrap the string to the width of the window @@ -1553,7 +1547,8 @@ func (s *Screen) printString(x, y int, bounds Rectangle, str string, truncate bo // Print the cell to the screen cell.Style = s.cur.Style cell.Link = s.cur.Link - s.SetCell(x, y, cell) //nolint:errcheck + s.SetCell(x, y, &cell) //nolint:errcheck + cell.Reset() x += width } } // String is too long for the line, truncate it. @@ -1561,20 +1556,18 @@ func (s *Screen) printString(x, y int, bounds Rectangle, str string, truncate bo // Valid sequences always have a non-zero Cmd. // TODO: Handle cursor movement and other sequences switch { - case ansi.HasCsiPrefix(seq) && p.Command() != 0: - switch p.Command() { - case 'm': // SGR - Select Graphic Rendition - ReadStyle(p.Params(), &s.cur.Style) - } - case ansi.HasOscPrefix(seq) && p.Command() != 0: - switch p.Command() { - case 8: // Hyperlinks - ReadLink(p.Data(), &s.cur.Link) - } + case ansi.HasCsiPrefix(seq) && p.Command() == 'm': + // SGR - Select Graphic Rendition + ReadStyle(p.Params(), &s.cur.Style) + case ansi.HasOscPrefix(seq) && p.Command() == 8: + // Hyperlinks + ReadLink(p.Data(), &s.cur.Link) case ansi.Equal(seq, "\n"): y++ case ansi.Equal(seq, "\r"): x = bounds.Min.X + default: + cell.Append([]rune(seq)...) } } @@ -1582,4 +1575,10 @@ func (s *Screen) printString(x, y int, bounds Rectangle, str string, truncate bo state = newState str = str[n:] } + + // Make sure to set the last cell if it's not empty. + if !cell.Empty() { + s.SetCell(x, y, &cell) //nolint:errcheck + cell.Reset() + } }