1 package color
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "strconv"
8 "strings"
9 "sync"
10
11 "github.com/mattn/go-colorable"
12 "github.com/mattn/go-isatty"
13 )
14
15 var (
16
17
18
19
20
21
22 NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" ||
23 (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
24
25
26
27 Output = colorable.NewColorableStdout()
28
29
30 Error = colorable.NewColorableStderr()
31
32
33
34 colorsCache = make(map[Attribute]*Color)
35 colorsCacheMu sync.Mutex
36 )
37
38
39 func noColorIsSet() bool {
40 return os.Getenv("NO_COLOR") != ""
41 }
42
43
44 type Color struct {
45 params []Attribute
46 noColor *bool
47 }
48
49
50 type Attribute int
51
52 const escape = "\x1b"
53
54
55 const (
56 Reset Attribute = iota
57 Bold
58 Faint
59 Italic
60 Underline
61 BlinkSlow
62 BlinkRapid
63 ReverseVideo
64 Concealed
65 CrossedOut
66 )
67
68 const (
69 ResetBold Attribute = iota + 22
70 ResetItalic
71 ResetUnderline
72 ResetBlinking
73 _
74 ResetReversed
75 ResetConcealed
76 ResetCrossedOut
77 )
78
79 var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
80 Bold: ResetBold,
81 Faint: ResetBold,
82 Italic: ResetItalic,
83 Underline: ResetUnderline,
84 BlinkSlow: ResetBlinking,
85 BlinkRapid: ResetBlinking,
86 ReverseVideo: ResetReversed,
87 Concealed: ResetConcealed,
88 CrossedOut: ResetCrossedOut,
89 }
90
91
92 const (
93 FgBlack Attribute = iota + 30
94 FgRed
95 FgGreen
96 FgYellow
97 FgBlue
98 FgMagenta
99 FgCyan
100 FgWhite
101 )
102
103
104 const (
105 FgHiBlack Attribute = iota + 90
106 FgHiRed
107 FgHiGreen
108 FgHiYellow
109 FgHiBlue
110 FgHiMagenta
111 FgHiCyan
112 FgHiWhite
113 )
114
115
116 const (
117 BgBlack Attribute = iota + 40
118 BgRed
119 BgGreen
120 BgYellow
121 BgBlue
122 BgMagenta
123 BgCyan
124 BgWhite
125 )
126
127
128 const (
129 BgHiBlack Attribute = iota + 100
130 BgHiRed
131 BgHiGreen
132 BgHiYellow
133 BgHiBlue
134 BgHiMagenta
135 BgHiCyan
136 BgHiWhite
137 )
138
139
140 func New(value ...Attribute) *Color {
141 c := &Color{
142 params: make([]Attribute, 0),
143 }
144
145 if noColorIsSet() {
146 c.noColor = boolPtr(true)
147 }
148
149 c.Add(value...)
150 return c
151 }
152
153
154
155 func Set(p ...Attribute) *Color {
156 c := New(p...)
157 c.Set()
158 return c
159 }
160
161
162
163 func Unset() {
164 if NoColor {
165 return
166 }
167
168 fmt.Fprintf(Output, "%s[%dm", escape, Reset)
169 }
170
171
172 func (c *Color) Set() *Color {
173 if c.isNoColorSet() {
174 return c
175 }
176
177 fmt.Fprint(Output, c.format())
178 return c
179 }
180
181 func (c *Color) unset() {
182 if c.isNoColorSet() {
183 return
184 }
185
186 Unset()
187 }
188
189
190
191
192 func (c *Color) SetWriter(w io.Writer) *Color {
193 if c.isNoColorSet() {
194 return c
195 }
196
197 fmt.Fprint(w, c.format())
198 return c
199 }
200
201
202
203 func (c *Color) UnsetWriter(w io.Writer) {
204 if c.isNoColorSet() {
205 return
206 }
207
208 if NoColor {
209 return
210 }
211
212 fmt.Fprintf(w, "%s[%dm", escape, Reset)
213 }
214
215
216
217 func (c *Color) Add(value ...Attribute) *Color {
218 c.params = append(c.params, value...)
219 return c
220 }
221
222
223
224
225
226
227 func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
228 c.SetWriter(w)
229 defer c.UnsetWriter(w)
230
231 return fmt.Fprint(w, a...)
232 }
233
234
235
236
237
238
239 func (c *Color) Print(a ...interface{}) (n int, err error) {
240 c.Set()
241 defer c.unset()
242
243 return fmt.Fprint(Output, a...)
244 }
245
246
247
248
249
250 func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
251 c.SetWriter(w)
252 defer c.UnsetWriter(w)
253
254 return fmt.Fprintf(w, format, a...)
255 }
256
257
258
259
260 func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
261 c.Set()
262 defer c.unset()
263
264 return fmt.Fprintf(Output, format, a...)
265 }
266
267
268
269
270
271 func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
272 return fmt.Fprintln(w, c.wrap(fmt.Sprint(a...)))
273 }
274
275
276
277
278
279
280 func (c *Color) Println(a ...interface{}) (n int, err error) {
281 return fmt.Fprintln(Output, c.wrap(fmt.Sprint(a...)))
282 }
283
284
285 func (c *Color) Sprint(a ...interface{}) string {
286 return c.wrap(fmt.Sprint(a...))
287 }
288
289
290 func (c *Color) Sprintln(a ...interface{}) string {
291 return fmt.Sprintln(c.Sprint(a...))
292 }
293
294
295 func (c *Color) Sprintf(format string, a ...interface{}) string {
296 return c.wrap(fmt.Sprintf(format, a...))
297 }
298
299
300
301 func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
302 return func(w io.Writer, a ...interface{}) {
303 c.Fprint(w, a...)
304 }
305 }
306
307
308
309 func (c *Color) PrintFunc() func(a ...interface{}) {
310 return func(a ...interface{}) {
311 c.Print(a...)
312 }
313 }
314
315
316
317 func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
318 return func(w io.Writer, format string, a ...interface{}) {
319 c.Fprintf(w, format, a...)
320 }
321 }
322
323
324
325 func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
326 return func(format string, a ...interface{}) {
327 c.Printf(format, a...)
328 }
329 }
330
331
332
333 func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
334 return func(w io.Writer, a ...interface{}) {
335 c.Fprintln(w, a...)
336 }
337 }
338
339
340
341 func (c *Color) PrintlnFunc() func(a ...interface{}) {
342 return func(a ...interface{}) {
343 c.Println(a...)
344 }
345 }
346
347
348
349
350
351
352
353 func (c *Color) SprintFunc() func(a ...interface{}) string {
354 return func(a ...interface{}) string {
355 return c.wrap(fmt.Sprint(a...))
356 }
357 }
358
359
360
361
362 func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
363 return func(format string, a ...interface{}) string {
364 return c.wrap(fmt.Sprintf(format, a...))
365 }
366 }
367
368
369
370
371 func (c *Color) SprintlnFunc() func(a ...interface{}) string {
372 return func(a ...interface{}) string {
373 return fmt.Sprintln(c.Sprint(a...))
374 }
375 }
376
377
378
379 func (c *Color) sequence() string {
380 format := make([]string, len(c.params))
381 for i, v := range c.params {
382 format[i] = strconv.Itoa(int(v))
383 }
384
385 return strings.Join(format, ";")
386 }
387
388
389
390 func (c *Color) wrap(s string) string {
391 if c.isNoColorSet() {
392 return s
393 }
394
395 return c.format() + s + c.unformat()
396 }
397
398 func (c *Color) format() string {
399 return fmt.Sprintf("%s[%sm", escape, c.sequence())
400 }
401
402 func (c *Color) unformat() string {
403
404
405 format := make([]string, len(c.params))
406 for i, v := range c.params {
407 format[i] = strconv.Itoa(int(Reset))
408 ra, ok := mapResetAttributes[v]
409 if ok {
410 format[i] = strconv.Itoa(int(ra))
411 }
412 }
413
414 return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
415 }
416
417
418
419
420 func (c *Color) DisableColor() {
421 c.noColor = boolPtr(true)
422 }
423
424
425
426 func (c *Color) EnableColor() {
427 c.noColor = boolPtr(false)
428 }
429
430 func (c *Color) isNoColorSet() bool {
431
432 if c.noColor != nil {
433 return *c.noColor
434 }
435
436
437 return NoColor
438 }
439
440
441 func (c *Color) Equals(c2 *Color) bool {
442 if c == nil && c2 == nil {
443 return true
444 }
445 if c == nil || c2 == nil {
446 return false
447 }
448 if len(c.params) != len(c2.params) {
449 return false
450 }
451
452 for _, attr := range c.params {
453 if !c2.attrExists(attr) {
454 return false
455 }
456 }
457
458 return true
459 }
460
461 func (c *Color) attrExists(a Attribute) bool {
462 for _, attr := range c.params {
463 if attr == a {
464 return true
465 }
466 }
467
468 return false
469 }
470
471 func boolPtr(v bool) *bool {
472 return &v
473 }
474
475 func getCachedColor(p Attribute) *Color {
476 colorsCacheMu.Lock()
477 defer colorsCacheMu.Unlock()
478
479 c, ok := colorsCache[p]
480 if !ok {
481 c = New(p)
482 colorsCache[p] = c
483 }
484
485 return c
486 }
487
488 func colorPrint(format string, p Attribute, a ...interface{}) {
489 c := getCachedColor(p)
490
491 if !strings.HasSuffix(format, "\n") {
492 format += "\n"
493 }
494
495 if len(a) == 0 {
496 c.Print(format)
497 } else {
498 c.Printf(format, a...)
499 }
500 }
501
502 func colorString(format string, p Attribute, a ...interface{}) string {
503 c := getCachedColor(p)
504
505 if len(a) == 0 {
506 return c.SprintFunc()(format)
507 }
508
509 return c.SprintfFunc()(format, a...)
510 }
511
512
513
514 func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
515
516
517
518 func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
519
520
521
522 func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
523
524
525
526 func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
527
528
529
530 func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
531
532
533
534 func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
535
536
537
538 func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
539
540
541
542 func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
543
544
545
546 func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
547
548
549
550 func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
551
552
553
554 func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
555
556
557
558 func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
559
560
561
562 func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
563
564
565
566 func MagentaString(format string, a ...interface{}) string {
567 return colorString(format, FgMagenta, a...)
568 }
569
570
571
572 func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
573
574
575
576 func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
577
578
579
580 func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
581
582
583
584 func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
585
586
587
588 func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
589
590
591
592 func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
593
594
595
596 func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
597
598
599
600 func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
601
602
603
604 func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
605
606
607
608 func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
609
610
611
612 func HiBlackString(format string, a ...interface{}) string {
613 return colorString(format, FgHiBlack, a...)
614 }
615
616
617
618 func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
619
620
621
622 func HiGreenString(format string, a ...interface{}) string {
623 return colorString(format, FgHiGreen, a...)
624 }
625
626
627
628 func HiYellowString(format string, a ...interface{}) string {
629 return colorString(format, FgHiYellow, a...)
630 }
631
632
633
634 func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
635
636
637
638 func HiMagentaString(format string, a ...interface{}) string {
639 return colorString(format, FgHiMagenta, a...)
640 }
641
642
643
644 func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
645
646
647
648 func HiWhiteString(format string, a ...interface{}) string {
649 return colorString(format, FgHiWhite, a...)
650 }
651
View as plain text