aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorTim Culverhouse <tim@timculverhouse.com>2024-02-12 06:26:11 -0600
committerRobin Jarry <robin@jarry.cc>2024-02-12 13:46:18 +0100
commita11e0bf692b7f17af07252748f105a8277a07196 (patch)
treed1751d33dbf60ccc0fd71c4d69599dd9d8403cb6 /lib
parent7c7a7ba8027e3e12ba88dc54b7f4d882afd2b30a (diff)
downloadaerc-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')
-rw-r--r--lib/parse/ansi.go78
-rw-r--r--lib/parse/ansi_test.go60
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",
},
}