516f1b1946
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
165 lines
3.3 KiB
Go
165 lines
3.3 KiB
Go
package terminalstate
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/rcarmo/go-te/pkg/te"
|
|
)
|
|
|
|
var ansi16Names = [...]string{
|
|
"black",
|
|
"red",
|
|
"green",
|
|
"yellow",
|
|
"blue",
|
|
"magenta",
|
|
"cyan",
|
|
"white",
|
|
"brightblack",
|
|
"brightred",
|
|
"brightgreen",
|
|
"brightyellow",
|
|
"brightblue",
|
|
"brightmagenta",
|
|
"brightcyan",
|
|
"brightwhite",
|
|
}
|
|
|
|
type Cell struct {
|
|
Data string `json:"data"`
|
|
FG string `json:"fg"`
|
|
BG string `json:"bg"`
|
|
Bold bool `json:"bold"`
|
|
Italics bool `json:"italics"`
|
|
Underscore bool `json:"underscore"`
|
|
Reverse bool `json:"reverse"`
|
|
}
|
|
|
|
type Snapshot struct {
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
Buffer [][]Cell `json:"buffer"`
|
|
HasChanges bool `json:"has_changes"`
|
|
}
|
|
|
|
type Tracker struct {
|
|
mu sync.Mutex
|
|
screen *te.DiffScreen
|
|
stream *te.ByteStream
|
|
changeCounter uint64
|
|
lastActivityCounter uint64
|
|
lastSnapshotCounter uint64
|
|
}
|
|
|
|
func NewTracker(width, height int) *Tracker {
|
|
screen := te.NewDiffScreen(width, height)
|
|
return &Tracker{
|
|
screen: screen,
|
|
stream: te.NewByteStream(screen, false),
|
|
}
|
|
}
|
|
|
|
func (t *Tracker) Feed(data []byte) error {
|
|
if len(data) == 0 {
|
|
return nil
|
|
}
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
if err := t.stream.Feed(data); err != nil {
|
|
return err
|
|
}
|
|
if len(t.screen.Dirty) > 0 {
|
|
t.changeCounter++
|
|
// Clear dirty set so subsequent feeds detect new changes
|
|
for k := range t.screen.Dirty {
|
|
delete(t.screen.Dirty, k)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Tracker) Resize(width, height int) {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
if width == t.screen.Columns && height == t.screen.Lines {
|
|
return
|
|
}
|
|
t.screen.Resize(height, width)
|
|
t.changeCounter++
|
|
}
|
|
|
|
func (t *Tracker) Snapshot() Snapshot {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
snapshot := Snapshot{
|
|
Width: t.screen.Columns,
|
|
Height: t.screen.Lines,
|
|
HasChanges: t.changeCounter > t.lastSnapshotCounter,
|
|
Buffer: make([][]Cell, t.screen.Lines),
|
|
}
|
|
t.lastSnapshotCounter = t.changeCounter
|
|
|
|
for row := 0; row < t.screen.Lines; row++ {
|
|
line := make([]Cell, t.screen.Columns)
|
|
for col := 0; col < t.screen.Columns; col++ {
|
|
raw := t.screen.Buffer[row][col]
|
|
data := raw.Data
|
|
if data == "" {
|
|
data = " "
|
|
}
|
|
line[col] = Cell{
|
|
Data: data,
|
|
FG: colorToString(raw.Attr.Fg),
|
|
BG: colorToString(raw.Attr.Bg),
|
|
Bold: raw.Attr.Bold,
|
|
Italics: raw.Attr.Italics,
|
|
Underscore: raw.Attr.Underline,
|
|
Reverse: raw.Attr.Reverse,
|
|
}
|
|
}
|
|
snapshot.Buffer[row] = line
|
|
}
|
|
return snapshot
|
|
}
|
|
|
|
func (t *Tracker) ConsumeActivityChanged() bool {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
if t.changeCounter > t.lastActivityCounter {
|
|
t.lastActivityCounter = t.changeCounter
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func colorToString(color te.Color) string {
|
|
if color.Name != "" {
|
|
name := strings.ToLower(strings.TrimPrefix(color.Name, "#"))
|
|
if len(name) == 6 {
|
|
if _, err := strconv.ParseUint(name, 16, 32); err == nil {
|
|
return name
|
|
}
|
|
}
|
|
return name
|
|
}
|
|
switch color.Mode {
|
|
case te.ColorDefault:
|
|
return "default"
|
|
case te.ColorANSI16:
|
|
if int(color.Index) < len(ansi16Names) {
|
|
return ansi16Names[color.Index]
|
|
}
|
|
return "default"
|
|
case te.ColorANSI256:
|
|
return fmt.Sprintf("%d", color.Index)
|
|
case te.ColorTrueColor:
|
|
return "default"
|
|
default:
|
|
return "default"
|
|
}
|
|
}
|