Skip to content

Commit a93d6b6

Browse files
committed
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/
1 parent a9f4749 commit a93d6b6

8 files changed

+151
-34
lines changed

color.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const (
1919
ForegroudSeq = "38"
2020
// Background sequence code.
2121
BackgroundSeq = "48"
22+
// Underline color sequence code.
23+
UnderColorSeq = "58"
2224
)
2325

2426
// Foreground and Background sequence codes

style.go

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,20 @@ import (
99

1010
// Sequence definitions.
1111
const (
12-
ResetSeq = "0"
13-
BoldSeq = "1"
14-
FaintSeq = "2"
15-
ItalicSeq = "3"
16-
UnderlineSeq = "4"
17-
BlinkSeq = "5"
18-
ReverseSeq = "7"
19-
CrossOutSeq = "9"
20-
OverlineSeq = "53"
12+
ResetSeq = "0"
13+
BoldSeq = "1"
14+
FaintSeq = "2"
15+
ItalicSeq = "3"
16+
UnderlineSeq = "4" // also 4:1
17+
UnderDoubleSeq = "4:2"
18+
UnderCurlSeq = "4:3"
19+
UnderDottedSeq = "4:4"
20+
UnderDashedSeq = "4:5"
21+
BlinkSeq = "5"
22+
ReverseSeq = "7"
23+
CrossOutSeq = "9"
24+
OverlineSeq = "53"
25+
UnderlineColorSeq = "58"
2126
)
2227

2328
// Style is a string that various rendering styles can be applied to.
@@ -106,9 +111,63 @@ func (t Style) Italic() Style {
106111
return t
107112
}
108113

114+
func undercolorSeq(c Color) []string {
115+
var seqs []string
116+
switch v := c.(type) {
117+
case NoColor:
118+
return seqs
119+
case ANSIColor:
120+
// ANSIColor(s) are their own sequences.
121+
// Underline colors don't support ANSI color sequences.
122+
// Convert them into ANSI256
123+
c = ANSI256Color(v.Color)
124+
}
125+
seqs = append(seqs, UnderlineColorSeq, c.Sequence())
126+
return seqs
127+
}
128+
109129
// Underline enables underline rendering.
110-
func (t Style) Underline() Style {
130+
func (t Style) Underline(c ...Color) Style {
111131
t.styles = append(t.styles, UnderlineSeq)
132+
if len(c) > 0 {
133+
t.styles = append(t.styles, undercolorSeq(c[0])...)
134+
}
135+
return t
136+
}
137+
138+
// UnderDouble enables double underline rendering.
139+
func (t Style) UnderDouble(c ...Color) Style {
140+
t.styles = append(t.styles, UnderDoubleSeq)
141+
if len(c) > 0 {
142+
t.styles = append(t.styles, undercolorSeq(c[0])...)
143+
}
144+
return t
145+
}
146+
147+
// UnderCurl enables curly underline rendering.
148+
func (t Style) UnderCurl(c ...Color) Style {
149+
t.styles = append(t.styles, UnderCurlSeq)
150+
if len(c) > 0 {
151+
t.styles = append(t.styles, undercolorSeq(c[0])...)
152+
}
153+
return t
154+
}
155+
156+
// UnderDotted enables dotted underline rendering.
157+
func (t Style) UnderDotted(c ...Color) Style {
158+
t.styles = append(t.styles, UnderDottedSeq)
159+
if len(c) > 0 {
160+
t.styles = append(t.styles, undercolorSeq(c[0])...)
161+
}
162+
return t
163+
}
164+
165+
// UnderDashed enables dashed underline rendering.
166+
func (t Style) UnderDashed(c ...Color) Style {
167+
t.styles = append(t.styles, UnderDashedSeq)
168+
if len(c) > 0 {
169+
t.styles = append(t.styles, undercolorSeq(c[0])...)
170+
}
112171
return t
113172
}
114173

templatehelper.go

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,29 @@ func TemplateFuncs(p Profile) template.FuncMap {
4545

4646
return s.String()
4747
},
48-
"Bold": styleFunc(p, Style.Bold),
49-
"Faint": styleFunc(p, Style.Faint),
50-
"Italic": styleFunc(p, Style.Italic),
51-
"Underline": styleFunc(p, Style.Underline),
52-
"Overline": styleFunc(p, Style.Overline),
53-
"Blink": styleFunc(p, Style.Blink),
54-
"Reverse": styleFunc(p, Style.Reverse),
55-
"CrossOut": styleFunc(p, Style.CrossOut),
48+
"Bold": styleFunc(p, Style.Bold),
49+
"Faint": styleFunc(p, Style.Faint),
50+
"Italic": styleFunc(p, Style.Italic),
51+
"Overline": styleFunc(p, Style.Overline),
52+
"Blink": styleFunc(p, Style.Blink),
53+
"Reverse": styleFunc(p, Style.Reverse),
54+
"CrossOut": styleFunc(p, Style.CrossOut),
55+
"Underline": styleUnderline(p, Style.Underline),
56+
"UnderDouble": styleUnderline(p, Style.UnderDouble),
57+
"UnderCurl": styleUnderline(p, Style.UnderCurl),
58+
"UnderDotted": styleUnderline(p, Style.UnderDotted),
59+
"UnderDashed": styleUnderline(p, Style.UnderDashed),
60+
}
61+
}
62+
63+
func styleUnderline(p Profile, f func(Style, ...Color) Style) func(...interface{}) string {
64+
return func(values ...interface{}) string {
65+
if len(values) == 2 {
66+
s := p.String(values[1].(string))
67+
return f(s, p.Color(values[0].(string))).String()
68+
}
69+
s := p.String(values[0].(string))
70+
return f(s).String()
5671
}
5772
}
5873

