crt/sgr.go

160 lines
2.7 KiB
Go
Raw Normal View History

2023-05-12 07:53:45 +00:00
package crt
import (
"fmt"
"github.com/muesli/termenv"
"strings"
2023-05-23 18:53:49 +00:00
"sync"
2023-05-12 07:53:45 +00:00
)
2023-05-23 18:53:49 +00:00
var sgrMtx = &sync.Mutex{}
var sgrCache = map[string][]any{}
2023-05-12 07:53:45 +00:00
// extractSGR extracts an SGR ansi sequence from the beginning of the string.
func extractSGR(s string) (string, bool) {
if len(s) < 2 {
return "", false
}
if !strings.HasPrefix(s, termenv.CSI) {
return "", false
}
for i := 2; i < len(s); i++ {
2023-05-19 17:57:17 +00:00
if s[i] == ' ' || s[i] == termenv.CSI[0] {
return "", false
}
2023-05-12 07:53:45 +00:00
if s[i] == 'm' {
return s[:i+1], true
}
}
return "", false
}
type SGRReset struct{}
type SGRBold struct{}
type SGRUnsetBold struct{}
type SGRItalic struct{}
type SGRUnsetItalic struct{}
type SGRFgTrueColor struct {
R, G, B byte
}
type SGRBgTrueColor struct {
R, G, B byte
}
2023-05-19 17:27:55 +00:00
type SGRFgColor struct {
Id int
}
type SGRBgColor struct {
Id int
}
2023-05-12 07:53:45 +00:00
// parseSGR parses a single SGR ansi sequence and returns a struct representing the sequence.
func parseSGR(s string) ([]any, bool) {
if !strings.HasPrefix(s, termenv.CSI) {
return nil, false
}
s = s[len(termenv.CSI):]
if len(s) == 0 {
return nil, false
}
2023-05-23 18:53:49 +00:00
sgrMtx.Lock()
if cached, ok := sgrCache[s]; ok {
sgrMtx.Unlock()
return cached, true
}
sgrMtx.Unlock()
full := s
2023-05-12 07:53:45 +00:00
if !strings.HasSuffix(s, "m") {
return nil, false
}
s = s[:len(s)-1]
if len(s) == 0 {
return nil, false
}
var skips int
var res []any
for len(s) > 0 {
code := strings.SplitN(s, ";", 2)[0]
if skips > 0 {
skips--
} else {
switch code {
case "0":
res = append(res, SGRReset{})
case "1":
res = append(res, SGRBold{})
case "22":
res = append(res, SGRUnsetBold{})
case "3":
res = append(res, SGRItalic{})
case "23":
res = append(res, SGRUnsetItalic{})
default:
if strings.HasPrefix(s, "38;2;") {
var r, g, b byte
_, err := fmt.Sscanf(s, "38;2;%d;%d;%d", &r, &g, &b)
if err == nil {
skips = 4
res = append(res, SGRFgTrueColor{r, g, b})
continue
}
} else if strings.HasPrefix(s, "48;2;") {
var r, g, b byte
_, err := fmt.Sscanf(s, "48;2;%d;%d;%d", &r, &g, &b)
if err == nil {
skips = 4
res = append(res, SGRBgTrueColor{r, g, b})
continue
}
2023-05-19 17:27:55 +00:00
} else if strings.HasPrefix(s, "38;5;") {
var id int
_, err := fmt.Sscanf(s, "38;5;%d", &id)
if err == nil {
skips = 2
res = append(res, SGRFgColor{id})
continue
}
} else if strings.HasPrefix(s, "48;5;") {
var id int
_, err := fmt.Sscanf(s, "48;5;%d", &id)
if err == nil {
skips = 2
res = append(res, SGRBgColor{id})
continue
}
2023-05-12 07:53:45 +00:00
}
}
}
if len(code) >= len(s) {
break
}
s = s[len(code)+1:]
}
2023-05-23 18:53:49 +00:00
sgrMtx.Lock()
sgrCache[full] = res
sgrMtx.Unlock()
2023-05-12 07:53:45 +00:00
return res, len(res) > 0
}