main
1package ansi
2
3import (
4 "image/color"
5 "strconv"
6 "strings"
7)
8
9// ResetStyle is a SGR (Select Graphic Rendition) style sequence that resets
10// all attributes.
11// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
12const ResetStyle = "\x1b[m"
13
14// Attr is a SGR (Select Graphic Rendition) style attribute.
15type Attr = int
16
17// Style represents an ANSI SGR (Select Graphic Rendition) style.
18type Style []string
19
20// String returns the ANSI SGR (Select Graphic Rendition) style sequence for
21// the given style.
22func (s Style) String() string {
23 if len(s) == 0 {
24 return ResetStyle
25 }
26 return "\x1b[" + strings.Join(s, ";") + "m"
27}
28
29// Styled returns a styled string with the given style applied.
30func (s Style) Styled(str string) string {
31 if len(s) == 0 {
32 return str
33 }
34 return s.String() + str + ResetStyle
35}
36
37// Reset appends the reset style attribute to the style.
38func (s Style) Reset() Style {
39 return append(s, resetAttr)
40}
41
42// Bold appends the bold style attribute to the style.
43func (s Style) Bold() Style {
44 return append(s, boldAttr)
45}
46
47// Faint appends the faint style attribute to the style.
48func (s Style) Faint() Style {
49 return append(s, faintAttr)
50}
51
52// Italic appends the italic style attribute to the style.
53func (s Style) Italic() Style {
54 return append(s, italicAttr)
55}
56
57// Underline appends the underline style attribute to the style.
58func (s Style) Underline() Style {
59 return append(s, underlineAttr)
60}
61
62// UnderlineStyle appends the underline style attribute to the style.
63func (s Style) UnderlineStyle(u UnderlineStyle) Style {
64 switch u {
65 case NoUnderlineStyle:
66 return s.NoUnderline()
67 case SingleUnderlineStyle:
68 return s.Underline()
69 case DoubleUnderlineStyle:
70 return append(s, doubleUnderlineStyle)
71 case CurlyUnderlineStyle:
72 return append(s, curlyUnderlineStyle)
73 case DottedUnderlineStyle:
74 return append(s, dottedUnderlineStyle)
75 case DashedUnderlineStyle:
76 return append(s, dashedUnderlineStyle)
77 }
78 return s
79}
80
81// DoubleUnderline appends the double underline style attribute to the style.
82// This is a convenience method for UnderlineStyle(DoubleUnderlineStyle).
83func (s Style) DoubleUnderline() Style {
84 return s.UnderlineStyle(DoubleUnderlineStyle)
85}
86
87// CurlyUnderline appends the curly underline style attribute to the style.
88// This is a convenience method for UnderlineStyle(CurlyUnderlineStyle).
89func (s Style) CurlyUnderline() Style {
90 return s.UnderlineStyle(CurlyUnderlineStyle)
91}
92
93// DottedUnderline appends the dotted underline style attribute to the style.
94// This is a convenience method for UnderlineStyle(DottedUnderlineStyle).
95func (s Style) DottedUnderline() Style {
96 return s.UnderlineStyle(DottedUnderlineStyle)
97}
98
99// DashedUnderline appends the dashed underline style attribute to the style.
100// This is a convenience method for UnderlineStyle(DashedUnderlineStyle).
101func (s Style) DashedUnderline() Style {
102 return s.UnderlineStyle(DashedUnderlineStyle)
103}
104
105// SlowBlink appends the slow blink style attribute to the style.
106func (s Style) SlowBlink() Style {
107 return append(s, slowBlinkAttr)
108}
109
110// RapidBlink appends the rapid blink style attribute to the style.
111func (s Style) RapidBlink() Style {
112 return append(s, rapidBlinkAttr)
113}
114
115// Reverse appends the reverse style attribute to the style.
116func (s Style) Reverse() Style {
117 return append(s, reverseAttr)
118}
119
120// Conceal appends the conceal style attribute to the style.
121func (s Style) Conceal() Style {
122 return append(s, concealAttr)
123}
124
125// Strikethrough appends the strikethrough style attribute to the style.
126func (s Style) Strikethrough() Style {
127 return append(s, strikethroughAttr)
128}
129
130// NoBold appends the no bold style attribute to the style.
131func (s Style) NoBold() Style {
132 return append(s, noBoldAttr)
133}
134
135// NormalIntensity appends the normal intensity style attribute to the style.
136func (s Style) NormalIntensity() Style {
137 return append(s, normalIntensityAttr)
138}
139
140// NoItalic appends the no italic style attribute to the style.
141func (s Style) NoItalic() Style {
142 return append(s, noItalicAttr)
143}
144
145// NoUnderline appends the no underline style attribute to the style.
146func (s Style) NoUnderline() Style {
147 return append(s, noUnderlineAttr)
148}
149
150// NoBlink appends the no blink style attribute to the style.
151func (s Style) NoBlink() Style {
152 return append(s, noBlinkAttr)
153}
154
155// NoReverse appends the no reverse style attribute to the style.
156func (s Style) NoReverse() Style {
157 return append(s, noReverseAttr)
158}
159
160// NoConceal appends the no conceal style attribute to the style.
161func (s Style) NoConceal() Style {
162 return append(s, noConcealAttr)
163}
164
165// NoStrikethrough appends the no strikethrough style attribute to the style.
166func (s Style) NoStrikethrough() Style {
167 return append(s, noStrikethroughAttr)
168}
169
170// DefaultForegroundColor appends the default foreground color style attribute to the style.
171func (s Style) DefaultForegroundColor() Style {
172 return append(s, defaultForegroundColorAttr)
173}
174
175// DefaultBackgroundColor appends the default background color style attribute to the style.
176func (s Style) DefaultBackgroundColor() Style {
177 return append(s, defaultBackgroundColorAttr)
178}
179
180// DefaultUnderlineColor appends the default underline color style attribute to the style.
181func (s Style) DefaultUnderlineColor() Style {
182 return append(s, defaultUnderlineColorAttr)
183}
184
185// ForegroundColor appends the foreground color style attribute to the style.
186func (s Style) ForegroundColor(c Color) Style {
187 return append(s, foregroundColorString(c))
188}
189
190// BackgroundColor appends the background color style attribute to the style.
191func (s Style) BackgroundColor(c Color) Style {
192 return append(s, backgroundColorString(c))
193}
194
195// UnderlineColor appends the underline color style attribute to the style.
196func (s Style) UnderlineColor(c Color) Style {
197 return append(s, underlineColorString(c))
198}
199
200// UnderlineStyle represents an ANSI SGR (Select Graphic Rendition) underline
201// style.
202type UnderlineStyle = byte
203
204const (
205 doubleUnderlineStyle = "4:2"
206 curlyUnderlineStyle = "4:3"
207 dottedUnderlineStyle = "4:4"
208 dashedUnderlineStyle = "4:5"
209)
210
211const (
212 // NoUnderlineStyle is the default underline style.
213 NoUnderlineStyle UnderlineStyle = iota
214 // SingleUnderlineStyle is a single underline style.
215 SingleUnderlineStyle
216 // DoubleUnderlineStyle is a double underline style.
217 DoubleUnderlineStyle
218 // CurlyUnderlineStyle is a curly underline style.
219 CurlyUnderlineStyle
220 // DottedUnderlineStyle is a dotted underline style.
221 DottedUnderlineStyle
222 // DashedUnderlineStyle is a dashed underline style.
223 DashedUnderlineStyle
224)
225
226// SGR (Select Graphic Rendition) style attributes.
227// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
228const (
229 ResetAttr Attr = 0
230 BoldAttr Attr = 1
231 FaintAttr Attr = 2
232 ItalicAttr Attr = 3
233 UnderlineAttr Attr = 4
234 SlowBlinkAttr Attr = 5
235 RapidBlinkAttr Attr = 6
236 ReverseAttr Attr = 7
237 ConcealAttr Attr = 8
238 StrikethroughAttr Attr = 9
239 NoBoldAttr Attr = 21 // Some terminals treat this as double underline.
240 NormalIntensityAttr Attr = 22
241 NoItalicAttr Attr = 23
242 NoUnderlineAttr Attr = 24
243 NoBlinkAttr Attr = 25
244 NoReverseAttr Attr = 27
245 NoConcealAttr Attr = 28
246 NoStrikethroughAttr Attr = 29
247 BlackForegroundColorAttr Attr = 30
248 RedForegroundColorAttr Attr = 31
249 GreenForegroundColorAttr Attr = 32
250 YellowForegroundColorAttr Attr = 33
251 BlueForegroundColorAttr Attr = 34
252 MagentaForegroundColorAttr Attr = 35
253 CyanForegroundColorAttr Attr = 36
254 WhiteForegroundColorAttr Attr = 37
255 ExtendedForegroundColorAttr Attr = 38
256 DefaultForegroundColorAttr Attr = 39
257 BlackBackgroundColorAttr Attr = 40
258 RedBackgroundColorAttr Attr = 41
259 GreenBackgroundColorAttr Attr = 42
260 YellowBackgroundColorAttr Attr = 43
261 BlueBackgroundColorAttr Attr = 44
262 MagentaBackgroundColorAttr Attr = 45
263 CyanBackgroundColorAttr Attr = 46
264 WhiteBackgroundColorAttr Attr = 47
265 ExtendedBackgroundColorAttr Attr = 48
266 DefaultBackgroundColorAttr Attr = 49
267 ExtendedUnderlineColorAttr Attr = 58
268 DefaultUnderlineColorAttr Attr = 59
269 BrightBlackForegroundColorAttr Attr = 90
270 BrightRedForegroundColorAttr Attr = 91
271 BrightGreenForegroundColorAttr Attr = 92
272 BrightYellowForegroundColorAttr Attr = 93
273 BrightBlueForegroundColorAttr Attr = 94
274 BrightMagentaForegroundColorAttr Attr = 95
275 BrightCyanForegroundColorAttr Attr = 96
276 BrightWhiteForegroundColorAttr Attr = 97
277 BrightBlackBackgroundColorAttr Attr = 100
278 BrightRedBackgroundColorAttr Attr = 101
279 BrightGreenBackgroundColorAttr Attr = 102
280 BrightYellowBackgroundColorAttr Attr = 103
281 BrightBlueBackgroundColorAttr Attr = 104
282 BrightMagentaBackgroundColorAttr Attr = 105
283 BrightCyanBackgroundColorAttr Attr = 106
284 BrightWhiteBackgroundColorAttr Attr = 107
285
286 RGBColorIntroducerAttr Attr = 2
287 ExtendedColorIntroducerAttr Attr = 5
288)
289
290const (
291 resetAttr = "0"
292 boldAttr = "1"
293 faintAttr = "2"
294 italicAttr = "3"
295 underlineAttr = "4"
296 slowBlinkAttr = "5"
297 rapidBlinkAttr = "6"
298 reverseAttr = "7"
299 concealAttr = "8"
300 strikethroughAttr = "9"
301 noBoldAttr = "21"
302 normalIntensityAttr = "22"
303 noItalicAttr = "23"
304 noUnderlineAttr = "24"
305 noBlinkAttr = "25"
306 noReverseAttr = "27"
307 noConcealAttr = "28"
308 noStrikethroughAttr = "29"
309 blackForegroundColorAttr = "30"
310 redForegroundColorAttr = "31"
311 greenForegroundColorAttr = "32"
312 yellowForegroundColorAttr = "33"
313 blueForegroundColorAttr = "34"
314 magentaForegroundColorAttr = "35"
315 cyanForegroundColorAttr = "36"
316 whiteForegroundColorAttr = "37"
317 extendedForegroundColorAttr = "38"
318 defaultForegroundColorAttr = "39"
319 blackBackgroundColorAttr = "40"
320 redBackgroundColorAttr = "41"
321 greenBackgroundColorAttr = "42"
322 yellowBackgroundColorAttr = "43"
323 blueBackgroundColorAttr = "44"
324 magentaBackgroundColorAttr = "45"
325 cyanBackgroundColorAttr = "46"
326 whiteBackgroundColorAttr = "47"
327 extendedBackgroundColorAttr = "48"
328 defaultBackgroundColorAttr = "49"
329 extendedUnderlineColorAttr = "58"
330 defaultUnderlineColorAttr = "59"
331 brightBlackForegroundColorAttr = "90"
332 brightRedForegroundColorAttr = "91"
333 brightGreenForegroundColorAttr = "92"
334 brightYellowForegroundColorAttr = "93"
335 brightBlueForegroundColorAttr = "94"
336 brightMagentaForegroundColorAttr = "95"
337 brightCyanForegroundColorAttr = "96"
338 brightWhiteForegroundColorAttr = "97"
339 brightBlackBackgroundColorAttr = "100"
340 brightRedBackgroundColorAttr = "101"
341 brightGreenBackgroundColorAttr = "102"
342 brightYellowBackgroundColorAttr = "103"
343 brightBlueBackgroundColorAttr = "104"
344 brightMagentaBackgroundColorAttr = "105"
345 brightCyanBackgroundColorAttr = "106"
346 brightWhiteBackgroundColorAttr = "107"
347)
348
349// foregroundColorString returns the style SGR attribute for the given
350// foreground color.
351// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
352func foregroundColorString(c Color) string {
353 switch c := c.(type) {
354 case BasicColor:
355 // 3-bit or 4-bit ANSI foreground
356 // "3<n>" or "9<n>" where n is the color number from 0 to 7
357 switch c {
358 case Black:
359 return blackForegroundColorAttr
360 case Red:
361 return redForegroundColorAttr
362 case Green:
363 return greenForegroundColorAttr
364 case Yellow:
365 return yellowForegroundColorAttr
366 case Blue:
367 return blueForegroundColorAttr
368 case Magenta:
369 return magentaForegroundColorAttr
370 case Cyan:
371 return cyanForegroundColorAttr
372 case White:
373 return whiteForegroundColorAttr
374 case BrightBlack:
375 return brightBlackForegroundColorAttr
376 case BrightRed:
377 return brightRedForegroundColorAttr
378 case BrightGreen:
379 return brightGreenForegroundColorAttr
380 case BrightYellow:
381 return brightYellowForegroundColorAttr
382 case BrightBlue:
383 return brightBlueForegroundColorAttr
384 case BrightMagenta:
385 return brightMagentaForegroundColorAttr
386 case BrightCyan:
387 return brightCyanForegroundColorAttr
388 case BrightWhite:
389 return brightWhiteForegroundColorAttr
390 }
391 case ExtendedColor:
392 // 256-color ANSI foreground
393 // "38;5;<n>"
394 return "38;5;" + strconv.FormatUint(uint64(c), 10)
395 case TrueColor, color.Color:
396 // 24-bit "true color" foreground
397 // "38;2;<r>;<g>;<b>"
398 r, g, b, _ := c.RGBA()
399 return "38;2;" +
400 strconv.FormatUint(uint64(shift(r)), 10) + ";" +
401 strconv.FormatUint(uint64(shift(g)), 10) + ";" +
402 strconv.FormatUint(uint64(shift(b)), 10)
403 }
404 return defaultForegroundColorAttr
405}
406
407// backgroundColorString returns the style SGR attribute for the given
408// background color.
409// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
410func backgroundColorString(c Color) string {
411 switch c := c.(type) {
412 case BasicColor:
413 // 3-bit or 4-bit ANSI foreground
414 // "4<n>" or "10<n>" where n is the color number from 0 to 7
415 switch c {
416 case Black:
417 return blackBackgroundColorAttr
418 case Red:
419 return redBackgroundColorAttr
420 case Green:
421 return greenBackgroundColorAttr
422 case Yellow:
423 return yellowBackgroundColorAttr
424 case Blue:
425 return blueBackgroundColorAttr
426 case Magenta:
427 return magentaBackgroundColorAttr
428 case Cyan:
429 return cyanBackgroundColorAttr
430 case White:
431 return whiteBackgroundColorAttr
432 case BrightBlack:
433 return brightBlackBackgroundColorAttr
434 case BrightRed:
435 return brightRedBackgroundColorAttr
436 case BrightGreen:
437 return brightGreenBackgroundColorAttr
438 case BrightYellow:
439 return brightYellowBackgroundColorAttr
440 case BrightBlue:
441 return brightBlueBackgroundColorAttr
442 case BrightMagenta:
443 return brightMagentaBackgroundColorAttr
444 case BrightCyan:
445 return brightCyanBackgroundColorAttr
446 case BrightWhite:
447 return brightWhiteBackgroundColorAttr
448 }
449 case ExtendedColor:
450 // 256-color ANSI foreground
451 // "48;5;<n>"
452 return "48;5;" + strconv.FormatUint(uint64(c), 10)
453 case TrueColor, color.Color:
454 // 24-bit "true color" foreground
455 // "38;2;<r>;<g>;<b>"
456 r, g, b, _ := c.RGBA()
457 return "48;2;" +
458 strconv.FormatUint(uint64(shift(r)), 10) + ";" +
459 strconv.FormatUint(uint64(shift(g)), 10) + ";" +
460 strconv.FormatUint(uint64(shift(b)), 10)
461 }
462 return defaultBackgroundColorAttr
463}
464
465// underlineColorString returns the style SGR attribute for the given underline
466// color.
467// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
468func underlineColorString(c Color) string {
469 switch c := c.(type) {
470 // NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline
471 // color, use 256-color instead.
472 //
473 // 256-color ANSI underline color
474 // "58;5;<n>"
475 case BasicColor:
476 return "58;5;" + strconv.FormatUint(uint64(c), 10)
477 case ExtendedColor:
478 return "58;5;" + strconv.FormatUint(uint64(c), 10)
479 case TrueColor, color.Color:
480 // 24-bit "true color" foreground
481 // "38;2;<r>;<g>;<b>"
482 r, g, b, _ := c.RGBA()
483 return "58;2;" +
484 strconv.FormatUint(uint64(shift(r)), 10) + ";" +
485 strconv.FormatUint(uint64(shift(g)), 10) + ";" +
486 strconv.FormatUint(uint64(shift(b)), 10)
487 }
488 return defaultUnderlineColorAttr
489}
490
491// ReadStyleColor decodes a color from a slice of parameters. It returns the
492// number of parameters read and the color. This function is used to read SGR
493// color parameters following the ITU T.416 standard.
494//
495// It supports reading the following color types:
496// - 0: implementation defined
497// - 1: transparent
498// - 2: RGB direct color
499// - 3: CMY direct color
500// - 4: CMYK direct color
501// - 5: indexed color
502// - 6: RGBA direct color (WezTerm extension)
503//
504// The parameters can be separated by semicolons (;) or colons (:). Mixing
505// separators is not allowed.
506//
507// The specs supports defining a color space id, a color tolerance value, and a
508// tolerance color space id. However, these values have no effect on the
509// returned color and will be ignored.
510//
511// This implementation includes a few modifications to the specs:
512// 1. Support for legacy color values separated by semicolons (;) with respect to RGB, and indexed colors
513// 2. Support ignoring and omitting the color space id (second parameter) with respect to RGB colors
514// 3. Support ignoring and omitting the 6th parameter with respect to RGB and CMY colors
515// 4. Support reading RGBA colors
516func ReadStyleColor(params Params, co *color.Color) (n int) {
517 if len(params) < 2 { // Need at least SGR type and color type
518 return 0
519 }
520
521 // First parameter indicates one of 38, 48, or 58 (foreground, background, or underline)
522 s := params[0]
523 p := params[1]
524 colorType := p.Param(0)
525 n = 2
526
527 paramsfn := func() (p1, p2, p3, p4 int) {
528 // Where should we start reading the color?
529 switch {
530 case s.HasMore() && p.HasMore() && len(params) > 8 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore() && params[6].HasMore() && params[7].HasMore():
531 // We have color space id, a 6th parameter, a tolerance value, and a tolerance color space
532 n += 7
533 return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
534 case s.HasMore() && p.HasMore() && len(params) > 7 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore() && params[6].HasMore():
535 // We have color space id, a 6th parameter, and a tolerance value
536 n += 6
537 return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
538 case s.HasMore() && p.HasMore() && len(params) > 6 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && params[5].HasMore():
539 // We have color space id and a 6th parameter
540 // 48 : 4 : : 1 : 2 : 3 :4
541 n += 5
542 return params[3].Param(0), params[4].Param(0), params[5].Param(0), params[6].Param(0)
543 case s.HasMore() && p.HasMore() && len(params) > 5 && params[2].HasMore() && params[3].HasMore() && params[4].HasMore() && !params[5].HasMore():
544 // We have color space
545 // 48 : 3 : : 1 : 2 : 3
546 n += 4
547 return params[3].Param(0), params[4].Param(0), params[5].Param(0), -1
548 case s.HasMore() && p.HasMore() && p.Param(0) == 2 && params[2].HasMore() && params[3].HasMore() && !params[4].HasMore():
549 // We have color values separated by colons (:)
550 // 48 : 2 : 1 : 2 : 3
551 fallthrough
552 case !s.HasMore() && !p.HasMore() && p.Param(0) == 2 && !params[2].HasMore() && !params[3].HasMore() && !params[4].HasMore():
553 // Support legacy color values separated by semicolons (;)
554 // 48 ; 2 ; 1 ; 2 ; 3
555 n += 3
556 return params[2].Param(0), params[3].Param(0), params[4].Param(0), -1
557 }
558 // Ambiguous SGR color
559 return -1, -1, -1, -1
560 }
561
562 switch colorType {
563 case 0: // implementation defined
564 return 2
565 case 1: // transparent
566 *co = color.Transparent
567 return 2
568 case 2: // RGB direct color
569 if len(params) < 5 {
570 return 0
571 }
572
573 r, g, b, _ := paramsfn()
574 if r == -1 || g == -1 || b == -1 {
575 return 0
576 }
577
578 *co = color.RGBA{
579 R: uint8(r), //nolint:gosec
580 G: uint8(g), //nolint:gosec
581 B: uint8(b), //nolint:gosec
582 A: 0xff,
583 }
584 return
585
586 case 3: // CMY direct color
587 if len(params) < 5 {
588 return 0
589 }
590
591 c, m, y, _ := paramsfn()
592 if c == -1 || m == -1 || y == -1 {
593 return 0
594 }
595
596 *co = color.CMYK{
597 C: uint8(c), //nolint:gosec
598 M: uint8(m), //nolint:gosec
599 Y: uint8(y), //nolint:gosec
600 K: 0,
601 }
602 return
603
604 case 4: // CMYK direct color
605 if len(params) < 6 {
606 return 0
607 }
608
609 c, m, y, k := paramsfn()
610 if c == -1 || m == -1 || y == -1 || k == -1 {
611 return 0
612 }
613
614 *co = color.CMYK{
615 C: uint8(c), //nolint:gosec
616 M: uint8(m), //nolint:gosec
617 Y: uint8(y), //nolint:gosec
618 K: uint8(k), //nolint:gosec
619 }
620 return
621
622 case 5: // indexed color
623 if len(params) < 3 {
624 return 0
625 }
626 switch {
627 case s.HasMore() && p.HasMore() && !params[2].HasMore():
628 // Colon separated indexed color
629 // 38 : 5 : 234
630 case !s.HasMore() && !p.HasMore() && !params[2].HasMore():
631 // Legacy semicolon indexed color
632 // 38 ; 5 ; 234
633 default:
634 return 0
635 }
636 *co = ExtendedColor(params[2].Param(0)) //nolint:gosec
637 return 3
638
639 case 6: // RGBA direct color
640 if len(params) < 6 {
641 return 0
642 }
643
644 r, g, b, a := paramsfn()
645 if r == -1 || g == -1 || b == -1 || a == -1 {
646 return 0
647 }
648
649 *co = color.RGBA{
650 R: uint8(r), //nolint:gosec
651 G: uint8(g), //nolint:gosec
652 B: uint8(b), //nolint:gosec
653 A: uint8(a), //nolint:gosec
654 }
655 return
656
657 default:
658 return 0
659 }
660}