Upstream gookit/color state of the touched files before a10b061e0b
This commit is contained in:
commit
baec7b1239
2 files changed
+1397
No files matched your search
@@ -0,0 +1,733 @@
|
|||||||
|
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. It's also set to true if the NO_COLOR environment variable is
|
||||||
|
// set (regardless of its value). This is a global option and affects all
|
||||||
|
// colors. For more control over each color block use the methods
|
||||||
|
// DisableColor() individually.
|
||||||
|
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" || !stdoutIsTerminal()
|
||||||
|
|
||||||
|
// Output defines the standard output of the print functions. By default,
|
||||||
|
// stdOut() is used.
|
||||||
|
Output = stdOut()
|
||||||
|
|
||||||
|
// Error defines the standard error of the print functions. By default,
|
||||||
|
// stdErr() is used.
|
||||||
|
Error = stdErr()
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||||
|
func noColorIsSet() bool {
|
||||||
|
return os.Getenv("NO_COLOR") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdoutIsTerminal returns true if os.Stdout is a terminal.
|
||||||
|
// Returns false if os.Stdout is nil (e.g., when running as a Windows service).
|
||||||
|
func stdoutIsTerminal() bool {
|
||||||
|
if os.Stdout == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdOut returns a writer for color output.
|
||||||
|
// Returns io.Discard if os.Stdout is nil (e.g., when running as a Windows service).
|
||||||
|
func stdOut() io.Writer {
|
||||||
|
if os.Stdout == nil {
|
||||||
|
return io.Discard
|
||||||
|
}
|
||||||
|
return colorable.NewColorableStdout()
|
||||||
|
}
|
||||||
|
|
||||||
|
// stdErr returns a writer for color error output.
|
||||||
|
// Returns io.Discard if os.Stderr is nil (e.g., when running as a Windows service).
|
||||||
|
func stdErr() io.Writer {
|
||||||
|
if os.Stderr == nil {
|
||||||
|
return io.Discard
|
||||||
|
}
|
||||||
|
return colorable.NewColorableStderr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResetBold Attribute = iota + 22
|
||||||
|
ResetItalic
|
||||||
|
ResetUnderline
|
||||||
|
ResetBlinking
|
||||||
|
_
|
||||||
|
ResetReversed
|
||||||
|
ResetConcealed
|
||||||
|
ResetCrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||||
|
Bold: ResetBold,
|
||||||
|
Faint: ResetBold,
|
||||||
|
Italic: ResetItalic,
|
||||||
|
Underline: ResetUnderline,
|
||||||
|
BlinkSlow: ResetBlinking,
|
||||||
|
BlinkRapid: ResetBlinking,
|
||||||
|
ReverseVideo: ResetReversed,
|
||||||
|
Concealed: ResetConcealed,
|
||||||
|
CrossedOut: ResetCrossedOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack Attribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
|
||||||
|
// used internally for 256 and 24-bit coloring
|
||||||
|
foreground
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// used internally for 256 and 24-bit coloring
|
||||||
|
background
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
if noColorIsSet() {
|
||||||
|
c.noColor = boolPtr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Add(value...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGB returns a new foreground color in 24-bit RGB.
|
||||||
|
func RGB(r, g, b int) *Color {
|
||||||
|
return New(foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BgRGB returns a new background color in 24-bit RGB.
|
||||||
|
func BgRGB(r, g, b int) *Color {
|
||||||
|
return New(background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRGB is used to chain foreground RGB SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||||
|
func (c *Color) AddRGB(r, g, b int) *Color {
|
||||||
|
c.params = append(c.params, foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRGB is used to chain background RGB SGR parameters. Use as many as parameters to combine
|
||||||
|
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||||
|
func (c *Color) AddBgRGB(r, g, b int) *Color {
|
||||||
|
c.params = append(c.params, background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||||
|
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.Fprint(Output, c.format())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unset() {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Unset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriter is used to set the SGR sequence with the given io.Writer. This is
|
||||||
|
// a low-level function, and users should use the higher-level functions, such
|
||||||
|
// as color.Fprint, color.Print, etc.
|
||||||
|
func (c *Color) SetWriter(w io.Writer) *Color {
|
||||||
|
_, _ = c.setWriter(w)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) setWriter(w io.Writer) (int, error) {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Fprint(w, c.format())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetWriter resets all escape attributes and clears the output with the give
|
||||||
|
// io.Writer. Usually should be called after SetWriter().
|
||||||
|
func (c *Color) UnsetWriter(w io.Writer) {
|
||||||
|
_, _ = c.unsetWriter(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Color) unsetWriter(w io.Writer) (int, error) {
|
||||||
|
if c.isNoColorSet() {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
n, err = c.setWriter(w)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err := fmt.Fprint(w, a...)
|
||||||
|
n += nn
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err = c.unsetWriter(w)
|
||||||
|
n += nn
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
n, err = c.setWriter(w)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err := fmt.Fprintf(w, format, a...)
|
||||||
|
n += nn
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nn, err = c.unsetWriter(w)
|
||||||
|
n += nn
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return fmt.Fprintln(w, c.wrap(sprintln(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) {
|
||||||
|
return fmt.Fprintln(Output, c.wrap(sprintln(a...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprint is just like Print, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprint(a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprint(a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintln(a ...interface{}) string {
|
||||||
|
return c.wrap(sprintln(a...)) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||||
|
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return c.wrap(fmt.Sprintf(format, 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(sprintln(a...)) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sequence returns a formatted 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)
|
||||||
|
//for each element in sequence let's use the specific reset escape, or the generic one if not found
|
||||||
|
format := make([]string, len(c.params))
|
||||||
|
for i, v := range c.params {
|
||||||
|
format[i] = strconv.Itoa(int(Reset))
|
||||||
|
ra, ok := mapResetAttributes[v]
|
||||||
|
if ok {
|
||||||
|
format[i] = strconv.Itoa(int(ra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 set 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 c == nil && c2 == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c == nil || c2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.params) != len(c2.params) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
counts := make(map[Attribute]int, len(c.params))
|
||||||
|
for _, attr := range c.params {
|
||||||
|
counts[attr]++
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range c2.params {
|
||||||
|
if counts[attr] == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
counts[attr]--
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
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 a 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 a 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 a 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 a 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 a 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 a 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 a 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 a 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 a convenient helper function to return a string with black
|
||||||
|
// foreground.
|
||||||
|
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||||
|
|
||||||
|
// RedString is a convenient helper function to return a string with red
|
||||||
|
// foreground.
|
||||||
|
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||||
|
|
||||||
|
// GreenString is a convenient helper function to return a string with green
|
||||||
|
// foreground.
|
||||||
|
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||||
|
|
||||||
|
// YellowString is a convenient helper function to return a string with yellow
|
||||||
|
// foreground.
|
||||||
|
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||||
|
|
||||||
|
// BlueString is a convenient helper function to return a string with blue
|
||||||
|
// foreground.
|
||||||
|
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||||
|
|
||||||
|
// MagentaString is a convenient helper function to return a string with magenta
|
||||||
|
// foreground.
|
||||||
|
func MagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CyanString is a convenient helper function to return a string with cyan
|
||||||
|
// foreground.
|
||||||
|
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||||
|
|
||||||
|
// WhiteString is a convenient helper function to return a string with white
|
||||||
|
// foreground.
|
||||||
|
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||||
|
|
||||||
|
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||||
|
|
||||||
|
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||||
|
|
||||||
|
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||||
|
// A newline is appended to format by default.
|
||||||
|
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||||
|
|
||||||
|
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||||
|
// newline is appended to format by default.
|
||||||
|
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||||
|
|
||||||
|
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||||
|
// foreground.
|
||||||
|
func HiBlackString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiBlack, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||||
|
// foreground.
|
||||||
|
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||||
|
|
||||||
|
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||||
|
// foreground.
|
||||||
|
func HiGreenString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiGreen, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||||
|
// foreground.
|
||||||
|
func HiYellowString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiYellow, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||||
|
// foreground.
|
||||||
|
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||||
|
|
||||||
|
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||||
|
// foreground.
|
||||||
|
func HiMagentaString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiMagenta, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||||
|
// foreground.
|
||||||
|
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||||
|
|
||||||
|
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||||
|
// foreground.
|
||||||
|
func HiWhiteString(format string, a ...interface{}) string {
|
||||||
|
return colorString(format, FgHiWhite, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sprintln is a helper function to format a string with fmt.Sprintln and trim the trailing newline.
|
||||||
|
func sprintln(a ...interface{}) string {
|
||||||
|
return strings.TrimSuffix(fmt.Sprintln(a...), "\n")
|
||||||
|
}
|
||||||
+664
@@ -0,0 +1,664 @@
|
|||||||
|
package color
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Testing colors is kinda different. First we test for given colors and their
|
||||||
|
// escaped formatted results. Next we create some visual tests to be tested.
|
||||||
|
// Each visual test includes the color name to be compared.
|
||||||
|
func TestColor(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
Output = rb
|
||||||
|
|
||||||
|
NoColor = false
|
||||||
|
|
||||||
|
testColors := []struct {
|
||||||
|
text string
|
||||||
|
code Attribute
|
||||||
|
}{
|
||||||
|
{text: "black", code: FgBlack},
|
||||||
|
{text: "red", code: FgRed},
|
||||||
|
{text: "green", code: FgGreen},
|
||||||
|
{text: "yellow", code: FgYellow},
|
||||||
|
{text: "blue", code: FgBlue},
|
||||||
|
{text: "magent", code: FgMagenta},
|
||||||
|
{text: "cyan", code: FgCyan},
|
||||||
|
{text: "white", code: FgWhite},
|
||||||
|
{text: "hblack", code: FgHiBlack},
|
||||||
|
{text: "hred", code: FgHiRed},
|
||||||
|
{text: "hgreen", code: FgHiGreen},
|
||||||
|
{text: "hyellow", code: FgHiYellow},
|
||||||
|
{text: "hblue", code: FgHiBlue},
|
||||||
|
{text: "hmagent", code: FgHiMagenta},
|
||||||
|
{text: "hcyan", code: FgHiCyan},
|
||||||
|
{text: "hwhite", code: FgHiWhite},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
New(c.code).Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
scannedLine := fmt.Sprintf("%q", line)
|
||||||
|
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||||
|
escapedForm := fmt.Sprintf("%q", colored)
|
||||||
|
|
||||||
|
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||||
|
|
||||||
|
if scannedLine != escapedForm {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
line := New(c.code).Sprintf("%s", c.text)
|
||||||
|
scannedLine := fmt.Sprintf("%q", line)
|
||||||
|
colored := fmt.Sprintf("\x1b[%dm%s\x1b[0m", c.code, c.text)
|
||||||
|
escapedForm := fmt.Sprintf("%q", colored)
|
||||||
|
|
||||||
|
fmt.Printf("%s\t: %s\n", c.text, line)
|
||||||
|
|
||||||
|
if scannedLine != escapedForm {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", escapedForm, scannedLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColorEquals(t *testing.T) {
|
||||||
|
fgblack1 := New(FgBlack)
|
||||||
|
fgblack2 := New(FgBlack)
|
||||||
|
bgblack := New(BgBlack)
|
||||||
|
fgbgblack := New(FgBlack, BgBlack)
|
||||||
|
fgblackbgred := New(FgBlack, BgRed)
|
||||||
|
fgred := New(FgRed)
|
||||||
|
bgred := New(BgRed)
|
||||||
|
|
||||||
|
if !fgblack1.Equals(fgblack2) {
|
||||||
|
t.Error("Two black colors are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fgblack1.Equals(bgblack) {
|
||||||
|
t.Error("Fg and bg black colors are equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fgblack1.Equals(fgbgblack) {
|
||||||
|
t.Error("Fg black equals fg/bg black color")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fgblack1.Equals(fgred) {
|
||||||
|
t.Error("Fg black equals Fg red")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fgblack1.Equals(bgred) {
|
||||||
|
t.Error("Fg black equals Bg red")
|
||||||
|
}
|
||||||
|
|
||||||
|
if fgblack1.Equals(fgblackbgred) {
|
||||||
|
t.Error("Fg black equals fg black bg red")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColorEquals_DuplicateAttributes(t *testing.T) {
|
||||||
|
ordered := New(FgRed, Bold).Add(FgRed)
|
||||||
|
reordered := New(Bold, FgRed).Add(FgRed)
|
||||||
|
differentCounts := New(FgRed, Bold).Add(Bold)
|
||||||
|
|
||||||
|
if !ordered.Equals(reordered) {
|
||||||
|
t.Error("Colors with the same attributes in different orders are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ordered.Equals(differentCounts) {
|
||||||
|
t.Error("Colors with different duplicate attribute counts are equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoColor(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
Output = rb
|
||||||
|
|
||||||
|
testColors := []struct {
|
||||||
|
text string
|
||||||
|
code Attribute
|
||||||
|
}{
|
||||||
|
{text: "black", code: FgBlack},
|
||||||
|
{text: "red", code: FgRed},
|
||||||
|
{text: "green", code: FgGreen},
|
||||||
|
{text: "yellow", code: FgYellow},
|
||||||
|
{text: "blue", code: FgBlue},
|
||||||
|
{text: "magent", code: FgMagenta},
|
||||||
|
{text: "cyan", code: FgCyan},
|
||||||
|
{text: "white", code: FgWhite},
|
||||||
|
{text: "hblack", code: FgHiBlack},
|
||||||
|
{text: "hred", code: FgHiRed},
|
||||||
|
{text: "hgreen", code: FgHiGreen},
|
||||||
|
{text: "hyellow", code: FgHiYellow},
|
||||||
|
{text: "hblue", code: FgHiBlue},
|
||||||
|
{text: "hmagent", code: FgHiMagenta},
|
||||||
|
{text: "hcyan", code: FgHiCyan},
|
||||||
|
{text: "hwhite", code: FgHiWhite},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
p := New(c.code)
|
||||||
|
p.DisableColor()
|
||||||
|
p.Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
if line != c.text {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// global check
|
||||||
|
NoColor = true
|
||||||
|
t.Cleanup(func() {
|
||||||
|
NoColor = false
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
p := New(c.code)
|
||||||
|
p.Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
if line != c.text {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoColor_Env(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
Output = rb
|
||||||
|
|
||||||
|
testColors := []struct {
|
||||||
|
text string
|
||||||
|
code Attribute
|
||||||
|
}{
|
||||||
|
{text: "black", code: FgBlack},
|
||||||
|
{text: "red", code: FgRed},
|
||||||
|
{text: "green", code: FgGreen},
|
||||||
|
{text: "yellow", code: FgYellow},
|
||||||
|
{text: "blue", code: FgBlue},
|
||||||
|
{text: "magent", code: FgMagenta},
|
||||||
|
{text: "cyan", code: FgCyan},
|
||||||
|
{text: "white", code: FgWhite},
|
||||||
|
{text: "hblack", code: FgHiBlack},
|
||||||
|
{text: "hred", code: FgHiRed},
|
||||||
|
{text: "hgreen", code: FgHiGreen},
|
||||||
|
{text: "hyellow", code: FgHiYellow},
|
||||||
|
{text: "hblue", code: FgHiBlue},
|
||||||
|
{text: "hmagent", code: FgHiMagenta},
|
||||||
|
{text: "hcyan", code: FgHiCyan},
|
||||||
|
{text: "hwhite", code: FgHiWhite},
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("NO_COLOR", "1")
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.Unsetenv("NO_COLOR")
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, c := range testColors {
|
||||||
|
p := New(c.code)
|
||||||
|
p.Print(c.text)
|
||||||
|
|
||||||
|
line, _ := rb.ReadString('\n')
|
||||||
|
if line != c.text {
|
||||||
|
t.Errorf("Expecting %s, got '%s'\n", c.text, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_noColorIsSet(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
act func()
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default",
|
||||||
|
act: func() {},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NO_COLOR=1",
|
||||||
|
act: func() { os.Setenv("NO_COLOR", "1") },
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NO_COLOR=",
|
||||||
|
act: func() { os.Setenv("NO_COLOR", "") },
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.Unsetenv("NO_COLOR")
|
||||||
|
})
|
||||||
|
tt.act()
|
||||||
|
if got := noColorIsSet(); got != tt.want {
|
||||||
|
t.Errorf("noColorIsSet() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdoutIsTerminal_NilStdout(t *testing.T) {
|
||||||
|
stdout := os.Stdout
|
||||||
|
os.Stdout = nil
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.Stdout = stdout
|
||||||
|
})
|
||||||
|
|
||||||
|
if stdoutIsTerminal() {
|
||||||
|
t.Fatal("stdoutIsTerminal() = true, want false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdOut_NilStdout(t *testing.T) {
|
||||||
|
stdout := os.Stdout
|
||||||
|
os.Stdout = nil
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.Stdout = stdout
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := stdOut(); got != io.Discard {
|
||||||
|
t.Fatalf("stdOut() = %v, want %v", got, io.Discard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdErr_NilStderr(t *testing.T) {
|
||||||
|
stderr := os.Stderr
|
||||||
|
os.Stderr = nil
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.Stderr = stderr
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := stdErr(); got != io.Discard {
|
||||||
|
t.Fatalf("stdErr() = %v, want %v", got, io.Discard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColorVisual(t *testing.T) {
|
||||||
|
// First Visual Test
|
||||||
|
Output = colorable.NewColorableStdout()
|
||||||
|
|
||||||
|
New(FgRed).Printf("red\t")
|
||||||
|
New(BgRed).Print(" ")
|
||||||
|
New(FgRed, Bold).Println(" red")
|
||||||
|
|
||||||
|
New(FgGreen).Printf("green\t")
|
||||||
|
New(BgGreen).Print(" ")
|
||||||
|
New(FgGreen, Bold).Println(" green")
|
||||||
|
|
||||||
|
New(FgYellow).Printf("yellow\t")
|
||||||
|
New(BgYellow).Print(" ")
|
||||||
|
New(FgYellow, Bold).Println(" yellow")
|
||||||
|
|
||||||
|
New(FgBlue).Printf("blue\t")
|
||||||
|
New(BgBlue).Print(" ")
|
||||||
|
New(FgBlue, Bold).Println(" blue")
|
||||||
|
|
||||||
|
New(FgMagenta).Printf("magenta\t")
|
||||||
|
New(BgMagenta).Print(" ")
|
||||||
|
New(FgMagenta, Bold).Println(" magenta")
|
||||||
|
|
||||||
|
New(FgCyan).Printf("cyan\t")
|
||||||
|
New(BgCyan).Print(" ")
|
||||||
|
New(FgCyan, Bold).Println(" cyan")
|
||||||
|
|
||||||
|
New(FgWhite).Printf("white\t")
|
||||||
|
New(BgWhite).Print(" ")
|
||||||
|
New(FgWhite, Bold).Println(" white")
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
|
// Second Visual test
|
||||||
|
Black("black")
|
||||||
|
Red("red")
|
||||||
|
Green("green")
|
||||||
|
Yellow("yellow")
|
||||||
|
Blue("blue")
|
||||||
|
Magenta("magenta")
|
||||||
|
Cyan("cyan")
|
||||||
|
White("white")
|
||||||
|
HiBlack("hblack")
|
||||||
|
HiRed("hred")
|
||||||
|
HiGreen("hgreen")
|
||||||
|
HiYellow("hyellow")
|
||||||
|
HiBlue("hblue")
|
||||||
|
HiMagenta("hmagenta")
|
||||||
|
HiCyan("hcyan")
|
||||||
|
HiWhite("hwhite")
|
||||||
|
|
||||||
|
// Third visual test
|
||||||
|
fmt.Println()
|
||||||
|
Set(FgBlue)
|
||||||
|
fmt.Println("is this blue?")
|
||||||
|
Unset()
|
||||||
|
|
||||||
|
Set(FgMagenta)
|
||||||
|
fmt.Println("and this magenta?")
|
||||||
|
Unset()
|
||||||
|
|
||||||
|
// Fourth Visual test
|
||||||
|
fmt.Println()
|
||||||
|
blue := New(FgBlue).PrintlnFunc()
|
||||||
|
blue("blue text with custom print func")
|
||||||
|
|
||||||
|
red := New(FgRed).PrintfFunc()
|
||||||
|
red("red text with a printf func: %d\n", 123)
|
||||||
|
|
||||||
|
put := New(FgYellow).SprintFunc()
|
||||||
|
warn := New(FgRed).SprintFunc()
|
||||||
|
|
||||||
|
fmt.Fprintf(Output, "this is a %s and this is %s.\n", put("warning"), warn("error"))
|
||||||
|
|
||||||
|
info := New(FgWhite, BgGreen).SprintFunc()
|
||||||
|
fmt.Fprintf(Output, "this %s rocks!\n", info("package"))
|
||||||
|
|
||||||
|
notice := New(FgBlue).FprintFunc()
|
||||||
|
notice(os.Stderr, "just a blue notice to stderr")
|
||||||
|
|
||||||
|
// Fifth Visual Test
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Fprintln(Output, BlackString("black"))
|
||||||
|
fmt.Fprintln(Output, RedString("red"))
|
||||||
|
fmt.Fprintln(Output, GreenString("green"))
|
||||||
|
fmt.Fprintln(Output, YellowString("yellow"))
|
||||||
|
fmt.Fprintln(Output, BlueString("blue"))
|
||||||
|
fmt.Fprintln(Output, MagentaString("magenta"))
|
||||||
|
fmt.Fprintln(Output, CyanString("cyan"))
|
||||||
|
fmt.Fprintln(Output, WhiteString("white"))
|
||||||
|
fmt.Fprintln(Output, HiBlackString("hblack"))
|
||||||
|
fmt.Fprintln(Output, HiRedString("hred"))
|
||||||
|
fmt.Fprintln(Output, HiGreenString("hgreen"))
|
||||||
|
fmt.Fprintln(Output, HiYellowString("hyellow"))
|
||||||
|
fmt.Fprintln(Output, HiBlueString("hblue"))
|
||||||
|
fmt.Fprintln(Output, HiMagentaString("hmagenta"))
|
||||||
|
fmt.Fprintln(Output, HiCyanString("hcyan"))
|
||||||
|
fmt.Fprintln(Output, HiWhiteString("hwhite"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoFormat(t *testing.T) {
|
||||||
|
fmt.Printf("%s %%s = ", BlackString("Black"))
|
||||||
|
Black("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", RedString("Red"))
|
||||||
|
Red("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", GreenString("Green"))
|
||||||
|
Green("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", YellowString("Yellow"))
|
||||||
|
Yellow("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", BlueString("Blue"))
|
||||||
|
Blue("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", MagentaString("Magenta"))
|
||||||
|
Magenta("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", CyanString("Cyan"))
|
||||||
|
Cyan("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", WhiteString("White"))
|
||||||
|
White("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiBlackString("HiBlack"))
|
||||||
|
HiBlack("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiRedString("HiRed"))
|
||||||
|
HiRed("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiGreenString("HiGreen"))
|
||||||
|
HiGreen("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiYellowString("HiYellow"))
|
||||||
|
HiYellow("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiBlueString("HiBlue"))
|
||||||
|
HiBlue("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiMagentaString("HiMagenta"))
|
||||||
|
HiMagenta("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiCyanString("HiCyan"))
|
||||||
|
HiCyan("%s")
|
||||||
|
|
||||||
|
fmt.Printf("%s %%s = ", HiWhiteString("HiWhite"))
|
||||||
|
HiWhite("%s")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoFormatString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
f func(string, ...interface{}) string
|
||||||
|
format string
|
||||||
|
args []interface{}
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{BlackString, "%s", nil, "\x1b[30m%s\x1b[0m"},
|
||||||
|
{RedString, "%s", nil, "\x1b[31m%s\x1b[0m"},
|
||||||
|
{GreenString, "%s", nil, "\x1b[32m%s\x1b[0m"},
|
||||||
|
{YellowString, "%s", nil, "\x1b[33m%s\x1b[0m"},
|
||||||
|
{BlueString, "%s", nil, "\x1b[34m%s\x1b[0m"},
|
||||||
|
{MagentaString, "%s", nil, "\x1b[35m%s\x1b[0m"},
|
||||||
|
{CyanString, "%s", nil, "\x1b[36m%s\x1b[0m"},
|
||||||
|
{WhiteString, "%s", nil, "\x1b[37m%s\x1b[0m"},
|
||||||
|
{HiBlackString, "%s", nil, "\x1b[90m%s\x1b[0m"},
|
||||||
|
{HiRedString, "%s", nil, "\x1b[91m%s\x1b[0m"},
|
||||||
|
{HiGreenString, "%s", nil, "\x1b[92m%s\x1b[0m"},
|
||||||
|
{HiYellowString, "%s", nil, "\x1b[93m%s\x1b[0m"},
|
||||||
|
{HiBlueString, "%s", nil, "\x1b[94m%s\x1b[0m"},
|
||||||
|
{HiMagentaString, "%s", nil, "\x1b[95m%s\x1b[0m"},
|
||||||
|
{HiCyanString, "%s", nil, "\x1b[96m%s\x1b[0m"},
|
||||||
|
{HiWhiteString, "%s", nil, "\x1b[97m%s\x1b[0m"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
s := test.f(test.format, test.args...)
|
||||||
|
|
||||||
|
if s != test.want {
|
||||||
|
t.Errorf("[%d] want: %q, got: %q", i, test.want, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColor_Println_Newline(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
Output = rb
|
||||||
|
|
||||||
|
c := New(FgRed)
|
||||||
|
c.Println("foo")
|
||||||
|
|
||||||
|
got := readRaw(t, rb)
|
||||||
|
want := "\x1b[31mfoo\x1b[0m\n"
|
||||||
|
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Println newline error\n\nwant: %q\n got: %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColor_Sprintln_Newline(t *testing.T) {
|
||||||
|
c := New(FgRed)
|
||||||
|
|
||||||
|
got := c.Sprintln("foo")
|
||||||
|
want := "\x1b[31mfoo\x1b[0m\n"
|
||||||
|
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Println newline error\n\nwant: %q\n got: %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColor_Fprint(t *testing.T) {
|
||||||
|
rb := new(strings.Builder)
|
||||||
|
c := New(FgRed)
|
||||||
|
|
||||||
|
n, err := c.Fprint(rb, "foo", "bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Fprint error: %v", err)
|
||||||
|
}
|
||||||
|
got := rb.String()
|
||||||
|
want := "\x1b[31mfoobar\x1b[0m"
|
||||||
|
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Fprint error\n\nwant: %q\n got: %q", want, got)
|
||||||
|
}
|
||||||
|
if n != len(got) {
|
||||||
|
t.Errorf("Fprint byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColor_Fprintln(t *testing.T) {
|
||||||
|
rb := new(strings.Builder)
|
||||||
|
c := New(FgRed)
|
||||||
|
|
||||||
|
n, err := c.Fprintln(rb, "foo", "bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Fprint error: %v", err)
|
||||||
|
}
|
||||||
|
got := rb.String()
|
||||||
|
want := "\x1b[31mfoo bar\x1b[0m\n"
|
||||||
|
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Fprintln error\n\nwant: %q\n got: %q", want, got)
|
||||||
|
}
|
||||||
|
if n != len(got) {
|
||||||
|
t.Errorf("Fprintln byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColor_Fprintf(t *testing.T) {
|
||||||
|
rb := new(strings.Builder)
|
||||||
|
c := New(FgRed)
|
||||||
|
|
||||||
|
n, err := c.Fprintf(rb, "%-7s %-7s %5d\n", "hello", "world", 123)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Fprint error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := "\x1b[31mhello world 123\n\x1b[0m"
|
||||||
|
|
||||||
|
got := rb.String()
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Fprintf error\n\nwant: %q\n got: %q", want, got)
|
||||||
|
}
|
||||||
|
if n != len(got) {
|
||||||
|
t.Errorf("Fprintf byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColor_Fprintln_Newline(t *testing.T) {
|
||||||
|
rb := new(bytes.Buffer)
|
||||||
|
c := New(FgRed)
|
||||||
|
c.Fprintln(rb, "foo")
|
||||||
|
|
||||||
|
got := readRaw(t, rb)
|
||||||
|
want := "\x1b[31mfoo\x1b[0m\n"
|
||||||
|
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Println newline error\n\nwant: %q\n got: %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readRaw(t *testing.T, r io.Reader) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
out, err := io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue206_1(t *testing.T) {
|
||||||
|
// visual test, go test -v .
|
||||||
|
// to see the string with escape codes, use go test -v . > c:\temp\test.txt
|
||||||
|
underline := New(Underline).Sprint
|
||||||
|
|
||||||
|
line := fmt.Sprintf("%s %s %s %s", "word1", underline("word2"), "word3", underline("word4"))
|
||||||
|
|
||||||
|
line = CyanString(line)
|
||||||
|
|
||||||
|
fmt.Println(line)
|
||||||
|
|
||||||
|
result := fmt.Sprintf("%v", line)
|
||||||
|
const expectedResult = "\x1b[36mword1 \x1b[4mword2\x1b[24m word3 \x1b[4mword4\x1b[24m\x1b[0m"
|
||||||
|
|
||||||
|
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||||
|
t.Errorf("Expecting %v, got '%v'\n", expectedResult, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue206_2(t *testing.T) {
|
||||||
|
underline := New(Underline).Sprint
|
||||||
|
bold := New(Bold).Sprint
|
||||||
|
|
||||||
|
line := fmt.Sprintf("%s %s", GreenString(underline("underlined regular green")), RedString(bold("bold red")))
|
||||||
|
|
||||||
|
fmt.Println(line)
|
||||||
|
|
||||||
|
result := fmt.Sprintf("%v", line)
|
||||||
|
const expectedResult = "\x1b[32m\x1b[4munderlined regular green\x1b[24m\x1b[0m \x1b[31m\x1b[1mbold red\x1b[22m\x1b[0m"
|
||||||
|
|
||||||
|
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||||
|
t.Errorf("Expecting %v, got '%v'\n", expectedResult, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue218(t *testing.T) {
|
||||||
|
// Adds a newline to the end of the last string to make sure it isn't trimmed.
|
||||||
|
params := []interface{}{"word1", "word2", "word3", "word4\n"}
|
||||||
|
|
||||||
|
c := New(FgCyan)
|
||||||
|
c.Println(params...)
|
||||||
|
|
||||||
|
result := c.Sprintln(params...)
|
||||||
|
fmt.Println(params...)
|
||||||
|
fmt.Print(result)
|
||||||
|
|
||||||
|
const expectedResult = "\x1b[36mword1 word2 word3 word4\n\x1b[0m\n"
|
||||||
|
|
||||||
|
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||||
|
t.Errorf("Sprintln: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := c.SprintlnFunc()
|
||||||
|
result = fn(params...)
|
||||||
|
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||||
|
t.Errorf("SprintlnFunc: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
c.Fprintln(&buf, params...)
|
||||||
|
result = buf.String()
|
||||||
|
if !bytes.Equal([]byte(result), []byte(expectedResult)) {
|
||||||
|
t.Errorf("Fprintln: Expecting %v (%v), got '%v (%v)'\n", expectedResult, []byte(expectedResult), result, []byte(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRGB(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
r, g, b int
|
||||||
|
}{
|
||||||
|
{255, 128, 0}, // orange
|
||||||
|
{230, 42, 42}, // red
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
|
RGB(tt.r, tt.g, tt.b).Println("foreground")
|
||||||
|
RGB(tt.r, tt.g, tt.b).AddBgRGB(0, 0, 0).Println("with background")
|
||||||
|
BgRGB(tt.r, tt.g, tt.b).Println("background")
|
||||||
|
BgRGB(tt.r, tt.g, tt.b).AddRGB(255, 255, 255).Println("with foreground")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user