1package color
2
3import (
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
15var (
16	// NoColor defines if the output is colorized or not. It's dynamically set to
17	// false or true based on the stdout's file descriptor referring to a terminal
18	// or not. This is a global option and affects all colors. For more control
19	// over each color block use the methods DisableColor() individually.
20	NoColor = os.Getenv("TERM") == "dumb" ||
21		(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
22
23	// Output defines the standard output of the print functions. By default
24	// os.Stdout is used.
25	Output = colorable.NewColorableStdout()
26
27	// Error defines a color supporting writer for os.Stderr.
28	Error = colorable.NewColorableStderr()
29
30	// colorsCache is used to reduce the count of created Color objects and
31	// allows to reuse already created objects with required Attribute.
32	colorsCache   = make(map[Attribute]*Color)
33	colorsCacheMu sync.Mutex // protects colorsCache
34)
35
36// Color defines a custom color object which is defined by SGR parameters.
37type Color struct {
38	params  []Attribute
39	noColor *bool
40}
41
42// Attribute defines a single SGR Code
43type Attribute int
44
45const escape = "\x1b"
46
47// Base attributes
48const (
49	Reset Attribute = iota
50	Bold
51	Faint
52	Italic
53	Underline
54	BlinkSlow
55	BlinkRapid
56	ReverseVideo
57	Concealed
58	CrossedOut
59)
60
61// Foreground text colors
62const (
63	FgBlack Attribute = iota + 30
64	FgRed
65	FgGreen
66	FgYellow
67	FgBlue
68	FgMagenta
69	FgCyan
70	FgWhite
71)
72
73// Foreground Hi-Intensity text colors
74const (
75	FgHiBlack Attribute = iota + 90
76	FgHiRed
77	FgHiGreen
78	FgHiYellow
79	FgHiBlue
80	FgHiMagenta
81	FgHiCyan
82	FgHiWhite
83)
84
85// Background text colors
86const (
87	BgBlack Attribute = iota + 40
88	BgRed
89	BgGreen
90	BgYellow
91	BgBlue
92	BgMagenta
93	BgCyan
94	BgWhite
95)
96
97// Background Hi-Intensity text colors
98const (
99	BgHiBlack Attribute = iota + 100
100	BgHiRed
101	BgHiGreen
102	BgHiYellow
103	BgHiBlue
104	BgHiMagenta
105	BgHiCyan
106	BgHiWhite
107)
108
109// New returns a newly created color object.
110func New(value ...Attribute) *Color {
111	c := &Color{params: make([]Attribute, 0)}
112	c.Add(value...)
113	return c
114}
115
116// Set sets the given parameters immediately. It will change the color of
117// output with the given SGR parameters until color.Unset() is called.
118func Set(p ...Attribute) *Color {
119	c := New(p...)
120	c.Set()
121	return c
122}
123
124// Unset resets all escape attributes and clears the output. Usually should
125// be called after Set().
126func Unset() {
127	if NoColor {
128		return
129	}
130
131	fmt.Fprintf(Output, "%s[%dm", escape, Reset)
132}
133
134// Set sets the SGR sequence.
135func (c *Color) Set() *Color {
136	if c.isNoColorSet() {
137		return c
138	}
139
140	fmt.Fprintf(Output, c.format())
141	return c
142}
143
144func (c *Color) unset() {
145	if c.isNoColorSet() {
146		return
147	}
148
149	Unset()
150}
151
152func (c *Color) setWriter(w io.Writer) *Color {
153	if c.isNoColorSet() {
154		return c
155	}
156
157	fmt.Fprintf(w, c.format())
158	return c
159}
160
161func (c *Color) unsetWriter(w io.Writer) {
162	if c.isNoColorSet() {
163		return
164	}
165
166	if NoColor {
167		return
168	}
169
170	fmt.Fprintf(w, "%s[%dm", escape, Reset)
171}
172
173// Add is used to chain SGR parameters. Use as many as parameters to combine
174// and create custom color objects. Example: Add(color.FgRed, color.Underline).
175func (c *Color) Add(value ...Attribute) *Color {
176	c.params = append(c.params, value...)
177	return c
178}
179
180func (c *Color) prepend(value Attribute) {
181	c.params = append(c.params, 0)
182	copy(c.params[1:], c.params[0:])
183	c.params[0] = value
184}
185
186// Fprint formats using the default formats for its operands and writes to w.
187// Spaces are added between operands when neither is a string.
188// It returns the number of bytes written and any write error encountered.
189// On Windows, users should wrap w with colorable.NewColorable() if w is of
190// type *os.File.
191func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
192	c.setWriter(w)
193	defer c.unsetWriter(w)
194
195	return fmt.Fprint(w, a...)
196}
197
198// Print formats using the default formats for its operands and writes to
199// standard output. Spaces are added between operands when neither is a
200// string. It returns the number of bytes written and any write error
201// encountered. This is the standard fmt.Print() method wrapped with the given
202// color.
203func (c *Color) Print(a ...interface{}) (n int, err error) {
204	c.Set()
205	defer c.unset()
206
207	return fmt.Fprint(Output, a...)
208}
209
210// Fprintf formats according to a format specifier and writes to w.
211// It returns the number of bytes written and any write error encountered.
212// On Windows, users should wrap w with colorable.NewColorable() if w is of
213// type *os.File.
214func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
215	c.setWriter(w)
216	defer c.unsetWriter(w)
217
218	return fmt.Fprintf(w, format, a...)
219}
220
221// Printf formats according to a format specifier and writes to standard output.
222// It returns the number of bytes written and any write error encountered.
223// This is the standard fmt.Printf() method wrapped with the given color.
224func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
225	c.Set()
226	defer c.unset()
227
228	return fmt.Fprintf(Output, format, a...)
229}
230
231// Fprintln formats using the default formats for its operands and writes to w.
232// Spaces are always added between operands and a newline is appended.
233// On Windows, users should wrap w with colorable.NewColorable() if w is of
234// type *os.File.
235func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
236	c.setWriter(w)
237	defer c.unsetWriter(w)
238
239	return fmt.Fprintln(w, a...)
240}
241
242// Println formats using the default formats for its operands and writes to
243// standard output. Spaces are always added between operands and a newline is
244// appended. It returns the number of bytes written and any write error
245// encountered. This is the standard fmt.Print() method wrapped with the given
246// color.
247func (c *Color) Println(a ...interface{}) (n int, err error) {
248	c.Set()
249	defer c.unset()
250
251	return fmt.Fprintln(Output, a...)
252}
253
254// Sprint is just like Print, but returns a string instead of printing it.
255func (c *Color) Sprint(a ...interface{}) string {
256	return c.wrap(fmt.Sprint(a...))
257}
258
259// Sprintln is just like Println, but returns a string instead of printing it.
260func (c *Color) Sprintln(a ...interface{}) string {
261	return c.wrap(fmt.Sprintln(a...))
262}
263
264// Sprintf is just like Printf, but returns a string instead of printing it.
265func (c *Color) Sprintf(format string, a ...interface{}) string {
266	return c.wrap(fmt.Sprintf(format, a...))
267}
268
269// FprintFunc returns a new function that prints the passed arguments as
270// colorized with color.Fprint().
271func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
272	return func(w io.Writer, a ...interface{}) {
273		c.Fprint(w, a...)
274	}
275}
276
277// PrintFunc returns a new function that prints the passed arguments as
278// colorized with color.Print().
279func (c *Color) PrintFunc() func(a ...interface{}) {
280	return func(a ...interface{}) {
281		c.Print(a...)
282	}
283}
284
285// FprintfFunc returns a new function that prints the passed arguments as
286// colorized with color.Fprintf().
287func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
288	return func(w io.Writer, format string, a ...interface{}) {
289		c.Fprintf(w, format, a...)
290	}
291}
292
293// PrintfFunc returns a new function that prints the passed arguments as
294// colorized with color.Printf().
295func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
296	return func(format string, a ...interface{}) {
297		c.Printf(format, a...)
298	}
299}
300
301// FprintlnFunc returns a new function that prints the passed arguments as
302// colorized with color.Fprintln().
303func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
304	return func(w io.Writer, a ...interface{}) {
305		c.Fprintln(w, a...)
306	}
307}
308
309// PrintlnFunc returns a new function that prints the passed arguments as
310// colorized with color.Println().
311func (c *Color) PrintlnFunc() func(a ...interface{}) {
312	return func(a ...interface{}) {
313		c.Println(a...)
314	}
315}
316
317// SprintFunc returns a new function that returns colorized strings for the
318// given arguments with fmt.Sprint(). Useful to put into or mix into other
319// string. Windows users should use this in conjunction with color.Output, example:
320//
321//	put := New(FgYellow).SprintFunc()
322//	fmt.Fprintf(color.Output, "This is a %s", put("warning"))
323func (c *Color) SprintFunc() func(a ...interface{}) string {
324	return func(a ...interface{}) string {
325		return c.wrap(fmt.Sprint(a...))
326	}
327}
328
329// SprintfFunc returns a new function that returns colorized strings for the
330// given arguments with fmt.Sprintf(). Useful to put into or mix into other
331// string. Windows users should use this in conjunction with color.Output.
332func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
333	return func(format string, a ...interface{}) string {
334		return c.wrap(fmt.Sprintf(format, a...))
335	}
336}
337
338// SprintlnFunc returns a new function that returns colorized strings for the
339// given arguments with fmt.Sprintln(). Useful to put into or mix into other
340// string. Windows users should use this in conjunction with color.Output.
341func (c *Color) SprintlnFunc() func(a ...interface{}) string {
342	return func(a ...interface{}) string {
343		return c.wrap(fmt.Sprintln(a...))
344	}
345}
346
347// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
348// an example output might be: "1;36" -> bold cyan
349func (c *Color) sequence() string {
350	format := make([]string, len(c.params))
351	for i, v := range c.params {
352		format[i] = strconv.Itoa(int(v))
353	}
354
355	return strings.Join(format, ";")
356}
357
358// wrap wraps the s string with the colors attributes. The string is ready to
359// be printed.
360func (c *Color) wrap(s string) string {
361	if c.isNoColorSet() {
362		return s
363	}
364
365	return c.format() + s + c.unformat()
366}
367
368func (c *Color) format() string {
369	return fmt.Sprintf("%s[%sm", escape, c.sequence())
370}
371
372func (c *Color) unformat() string {
373	return fmt.Sprintf("%s[%dm", escape, Reset)
374}
375
376// DisableColor disables the color output. Useful to not change any existing
377// code and still being able to output. Can be used for flags like
378// "--no-color". To enable back use EnableColor() method.
379func (c *Color) DisableColor() {
380	c.noColor = boolPtr(true)
381}
382
383// EnableColor enables the color output. Use it in conjunction with
384// DisableColor(). Otherwise this method has no side effects.
385func (c *Color) EnableColor() {
386	c.noColor = boolPtr(false)
387}
388
389func (c *Color) isNoColorSet() bool {
390	// check first if we have user setted action
391	if c.noColor != nil {
392		return *c.noColor
393	}
394
395	// if not return the global option, which is disabled by default
396	return NoColor
397}
398
399// Equals returns a boolean value indicating whether two colors are equal.
400func (c *Color) Equals(c2 *Color) bool {
401	if len(c.params) != len(c2.params) {
402		return false
403	}
404
405	for _, attr := range c.params {
406		if !c2.attrExists(attr) {
407			return false
408		}
409	}
410
411	return true
412}
413
414func (c *Color) attrExists(a Attribute) bool {
415	for _, attr := range c.params {
416		if attr == a {
417			return true
418		}
419	}
420
421	return false
422}
423
424func boolPtr(v bool) *bool {
425	return &v
426}
427
428func getCachedColor(p Attribute) *Color {
429	colorsCacheMu.Lock()
430	defer colorsCacheMu.Unlock()
431
432	c, ok := colorsCache[p]
433	if !ok {
434		c = New(p)
435		colorsCache[p] = c
436	}
437
438	return c
439}
440
441func colorPrint(format string, p Attribute, a ...interface{}) {
442	c := getCachedColor(p)
443
444	if !strings.HasSuffix(format, "\n") {
445		format += "\n"
446	}
447
448	if len(a) == 0 {
449		c.Print(format)
450	} else {
451		c.Printf(format, a...)
452	}
453}
454
455func colorString(format string, p Attribute, a ...interface{}) string {
456	c := getCachedColor(p)
457
458	if len(a) == 0 {
459		return c.SprintFunc()(format)
460	}
461
462	return c.SprintfFunc()(format, a...)
463}
464
465// Black is a convenient helper function to print with black foreground. A
466// newline is appended to format by default.
467func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
468
469// Red is a convenient helper function to print with red foreground. A
470// newline is appended to format by default.
471func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
472
473// Green is a convenient helper function to print with green foreground. A
474// newline is appended to format by default.
475func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
476
477// Yellow is a convenient helper function to print with yellow foreground.
478// A newline is appended to format by default.
479func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
480
481// Blue is a convenient helper function to print with blue foreground. A
482// newline is appended to format by default.
483func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
484
485// Magenta is a convenient helper function to print with magenta foreground.
486// A newline is appended to format by default.
487func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
488
489// Cyan is a convenient helper function to print with cyan foreground. A
490// newline is appended to format by default.
491func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
492
493// White is a convenient helper function to print with white foreground. A
494// newline is appended to format by default.
495func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
496
497// BlackString is a convenient helper function to return a string with black
498// foreground.
499func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
500
501// RedString is a convenient helper function to return a string with red
502// foreground.
503func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
504
505// GreenString is a convenient helper function to return a string with green
506// foreground.
507func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
508
509// YellowString is a convenient helper function to return a string with yellow
510// foreground.
511func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
512
513// BlueString is a convenient helper function to return a string with blue
514// foreground.
515func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
516
517// MagentaString is a convenient helper function to return a string with magenta
518// foreground.
519func MagentaString(format string, a ...interface{}) string {
520	return colorString(format, FgMagenta, a...)
521}
522
523// CyanString is a convenient helper function to return a string with cyan
524// foreground.
525func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
526
527// WhiteString is a convenient helper function to return a string with white
528// foreground.
529func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
530
531// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
532// newline is appended to format by default.
533func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
534
535// HiRed is a convenient helper function to print with hi-intensity red foreground. A
536// newline is appended to format by default.
537func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
538
539// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
540// newline is appended to format by default.
541func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
542
543// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
544// A newline is appended to format by default.
545func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
546
547// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
548// newline is appended to format by default.
549func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
550
551// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
552// A newline is appended to format by default.
553func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
554
555// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
556// newline is appended to format by default.
557func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
558
559// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
560// newline is appended to format by default.
561func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
562
563// HiBlackString is a convenient helper function to return a string with hi-intensity black
564// foreground.
565func HiBlackString(format string, a ...interface{}) string {
566	return colorString(format, FgHiBlack, a...)
567}
568
569// HiRedString is a convenient helper function to return a string with hi-intensity red
570// foreground.
571func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
572
573// HiGreenString is a convenient helper function to return a string with hi-intensity green
574// foreground.
575func HiGreenString(format string, a ...interface{}) string {
576	return colorString(format, FgHiGreen, a...)
577}
578
579// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
580// foreground.
581func HiYellowString(format string, a ...interface{}) string {
582	return colorString(format, FgHiYellow, a...)
583}
584
585// HiBlueString is a convenient helper function to return a string with hi-intensity blue
586// foreground.
587func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
588
589// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
590// foreground.
591func HiMagentaString(format string, a ...interface{}) string {
592	return colorString(format, FgHiMagenta, a...)
593}
594
595// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
596// foreground.
597func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
598
599// HiWhiteString is a convenient helper function to return a string with hi-intensity white
600// foreground.
601func HiWhiteString(format string, a ...interface{}) string {
602	return colorString(format, FgHiWhite, a...)
603}
604