diff options
author | Tim Culverhouse <tim@timculverhouse.com> | 2024-02-12 06:26:11 -0600 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2024-02-12 13:46:18 +0100 |
commit | a11e0bf692b7f17af07252748f105a8277a07196 (patch) | |
tree | d1751d33dbf60ccc0fd71c4d69599dd9d8403cb6 /lib/parse | |
parent | 7c7a7ba8027e3e12ba88dc54b7f4d882afd2b30a (diff) | |
download | aerc-a11e0bf692b7f17af07252748f105a8277a07196.tar.gz |
parse/ansi: remove tcell/terminfo dependency
The parse library builds an ansi-escaped string based on a buffer of
styled cells. Use constants which aerc will still parse properly (and
are the same as the terminfo package was pulling in) to remove
dependency on tcell/terminfo. Additionally, we can use the internal go
"fmt" package to write strings instead of the terminfo.TParm method
(which is much slower at formatting strings).
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'lib/parse')
-rw-r--r-- | lib/parse/ansi.go | 78 | ||||
-rw-r--r-- | lib/parse/ansi_test.go | 60 |
2 files changed, 69 insertions, 69 deletions
diff --git a/lib/parse/ansi.go b/lib/parse/ansi.go index ff44f8ba..3a802a51 100644 --- a/lib/parse/ansi.go +++ b/lib/parse/ansi.go @@ -13,12 +13,28 @@ import ( "git.sr.ht/~rjarry/aerc/log" "github.com/gdamore/tcell/v2" - "github.com/gdamore/tcell/v2/terminfo" "github.com/mattn/go-runewidth" ) var AnsiReg = regexp.MustCompile("\x1B\\[[0-?]*[ -/]*[@-~]") +const ( + setfgbgrgb = "\x1b[38;2;%d;%d;%d;48;2;%d;%d;%dm" + setfgrgb = "\x1b[38;2;%d;%d;%dm" + setbgrgb = "\x1b[48;2;%d;%d;%dm" + setfgbg = "\x1b[38;5;%d;48;5;%dm" + setfg = "\x1b[38;5;%dm" + setbg = "\x1b[48;5;%dm" + attrOff = "\x1B[m" + bold = "\x1B[1m" + dim = "\x1B[2m" + italic = "\x1B[3m" + underline = "\x1B[4m" + blink = "\x1B[5m" + reverse = "\x1B[7m" + strikethrough = "\x1B[9m" +) + // StripAnsi strips ansi escape codes from the reader func StripAnsi(r io.Reader) io.Reader { buf := bytes.NewBuffer(nil) @@ -101,16 +117,8 @@ func (rb *RuneBuffer) String() string { // string returns a string no longer than n runes. If 'left' is true, the left // side of the text is truncated. Pass 0 to return the full string func (rb *RuneBuffer) string(n int, left bool, char rune) string { - // Use xterm-256color to generate the string. Ultimately all output will - // be re-parsed as 'xterm-256color' and tcell will handle the final - // output sequences based on the user's TERM - ti, err := terminfo.LookupTerminfo("xterm-256color") - if err != nil { - // Who knows what happened - return "" - } var ( - s = strings.Builder{} + s = bytes.NewBuffer(nil) style = tcell.StyleDefault hasStyle = false // w will track the length we have written, or would have @@ -127,60 +135,52 @@ func (rb *RuneBuffer) string(n int, left bool, char rune) string { if style != r.Style { hasStyle = true style = r.Style - s.WriteString(ti.AttrOff) + s.WriteString(attrOff) fg, bg, attrs := style.Decompose() switch { - case fg.IsRGB() && bg.IsRGB() && ti.SetFgBgRGB != "": + case fg.IsRGB() && bg.IsRGB(): fr, fg, fb := fg.RGB() br, bg, bb := bg.RGB() - s.WriteString(ti.TParm( - ti.SetFgBgRGB, - int(fr), - int(fg), - int(fb), - int(br), - int(bg), - int(bb), - )) - case fg.IsRGB() && ti.SetFgRGB != "": + fmt.Fprintf(s, setfgbgrgb, fr, fg, fb, br, bg, bb) + case fg.IsRGB(): // RGB r, g, b := fg.RGB() - s.WriteString(ti.TParm(ti.SetFgRGB, int(r), int(g), int(b))) - case bg.IsRGB() && ti.SetBgRGB != "": + fmt.Fprintf(s, setfgrgb, r, g, b) + case bg.IsRGB(): // RGB r, g, b := bg.RGB() - s.WriteString(ti.TParm(ti.SetBgRGB, int(r), int(g), int(b))) + fmt.Fprintf(s, setbgrgb, r, g, b) // Indexed - case fg.Valid() && bg.Valid() && ti.SetFgBg != "": - s.WriteString(ti.TParm(ti.SetFgBg, int(fg&0xff), int(bg&0xff))) - case fg.Valid() && ti.SetFg != "": - s.WriteString(ti.TParm(ti.SetFg, int(fg&0xff))) - case bg.Valid() && ti.SetBg != "": - s.WriteString(ti.TParm(ti.SetBg, int(bg&0xff))) + case fg.Valid() && bg.Valid(): + fmt.Fprintf(s, setfgbg, fg&0xFF, bg&0xFF) + case fg.Valid(): + fmt.Fprintf(s, setfg, fg&0xFF) + case bg.Valid(): + fmt.Fprintf(s, setbg, bg&0xFF) } if attrs&tcell.AttrBold != 0 { - s.WriteString(ti.Bold) + s.WriteString(bold) } if attrs&tcell.AttrUnderline != 0 { - s.WriteString(ti.Underline) + s.WriteString(underline) } if attrs&tcell.AttrReverse != 0 { - s.WriteString(ti.Reverse) + s.WriteString(reverse) } if attrs&tcell.AttrBlink != 0 { - s.WriteString(ti.Blink) + s.WriteString(blink) } if attrs&tcell.AttrDim != 0 { - s.WriteString(ti.Dim) + s.WriteString(dim) } if attrs&tcell.AttrItalic != 0 { - s.WriteString(ti.Italic) + s.WriteString(italic) } if attrs&tcell.AttrStrikeThrough != 0 { - s.WriteString(ti.StrikeThrough) + s.WriteString(strikethrough) } } @@ -200,7 +200,7 @@ func (rb *RuneBuffer) string(n int, left bool, char rune) string { } } if hasStyle { - s.WriteString(ti.AttrOff) + s.WriteString(attrOff) } return s.String() } diff --git a/lib/parse/ansi_test.go b/lib/parse/ansi_test.go index f916412b..6aa95b18 100644 --- a/lib/parse/ansi_test.go +++ b/lib/parse/ansi_test.go @@ -26,49 +26,49 @@ func TestParser(t *testing.T) { { name: "bold", input: "\x1b[1mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello, world\x1b[m", expectedLen: 12, }, { name: "dim", input: "\x1b[2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[2mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[2mhello, world\x1b[m", expectedLen: 12, }, { name: "bold and dim", input: "\x1b[1;2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1m\x1b[2mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1m\x1b[2mhello, world\x1b[m", expectedLen: 12, }, { name: "italic", input: "\x1b[3mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[3mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[3mhello, world\x1b[m", expectedLen: 12, }, { name: "underline", input: "\x1b[4mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[4mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[4mhello, world\x1b[m", expectedLen: 12, }, { name: "blink", input: "\x1b[5mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[5mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[5mhello, world\x1b[m", expectedLen: 12, }, { name: "fast blink", input: "\x1b[6mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[5mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[5mhello, world\x1b[m", expectedLen: 12, }, { name: "reverse", input: "\x1b[7mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[7mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[7mhello, world\x1b[m", expectedLen: 12, }, { @@ -80,121 +80,121 @@ func TestParser(t *testing.T) { { name: "strikethrough", input: "\x1b[9mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[9mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[9mhello, world\x1b[m", expectedLen: 12, }, { name: "bold hello, normal world", input: "\x1b[1mhello, \x1b[21mworld", - expectedString: "\x1b(B\x1b[m\x1b[1mhello, \x1b(B\x1b[mworld\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello, \x1b[mworld\x1b[m", expectedLen: 12, }, { name: "bold hello, normal world v2", input: "\x1b[1mhello, \x1b[mworld", - expectedString: "\x1b(B\x1b[m\x1b[1mhello, \x1b(B\x1b[mworld\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello, \x1b[mworld\x1b[m", expectedLen: 12, }, { name: "8 bit color: foreground", input: "\x1b[30mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[30mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;0mhello, world\x1b[m", expectedLen: 12, }, { name: "8 bit color: background", input: "\x1b[41mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[41mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;1mhello, world\x1b[m", expectedLen: 12, }, { name: "8 bit color: foreground and background", input: "\x1b[31;41mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[31;41mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;1;48;5;1mhello, world\x1b[m", expectedLen: 12, }, { name: "16 bit color: foreground", input: "\x1b[90mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[90mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;8mhello, world\x1b[m", expectedLen: 12, }, { name: "16 bit color: background", input: "\x1b[101mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[101mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;9mhello, world\x1b[m", expectedLen: 12, }, { name: "16 bit color: foreground and background", input: "\x1b[91;101mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[91;101mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;9;48;5;9mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: foreground", input: "\x1b[38;5;2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[32mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;2mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: foreground", input: "\x1b[38;5;132mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;5;132mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;132mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: background", input: "\x1b[48;5;132mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[48;5;132mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;132mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: foreground and background", input: "\x1b[38;5;20;48;5;20mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;5;20;48;5;20mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;20;48;5;20mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: background", input: "\x1b[48;5;2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[42mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;2mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground", input: "\x1b[38;2;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground with color space", input: "\x1b[38;2;;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground with color space and colons", input: "\x1b[38:2::0:0:0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: background", input: "\x1b[48;2;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[48;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: background with color space", input: "\x1b[48;2;;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[48;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground and background", input: "\x1b[38;2;200;200;200;48;2;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;200;200;200;48;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;200;200;200;48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, } @@ -222,7 +222,7 @@ func TestTruncate(t *testing.T) { { name: "bold, truncate at 5", input: "\x1b[1mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1mhello\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello\x1b[m", }, } @@ -249,7 +249,7 @@ func TestTruncateHead(t *testing.T) { { name: "bold, truncate head at 5", input: "\x1b[1mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1mworld\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mworld\x1b[m", }, } |