github.com/neilotoole/jsoncolor@v0.6.0/jsoncolor.go (about) 1 package jsoncolor 2 3 import ( 4 "io" 5 "os" 6 "strconv" 7 8 "github.com/mattn/go-isatty" 9 10 "golang.org/x/term" 11 ) 12 13 // Colors specifies colorization of JSON output. Each field 14 // is a Color, which is simply the bytes of the terminal color code. 15 type Colors struct { 16 // Null is the color for JSON nil. 17 Null Color 18 19 // Bool is the color for boolean values. 20 Bool Color 21 22 // Number is the color for number values. 23 Number Color 24 25 // String is the color for string values. 26 String Color 27 28 // Key is the color for JSON keys. 29 Key Color 30 31 // Bytes is the color for byte data. 32 Bytes Color 33 34 // Time is the color for datetime values. 35 Time Color 36 37 // Punc is the color for JSON punctuation: []{},: etc. 38 Punc Color 39 } 40 41 // appendNull appends a colorized "null" to b. 42 func (c *Colors) appendNull(b []byte) []byte { 43 if c == nil { 44 return append(b, "null"...) 45 } 46 47 b = append(b, c.Null...) 48 b = append(b, "null"...) 49 return append(b, ansiReset...) 50 } 51 52 // appendBool appends the colorized bool v to b. 53 func (c *Colors) appendBool(b []byte, v bool) []byte { 54 if c == nil { 55 if v { 56 return append(b, "true"...) 57 } 58 59 return append(b, "false"...) 60 } 61 62 b = append(b, c.Bool...) 63 if v { 64 b = append(b, "true"...) 65 } else { 66 b = append(b, "false"...) 67 } 68 69 return append(b, ansiReset...) 70 } 71 72 // appendKey appends the colorized key v to b. 73 func (c *Colors) appendKey(b []byte, v []byte) []byte { 74 if c == nil { 75 return append(b, v...) 76 } 77 78 b = append(b, c.Key...) 79 b = append(b, v...) 80 return append(b, ansiReset...) 81 } 82 83 // appendInt64 appends the colorized int64 v to b. 84 func (c *Colors) appendInt64(b []byte, v int64) []byte { 85 if c == nil { 86 return strconv.AppendInt(b, v, 10) 87 } 88 89 b = append(b, c.Number...) 90 b = strconv.AppendInt(b, v, 10) 91 return append(b, ansiReset...) 92 } 93 94 // appendUint64 appends the colorized uint64 v to b. 95 func (c *Colors) appendUint64(b []byte, v uint64) []byte { 96 if c == nil { 97 return strconv.AppendUint(b, v, 10) 98 } 99 100 b = append(b, c.Number...) 101 b = strconv.AppendUint(b, v, 10) 102 return append(b, ansiReset...) 103 } 104 105 // appendPunc appends the colorized punctuation mark v to b. 106 func (c *Colors) appendPunc(b []byte, v byte) []byte { 107 if c == nil { 108 return append(b, v) 109 } 110 111 b = append(b, c.Punc...) 112 b = append(b, v) 113 return append(b, ansiReset...) 114 } 115 116 // Color is used to render terminal colors. In effect, Color is 117 // the bytes of the ANSI prefix code. The zero value is valid (results in 118 // no colorization). When Color is non-zero, the encoder writes the prefix, 119 //then the actual value, then the ANSI reset code. 120 // 121 // Example value: 122 // 123 // number := Color("\x1b[36m") 124 type Color []byte 125 126 // ansiReset is the ANSI ansiReset escape code. 127 const ansiReset = "\x1b[0m" 128 129 // DefaultColors returns the default Colors configuration. 130 // These colors largely follow jq's default colorization, 131 // with some deviation. 132 func DefaultColors() *Colors { 133 return &Colors{ 134 Null: Color("\x1b[2m"), 135 Bool: Color("\x1b[1m"), 136 Number: Color("\x1b[36m"), 137 String: Color("\x1b[32m"), 138 Key: Color("\x1b[34;1m"), 139 Bytes: Color("\x1b[2m"), 140 Time: Color("\x1b[32;2m"), 141 Punc: Color{}, // No colorization 142 } 143 } 144 145 // IsColorTerminal returns true if w is a colorable terminal. 146 func IsColorTerminal(w io.Writer) bool { 147 // This logic could be pretty dodgy; use at your own risk. 148 if w == nil { 149 return false 150 } 151 152 if !isTerminal(w) { 153 return false 154 } 155 156 if os.Getenv("TERM") == "dumb" { 157 return false 158 } 159 160 f, ok := w.(*os.File) 161 if !ok { 162 return false 163 } 164 165 if isatty.IsCygwinTerminal(f.Fd()) { 166 return false 167 } 168 169 return true 170 } 171 172 // isTerminal returns true if w is a terminal. 173 func isTerminal(w io.Writer) bool { 174 switch v := w.(type) { 175 case *os.File: 176 return term.IsTerminal(int(v.Fd())) 177 default: 178 return false 179 } 180 }