From d960f27da45fb9bf9120383c6df3184f3009360d Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Mon, 6 Feb 2023 16:49:29 -0500 Subject: [PATCH] feat(style): add underline style and color support Add support to CSI 4:x and CSI 58 escape sequences Ref: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR Ref: https://sw.kovidgoyal.net/kitty/underlines/ --- color.go | 2 + style.go | 79 +++++++++++++++++++++++---- templatehelper.go | 64 +++++++++++++++------- testdata/templatehelper.tpl | 8 ++- testdata/templatehelper_ansi.txt | 8 ++- testdata/templatehelper_ansi256.txt | 8 ++- testdata/templatehelper_ascii.txt | 8 ++- testdata/templatehelper_truecolor.txt | 8 ++- 8 files changed, 151 insertions(+), 34 deletions(-) diff --git a/color.go b/color.go index 2c59c2d..f53fb17 100644 --- a/color.go +++ b/color.go @@ -19,6 +19,8 @@ const ( ForegroudSeq = "38" // Background sequence code. BackgroundSeq = "48" + // Underline color sequence code. + UnderColorSeq = "58" ) // Foreground and Background sequence codes diff --git a/style.go b/style.go index e0138d3..9a11741 100644 --- a/style.go +++ b/style.go @@ -9,15 +9,20 @@ import ( // Sequence definitions. const ( - ResetSeq = "0" - BoldSeq = "1" - FaintSeq = "2" - ItalicSeq = "3" - UnderlineSeq = "4" - BlinkSeq = "5" - ReverseSeq = "7" - CrossOutSeq = "9" - OverlineSeq = "53" + ResetSeq = "0" + BoldSeq = "1" + FaintSeq = "2" + ItalicSeq = "3" + UnderlineSeq = "4" // also 4:1 + UnderdoubleSeq = "4:2" + UndercurlSeq = "4:3" + UnderdotSeq = "4:4" + UnderdashSeq = "4:5" + BlinkSeq = "5" + ReverseSeq = "7" + CrossOutSeq = "9" + OverlineSeq = "53" + UndercolorSeq = "58" ) // Style is a string that various rendering styles can be applied to. @@ -106,9 +111,63 @@ func (t Style) Italic() Style { return t } +func undercolorSeq(c Color) []string { + var seqs []string + switch v := c.(type) { + case NoColor: + return seqs + case ANSIColor: + // ANSIColor(s) are their own sequences. + // Underline colors don't support ANSI color sequences. + // Convert them into ANSI256 + c = ANSI256Color(v.Color) + } + seqs = append(seqs, UndercolorSeq, c.Sequence()) + return seqs +} + // Underline enables underline rendering. -func (t Style) Underline() Style { +func (t Style) Underline(c ...Color) Style { t.styles = append(t.styles, UnderlineSeq) + if len(c) > 0 { + t.styles = append(t.styles, undercolorSeq(c[0])...) + } + return t +} + +// Underdouble enables double underline rendering. +func (t Style) Underdouble(c ...Color) Style { + t.styles = append(t.styles, UnderdoubleSeq) + if len(c) > 0 { + t.styles = append(t.styles, undercolorSeq(c[0])...) + } + return t +} + +// Undercurl enables curly underline rendering. +func (t Style) Undercurl(c ...Color) Style { + t.styles = append(t.styles, UndercurlSeq) + if len(c) > 0 { + t.styles = append(t.styles, undercolorSeq(c[0])...) + } + return t +} + +// Underdot enables dotted underline rendering. +func (t Style) Underdot(c ...Color) Style { + t.styles = append(t.styles, UnderdotSeq) + if len(c) > 0 { + t.styles = append(t.styles, undercolorSeq(c[0])...) + } + return t +} + +// Underdash enables dashed underline rendering. +func (t Style) Underdash(c ...Color) Style { + t.styles = append(t.styles, UnderdashSeq) + if len(c) > 0 { + t.styles = append(t.styles, undercolorSeq(c[0])...) + } return t } diff --git a/templatehelper.go b/templatehelper.go index d75b079..74d3563 100644 --- a/templatehelper.go +++ b/templatehelper.go @@ -45,14 +45,29 @@ func TemplateFuncs(p Profile) template.FuncMap { return s.String() }, - "Bold": styleFunc(p, Style.Bold), - "Faint": styleFunc(p, Style.Faint), - "Italic": styleFunc(p, Style.Italic), - "Underline": styleFunc(p, Style.Underline), - "Overline": styleFunc(p, Style.Overline), - "Blink": styleFunc(p, Style.Blink), - "Reverse": styleFunc(p, Style.Reverse), - "CrossOut": styleFunc(p, Style.CrossOut), + "Bold": styleFunc(p, Style.Bold), + "Faint": styleFunc(p, Style.Faint), + "Italic": styleFunc(p, Style.Italic), + "Overline": styleFunc(p, Style.Overline), + "Blink": styleFunc(p, Style.Blink), + "Reverse": styleFunc(p, Style.Reverse), + "CrossOut": styleFunc(p, Style.CrossOut), + "Underline": styleUnderline(p, Style.Underline), + "Underdouble": styleUnderline(p, Style.Underdouble), + "Undercurl": styleUnderline(p, Style.Undercurl), + "Underdot": styleUnderline(p, Style.Underdot), + "Underdash": styleUnderline(p, Style.Underdash), + } +} + +func styleUnderline(p Profile, f func(Style, ...Color) Style) func(...interface{}) string { + return func(values ...interface{}) string { + if len(values) == 2 { + s := p.String(values[1].(string)) + return f(s, p.Color(values[0].(string))).String() + } + s := p.String(values[0].(string)) + return f(s).String() } } @@ -64,17 +79,21 @@ func styleFunc(p Profile, f func(Style) Style) func(...interface{}) string { } var noopTemplateFuncs = template.FuncMap{ - "Color": noColorFunc, - "Foreground": noColorFunc, - "Background": noColorFunc, - "Bold": noStyleFunc, - "Faint": noStyleFunc, - "Italic": noStyleFunc, - "Underline": noStyleFunc, - "Overline": noStyleFunc, - "Blink": noStyleFunc, - "Reverse": noStyleFunc, - "CrossOut": noStyleFunc, + "Color": noColorFunc, + "Foreground": noColorFunc, + "Background": noColorFunc, + "Bold": noStyleFunc, + "Faint": noStyleFunc, + "Italic": noStyleFunc, + "Overline": noStyleFunc, + "Blink": noStyleFunc, + "Reverse": noStyleFunc, + "CrossOut": noStyleFunc, + "Underline": noUnderline, + "Underdouble": noUnderline, + "Undercurl": noUnderline, + "Underdot": noUnderline, + "Underdash": noUnderline, } func noColorFunc(values ...interface{}) string { @@ -84,3 +103,10 @@ func noColorFunc(values ...interface{}) string { func noStyleFunc(values ...interface{}) string { return values[0].(string) } + +func noUnderline(values ...interface{}) string { + if len(values) == 2 { + return values[1].(string) + } + return values[0].(string) +} diff --git a/testdata/templatehelper.tpl b/testdata/templatehelper.tpl index f3eead9..957e128 100644 --- a/testdata/templatehelper.tpl +++ b/testdata/templatehelper.tpl @@ -8,7 +8,13 @@ Plain {{ Faint "Faint" }} {{ Italic "Italic" }} {{ Underline "Underline" }} +{{ Underline "#ff0000" "Red Underline" }} +{{ Underdouble "Double Underline" }} +{{ Undercurl "Undercurl" }} +{{ Undercurl "#00ff00" "Green Undercurl" }} +{{ Underdot "Dotted Underline" }} +{{ Underdash "Dashed Underline" }} {{ Overline "Overline" }} {{ Blink "Blink" }} {{ Reverse "Reverse" }} -{{ CrossOut "CrossOut" }} \ No newline at end of file +{{ CrossOut "CrossOut" }} diff --git a/testdata/templatehelper_ansi.txt b/testdata/templatehelper_ansi.txt index 285c638..94a5eda 100644 --- a/testdata/templatehelper_ansi.txt +++ b/testdata/templatehelper_ansi.txt @@ -8,7 +8,13 @@ Plain Faint Italic Underline +Red Underline +[4:2mDouble Underline +[4:3mUndercurl +[4:3;58;5;10mGreen Undercurl +[4:4mDotted Underline +[4:5mDashed Underline Overline Blink Reverse -CrossOut \ No newline at end of file +CrossOut diff --git a/testdata/templatehelper_ansi256.txt b/testdata/templatehelper_ansi256.txt index 1e28230..27db7bb 100644 --- a/testdata/templatehelper_ansi256.txt +++ b/testdata/templatehelper_ansi256.txt @@ -8,7 +8,13 @@ Plain Faint Italic Underline +Red Underline +[4:2mDouble Underline +[4:3mUndercurl +[4:3;58;5;46mGreen Undercurl +[4:4mDotted Underline +[4:5mDashed Underline Overline Blink Reverse -CrossOut \ No newline at end of file +CrossOut diff --git a/testdata/templatehelper_ascii.txt b/testdata/templatehelper_ascii.txt index e7b9b21..c4619c6 100644 --- a/testdata/templatehelper_ascii.txt +++ b/testdata/templatehelper_ascii.txt @@ -8,7 +8,13 @@ Bold Faint Italic Underline +Red Underline +Double Underline +Undercurl +Green Undercurl +Dotted Underline +Dashed Underline Overline Blink Reverse -CrossOut \ No newline at end of file +CrossOut diff --git a/testdata/templatehelper_truecolor.txt b/testdata/templatehelper_truecolor.txt index 81426c8..d5671bb 100644 --- a/testdata/templatehelper_truecolor.txt +++ b/testdata/templatehelper_truecolor.txt @@ -8,7 +8,13 @@ Plain Faint Italic Underline +Red Underline +[4:2mDouble Underline +[4:3mUndercurl +[4:3;58;2;0;255;0mGreen Undercurl +[4:4mDotted Underline +[4:5mDashed Underline Overline Blink Reverse -CrossOut \ No newline at end of file +CrossOut