1
1
mirror of https://github.com/wader/fq.git synced 2024-12-25 14:23:18 +03:00
fq/internal/ansi/ansi.go
Mattias Wadman 913f5780f4 columnwriter,dump: Add Column interface and refactor into BarColumn and MultiLineColumn
This removes bar column drawing responsility from already complicated dump code.

Start of dump code refactor that will enable configurable columns and proper column truncate/wrap.
2022-08-24 21:48:56 +02:00

253 lines
5.7 KiB
Go

package ansi
import (
"fmt"
"io"
"strconv"
"strings"
)
type Code struct {
Set []int
Reset []int
SetString string
ResetString string
}
func MakeCode(set []int, reset []int) Code {
setSB := &strings.Builder{}
setSB.WriteString("\x1b[")
for i, s := range set {
setSB.WriteString(strconv.Itoa(s))
if i != len(set)-1 {
setSB.WriteString(";")
}
}
setSB.WriteString("m")
resetSB := &strings.Builder{}
resetSB.WriteString("\x1b[")
for i, s := range reset {
resetSB.WriteString(strconv.Itoa(s))
if i != len(reset)-1 {
resetSB.WriteString(";")
}
}
resetSB.WriteString("m")
return Code{
Set: set,
Reset: reset,
SetString: setSB.String(),
ResetString: resetSB.String(),
}
}
func (c Code) Add(a Code) Code {
return MakeCode(
append(c.Set, a.Set...),
append(c.Reset, a.Reset...),
)
}
var Reset = MakeCode([]int{}, []int{})
var Black = MakeCode([]int{30}, []int{39})
var Red = MakeCode([]int{31}, []int{39})
var Green = MakeCode([]int{32}, []int{39})
var Yellow = MakeCode([]int{33}, []int{39})
var Blue = MakeCode([]int{34}, []int{39})
var Magenta = MakeCode([]int{35}, []int{39})
var Cyan = MakeCode([]int{36}, []int{39})
var White = MakeCode([]int{37}, []int{39})
var BrightBlack = MakeCode([]int{90}, []int{39})
var BrightRed = MakeCode([]int{91}, []int{39})
var BrightGreen = MakeCode([]int{92}, []int{39})
var BrightYellow = MakeCode([]int{93}, []int{39})
var BrightBlue = MakeCode([]int{94}, []int{39})
var BrightMagenta = MakeCode([]int{95}, []int{39})
var BrightCyan = MakeCode([]int{96}, []int{39})
var BrightWhite = MakeCode([]int{97}, []int{39})
var Bgblack = MakeCode([]int{40}, []int{49})
var Bgred = MakeCode([]int{41}, []int{49})
var Bggreen = MakeCode([]int{42}, []int{49})
var Bgyellow = MakeCode([]int{43}, []int{49})
var Bgblue = MakeCode([]int{44}, []int{49})
var Bgmagenta = MakeCode([]int{45}, []int{49})
var Bgcyan = MakeCode([]int{46}, []int{49})
var Bgwhite = MakeCode([]int{47}, []int{49})
var BgbrightBlack = MakeCode([]int{100}, []int{49})
var BgbrightRed = MakeCode([]int{101}, []int{49})
var BgbrightGreen = MakeCode([]int{102}, []int{49})
var BgbrightYellow = MakeCode([]int{103}, []int{49})
var BgbrightBlue = MakeCode([]int{104}, []int{49})
var BgbrightMagenta = MakeCode([]int{105}, []int{49})
var BgbrightCyan = MakeCode([]int{106}, []int{49})
var BgbrightWhite = MakeCode([]int{107}, []int{49})
var Bold = MakeCode([]int{1}, []int{22})
var Italic = MakeCode([]int{3}, []int{23})
var Underline = MakeCode([]int{4}, []int{24})
var Inverse = MakeCode([]int{7}, []int{27})
var StringToCode = map[string]Code{
"black": Black,
"red": Red,
"green": Green,
"yellow": Yellow,
"blue": Blue,
"magenta": Magenta,
"cyan": Cyan,
"white": White,
"brightblack": BrightBlack,
"brightred": BrightRed,
"brightgreen": BrightGreen,
"brightyellow": BrightYellow,
"brightblue": BrightBlue,
"brightmagenta": BrightMagenta,
"brightcyan": BrightCyan,
"brightwhite": BrightWhite,
"bgblack": Bgblack,
"bgred": Bgred,
"bggreen": Bggreen,
"bgyellow": Bgyellow,
"bgblue": Bgblue,
"bgmagenta": Bgmagenta,
"bgcyan": Bgcyan,
"bgwhite": Bgwhite,
"bgbrightblack": BgbrightBlack,
"bgbrightred": BgbrightRed,
"bgbrightgreen": BgbrightGreen,
"bgbrightyellow": BgbrightYellow,
"bgbrightblue": BgbrightBlue,
"bgbrightmagenta": BgbrightMagenta,
"bgbrightcyan": BgbrightCyan,
"bgbrightwhite": BgbrightWhite,
"bold": Bold,
"italic": Italic,
"underline": Underline,
"inverse": Inverse,
}
var None Code
func FromString(s string) Code {
c := Code{}
for _, part := range strings.Split(s, "+") {
pc, ok := StringToCode[part]
if !ok {
continue
}
c = c.Add(pc)
}
return c
}
func (c Code) Write(w io.Writer, p []byte) (int, error) {
if c.SetString != "" {
if n, err := w.Write([]byte(c.SetString)); err != nil {
return n, err
}
if n, err := w.Write(p); err != nil {
return n, err
}
if n, err := w.Write([]byte(c.ResetString)); err != nil {
return n, err
}
return len(c.SetString) + len(p) + len(c.ResetString), nil
}
return w.Write(p)
}
func (c Code) Wrap(s string) string {
if c.ResetString != "" {
return c.SetString + s + c.ResetString
}
return s
}
type colorFormatter [3]any
func (cf colorFormatter) Format(state fmt.State, verb rune) {
switch verb {
case 's':
for _, s := range cf {
if s == nil {
continue
}
fmt.Fprint(state, s)
}
}
}
func (c Code) F(s any) fmt.Formatter {
if c.SetString != "" {
return colorFormatter([3]any{c.SetString, s, c.ResetString})
}
return colorFormatter([3]any{s})
}
type colorWriter struct {
w io.Writer
c Code
}
func (cw colorWriter) Write(p []byte) (int, error) {
return cw.c.Write(cw.w, p)
}
func (c Code) W(w io.Writer) io.Writer {
if c.SetString != "" {
return colorWriter{w: w, c: c}
}
return w
}
// Len of string excluding ANSI escape sequences
func Len(s string) int {
l := 0
inANSI := false
for _, c := range s {
if inANSI {
if c == 'm' {
inANSI = false
}
} else {
if c == '\x1b' {
inANSI = true
} else {
l++
}
}
}
return l
}
// Slice string to start:stop visible characters.
// An ANSI reset is added to the end of the string.
func Slice(s string, start, stop int) string {
l := 0
startByte := -1
inANSI := false
for i, c := range s {
if inANSI {
if c == 'm' {
inANSI = false
}
} else {
if c == '\x1b' {
inANSI = true
} else {
if startByte == -1 && l == start {
startByte = i
if stop == -1 {
return s[startByte:] + "\x1b[0m"
}
} else if l == stop {
return s[startByte:i] + "\x1b[0m"
}
l++
}
}
}
return s
}