123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- package color
- import (
- "fmt"
- "io"
- "os"
- "strconv"
- "strings"
- "sync"
- "github.com/mattn/go-colorable"
- "github.com/mattn/go-isatty"
- )
- var (
- // NoColor defines if the output is colorized or not. It's dynamically set to
- // false or true based on the stdout's file descriptor referring to a terminal
- // or not. This is a global option and affects all colors. For more control
- // over each color block use the methods DisableColor() individually.
- NoColor = !isatty.IsTerminal(os.Stdout.Fd()) || os.Getenv("TERM") == "dumb"
- // Output defines the standard output of the print functions. By default
- // os.Stdout is used.
- Output = colorable.NewColorableStdout()
- // colorsCache is used to reduce the count of created Color objects and
- // allows to reuse already created objects with required Attribute.
- colorsCache = make(map[Attribute]*Color)
- colorsCacheMu sync.Mutex // protects colorsCache
- )
- // Color defines a custom color object which is defined by SGR parameters.
- type Color struct {
- params []Attribute
- noColor *bool
- }
- // Attribute defines a single SGR Code
- type Attribute int
- const escape = "\x1b"
- // Base attributes
- const (
- Reset Attribute = iota
- Bold
- Faint
- Italic
- Underline
- BlinkSlow
- BlinkRapid
- ReverseVideo
- Concealed
- CrossedOut
- )
- // Foreground text colors
- const (
- FgBlack Attribute = iota + 30
- FgRed
- FgGreen
- FgYellow
- FgBlue
- FgMagenta
- FgCyan
- FgWhite
- )
- // Foreground Hi-Intensity text colors
- const (
- FgHiBlack Attribute = iota + 90
- FgHiRed
- FgHiGreen
- FgHiYellow
- FgHiBlue
- FgHiMagenta
- FgHiCyan
- FgHiWhite
- )
- // Background text colors
- const (
- BgBlack Attribute = iota + 40
- BgRed
- BgGreen
- BgYellow
- BgBlue
- BgMagenta
- BgCyan
- BgWhite
- )
- // Background Hi-Intensity text colors
- const (
- BgHiBlack Attribute = iota + 100
- BgHiRed
- BgHiGreen
- BgHiYellow
- BgHiBlue
- BgHiMagenta
- BgHiCyan
- BgHiWhite
- )
- // New returns a newly created color object.
- func New(value ...Attribute) *Color {
- c := &Color{params: make([]Attribute, 0)}
- c.Add(value...)
- return c
- }
- // Set sets the given parameters immediately. It will change the color of
- // output with the given SGR parameters until color.Unset() is called.
- func Set(p ...Attribute) *Color {
- c := New(p...)
- c.Set()
- return c
- }
- // Unset resets all escape attributes and clears the output. Usually should
- // be called after Set().
- func Unset() {
- if NoColor {
- return
- }
- fmt.Fprintf(Output, "%s[%dm", escape, Reset)
- }
- // Set sets the SGR sequence.
- func (c *Color) Set() *Color {
- if c.isNoColorSet() {
- return c
- }
- fmt.Fprintf(Output, c.format())
- return c
- }
- func (c *Color) unset() {
- if c.isNoColorSet() {
- return
- }
- Unset()
- }
- func (c *Color) setWriter(w io.Writer) *Color {
- if c.isNoColorSet() {
- return c
- }
- fmt.Fprintf(w, c.format())
- return c
- }
- func (c *Color) unsetWriter(w io.Writer) {
- if c.isNoColorSet() {
- return
- }
- if NoColor {
- return
- }
- fmt.Fprintf(w, "%s[%dm", escape, Reset)
- }
- // Add is used to chain SGR parameters. Use as many as parameters to combine
- // and create custom color objects. Example: Add(color.FgRed, color.Underline).
- func (c *Color) Add(value ...Attribute) *Color {
- c.params = append(c.params, value...)
- return c
- }
- func (c *Color) prepend(value Attribute) {
- c.params = append(c.params, 0)
- copy(c.params[1:], c.params[0:])
- c.params[0] = value
- }
- // Fprint formats using the default formats for its operands and writes to w.
- // Spaces are added between operands when neither is a string.
- // It returns the number of bytes written and any write error encountered.
- // On Windows, users should wrap w with colorable.NewColorable() if w is of
- // type *os.File.
- func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
- c.setWriter(w)
- defer c.unsetWriter(w)
- return fmt.Fprint(w, a...)
- }
- // Print formats using the default formats for its operands and writes to
- // standard output. Spaces are added between operands when neither is a
- // string. It returns the number of bytes written and any write error
- // encountered. This is the standard fmt.Print() method wrapped with the given
- // color.
- func (c *Color) Print(a ...interface{}) (n int, err error) {
- c.Set()
- defer c.unset()
- return fmt.Fprint(Output, a...)
- }
- // Fprintf formats according to a format specifier and writes to w.
- // It returns the number of bytes written and any write error encountered.
- // On Windows, users should wrap w with colorable.NewColorable() if w is of
- // type *os.File.
- func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
- c.setWriter(w)
- defer c.unsetWriter(w)
- return fmt.Fprintf(w, format, a...)
- }
- // Printf formats according to a format specifier and writes to standard output.
- // It returns the number of bytes written and any write error encountered.
- // This is the standard fmt.Printf() method wrapped with the given color.
- func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
- c.Set()
- defer c.unset()
- return fmt.Fprintf(Output, format, a...)
- }
- // Fprintln formats using the default formats for its operands and writes to w.
- // Spaces are always added between operands and a newline is appended.
- // On Windows, users should wrap w with colorable.NewColorable() if w is of
- // type *os.File.
- func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
- c.setWriter(w)
- defer c.unsetWriter(w)
- return fmt.Fprintln(w, a...)
- }
- // Println formats using the default formats for its operands and writes to
- // standard output. Spaces are always added between operands and a newline is
- // appended. It returns the number of bytes written and any write error
- // encountered. This is the standard fmt.Print() method wrapped with the given
- // color.
- func (c *Color) Println(a ...interface{}) (n int, err error) {
- c.Set()
- defer c.unset()
- return fmt.Fprintln(Output, a...)
- }
- // FprintFunc returns a new function that prints the passed arguments as
- // colorized with color.Fprint().
- func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
- return func(w io.Writer, a ...interface{}) {
- c.Fprint(w, a...)
- }
- }
- // PrintFunc returns a new function that prints the passed arguments as
- // colorized with color.Print().
- func (c *Color) PrintFunc() func(a ...interface{}) {
- return func(a ...interface{}) {
- c.Print(a...)
- }
- }
- // FprintfFunc returns a new function that prints the passed arguments as
- // colorized with color.Fprintf().
- func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
- return func(w io.Writer, format string, a ...interface{}) {
- c.Fprintf(w, format, a...)
- }
- }
- // PrintfFunc returns a new function that prints the passed arguments as
- // colorized with color.Printf().
- func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
- return func(format string, a ...interface{}) {
- c.Printf(format, a...)
- }
- }
- // FprintlnFunc returns a new function that prints the passed arguments as
- // colorized with color.Fprintln().
- func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
- return func(w io.Writer, a ...interface{}) {
- c.Fprintln(w, a...)
- }
- }
- // PrintlnFunc returns a new function that prints the passed arguments as
- // colorized with color.Println().
- func (c *Color) PrintlnFunc() func(a ...interface{}) {
- return func(a ...interface{}) {
- c.Println(a...)
- }
- }
- // SprintFunc returns a new function that returns colorized strings for the
- // given arguments with fmt.Sprint(). Useful to put into or mix into other
- // string. Windows users should use this in conjunction with color.Output, example:
- //
- // put := New(FgYellow).SprintFunc()
- // fmt.Fprintf(color.Output, "This is a %s", put("warning"))
- func (c *Color) SprintFunc() func(a ...interface{}) string {
- return func(a ...interface{}) string {
- return c.wrap(fmt.Sprint(a...))
- }
- }
- // SprintfFunc returns a new function that returns colorized strings for the
- // given arguments with fmt.Sprintf(). Useful to put into or mix into other
- // string. Windows users should use this in conjunction with color.Output.
- func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
- return func(format string, a ...interface{}) string {
- return c.wrap(fmt.Sprintf(format, a...))
- }
- }
- // SprintlnFunc returns a new function that returns colorized strings for the
- // given arguments with fmt.Sprintln(). Useful to put into or mix into other
- // string. Windows users should use this in conjunction with color.Output.
- func (c *Color) SprintlnFunc() func(a ...interface{}) string {
- return func(a ...interface{}) string {
- return c.wrap(fmt.Sprintln(a...))
- }
- }
- // sequence returns a formated SGR sequence to be plugged into a "\x1b[...m"
- // an example output might be: "1;36" -> bold cyan
- func (c *Color) sequence() string {
- format := make([]string, len(c.params))
- for i, v := range c.params {
- format[i] = strconv.Itoa(int(v))
- }
- return strings.Join(format, ";")
- }
- // wrap wraps the s string with the colors attributes. The string is ready to
- // be printed.
- func (c *Color) wrap(s string) string {
- if c.isNoColorSet() {
- return s
- }
- return c.format() + s + c.unformat()
- }
- func (c *Color) format() string {
- return fmt.Sprintf("%s[%sm", escape, c.sequence())
- }
- func (c *Color) unformat() string {
- return fmt.Sprintf("%s[%dm", escape, Reset)
- }
- // DisableColor disables the color output. Useful to not change any existing
- // code and still being able to output. Can be used for flags like
- // "--no-color". To enable back use EnableColor() method.
- func (c *Color) DisableColor() {
- c.noColor = boolPtr(true)
- }
- // EnableColor enables the color output. Use it in conjunction with
- // DisableColor(). Otherwise this method has no side effects.
- func (c *Color) EnableColor() {
- c.noColor = boolPtr(false)
- }
- func (c *Color) isNoColorSet() bool {
- // check first if we have user setted action
- if c.noColor != nil {
- return *c.noColor
- }
- // if not return the global option, which is disabled by default
- return NoColor
- }
- // Equals returns a boolean value indicating whether two colors are equal.
- func (c *Color) Equals(c2 *Color) bool {
- if len(c.params) != len(c2.params) {
- return false
- }
- for _, attr := range c.params {
- if !c2.attrExists(attr) {
- return false
- }
- }
- return true
- }
- func (c *Color) attrExists(a Attribute) bool {
- for _, attr := range c.params {
- if attr == a {
- return true
- }
- }
- return false
- }
- func boolPtr(v bool) *bool {
- return &v
- }
- func getCachedColor(p Attribute) *Color {
- colorsCacheMu.Lock()
- defer colorsCacheMu.Unlock()
- c, ok := colorsCache[p]
- if !ok {
- c = New(p)
- colorsCache[p] = c
- }
- return c
- }
- func colorPrint(format string, p Attribute, a ...interface{}) {
- c := getCachedColor(p)
- if !strings.HasSuffix(format, "\n") {
- format += "\n"
- }
- if len(a) == 0 {
- c.Print(format)
- } else {
- c.Printf(format, a...)
- }
- }
- func colorString(format string, p Attribute, a ...interface{}) string {
- c := getCachedColor(p)
- if len(a) == 0 {
- return c.SprintFunc()(format)
- }
- return c.SprintfFunc()(format, a...)
- }
- // Black is an convenient helper function to print with black foreground. A
- // newline is appended to format by default.
- func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
- // Red is an convenient helper function to print with red foreground. A
- // newline is appended to format by default.
- func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
- // Green is an convenient helper function to print with green foreground. A
- // newline is appended to format by default.
- func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
- // Yellow is an convenient helper function to print with yellow foreground.
- // A newline is appended to format by default.
- func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
- // Blue is an convenient helper function to print with blue foreground. A
- // newline is appended to format by default.
- func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
- // Magenta is an convenient helper function to print with magenta foreground.
- // A newline is appended to format by default.
- func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
- // Cyan is an convenient helper function to print with cyan foreground. A
- // newline is appended to format by default.
- func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
- // White is an convenient helper function to print with white foreground. A
- // newline is appended to format by default.
- func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
- // BlackString is an convenient helper function to return a string with black
- // foreground.
- func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
- // RedString is an convenient helper function to return a string with red
- // foreground.
- func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
- // GreenString is an convenient helper function to return a string with green
- // foreground.
- func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
- // YellowString is an convenient helper function to return a string with yellow
- // foreground.
- func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
- // BlueString is an convenient helper function to return a string with blue
- // foreground.
- func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
- // MagentaString is an convenient helper function to return a string with magenta
- // foreground.
- func MagentaString(format string, a ...interface{}) string {
- return colorString(format, FgMagenta, a...)
- }
- // CyanString is an convenient helper function to return a string with cyan
- // foreground.
- func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
- // WhiteString is an convenient helper function to return a string with white
- // foreground.
- func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|