@@ -64,17 +79,21 @@ func styleFunc(p Profile, f func(Style) Style) func(...interface{}) string {
6479
}
6580

6681
var noopTemplateFuncs = template.FuncMap{
67-
"Color": noColorFunc,
68-
"Foreground": noColorFunc,
69-
"Background": noColorFunc,
70-
"Bold": noStyleFunc,
71-
"Faint": noStyleFunc,
72-
"Italic": noStyleFunc,
73-
"Underline": noStyleFunc,
74-
"Overline": noStyleFunc,
75-
"Blink": noStyleFunc,
76-
"Reverse": noStyleFunc,
77-
"CrossOut": noStyleFunc,
82+
"Color": noColorFunc,
83+
"Foreground": noColorFunc,
84+
"Background": noColorFunc,
85+
"Bold": noStyleFunc,
86+
"Faint": noStyleFunc,
87+
"Italic": noStyleFunc,
88+
"Overline": noStyleFunc,
89+
"Blink": noStyleFunc,
90+
"Reverse": noStyleFunc,
91+
"CrossOut": noStyleFunc,
92+
"Underline": noUnderline,
93+
"UnderDouble": noUnderline,
94+
"UnderCurl": noUnderline,
95+
"UnderDotted": noUnderline,
96+
"UnderDashed": noUnderline,
7897
}
7998

8099
func noColorFunc(values ...interface{}) string {
@@ -84,3 +103,10 @@ func noColorFunc(values ...interface{}) string {
84103
func noStyleFunc(values ...interface{}) string {
85104
return values[0].(string)
86105
}
106+
107+
func noUnderline(values ...interface{}) string {
108+
if len(values) == 2 {
109+
return values[1].(string)
110+
}
111+
return values[0].(string)
112+
}

testdata/templatehelper.tpl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ Plain
88
{{ Faint "Faint" }}
99
{{ Italic "Italic" }}
1010
{{ Underline "Underline" }}
11+
{{ Underline "#ff0000" "Red Underline" }}
12+
{{ UnderDouble "Double Underline" }}
13+
{{ UnderCurl "Undercurl" }}
14+
{{ UnderCurl "#00ff00" "Green Undercurl" }}
15+
{{ UnderDotted "Dotted Underline" }}
16+
{{ UnderDashed "Dashed Underline" }}
1117
{{ Overline "Overline" }}
1218
{{ Blink "Blink" }}
1319
{{ Reverse "Reverse" }}
14-
{{ CrossOut "CrossOut" }}
20+
{{ CrossOut "CrossOut" }}

testdata/templatehelper_ansi.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ Plain
88
Faint
99
Italic
1010
Underline
11+
Red Underline
12+
[4:2mDouble Underline
13+
[4:3mUndercurl
14+
[4:3;58;5;10mGreen Undercurl
15+
[4:4mDotted Underline
16+
[4:5mDashed Underline
1117
Overline
1218
Blink
1319
Reverse
14-
CrossOut
20+
CrossOut

testdata/templatehelper_ansi256.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ Plain
88
Faint
99
Italic
1010
Underline
11+
Red Underline
12+
[4:2mDouble Underline
13+
[4:3mUndercurl
14+
[4:3;58;5;46mGreen Undercurl
15+
[4:4mDotted Underline
16+
[4:5mDashed Underline
1117
Overline
1218
Blink
1319
Reverse
14-
CrossOut
20+
CrossOut

testdata/templatehelper_ascii.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ Bold
88
Faint
99
Italic
1010
Underline
11+
Red Underline
12+
Double Underline
13+
Undercurl
14+
Green Undercurl
15+
Dotted Underline
16+
Dashed Underline
1117
Overline
1218
Blink
1319
Reverse
14-
CrossOut
20+
CrossOut

testdata/templatehelper_truecolor.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ Plain
88
Faint
99
Italic
1010
Underline
11+
Red Underline
12+
[4:2mDouble Underline
13+
[4:3mUndercurl
14+
[4:3;58;2;0;255;0mGreen Undercurl
15+
[4:4mDotted Underline
16+
[4:5mDashed Underline
1117
Overline
1218
Blink
1319
Reverse
14-
CrossOut
20+
CrossOut

0 commit comments

Comments
 (0)