/
colour.go
196 lines (177 loc) · 4.18 KB
/
colour.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package blush
import (
"fmt"
"strconv"
"strings"
)
// BgLevel is the colour value of R, G, or B when the colour is shown in the
// background.
const BgLevel = 70
// These are colour settings. NoRGB results in no colouring in the terminal.
var (
NoRGB = RGB{-1, -1, -1}
FgRed = RGB{255, 0, 0}
FgBlue = RGB{0, 0, 255}
FgGreen = RGB{0, 255, 0}
FgBlack = RGB{0, 0, 0}
FgWhite = RGB{255, 255, 255}
FgCyan = RGB{0, 255, 255}
FgMagenta = RGB{255, 0, 255}
FgYellow = RGB{255, 255, 0}
BgRed = RGB{BgLevel, 0, 0}
BgBlue = RGB{0, 0, BgLevel}
BgGreen = RGB{0, BgLevel, 0}
BgBlack = RGB{0, 0, 0}
BgWhite = RGB{BgLevel, BgLevel, BgLevel}
BgCyan = RGB{0, BgLevel, BgLevel}
BgMagenta = RGB{BgLevel, 0, BgLevel}
BgYellow = RGB{BgLevel, BgLevel, 0}
)
// Some stock colours. There will be no colouring when NoColour is used.
var (
NoColour = Colour{NoRGB, NoRGB}
Red = Colour{FgRed, NoRGB}
Blue = Colour{FgBlue, NoRGB}
Green = Colour{FgGreen, NoRGB}
Black = Colour{FgBlack, NoRGB}
White = Colour{FgWhite, NoRGB}
Cyan = Colour{FgCyan, NoRGB}
Magenta = Colour{FgMagenta, NoRGB}
Yellow = Colour{FgYellow, NoRGB}
)
// DefaultColour is the default colour if no colour is set via arguments.
var DefaultColour = Blue
// RGB represents colours that can be printed in terminals. R, G and B should be
// between 0 and 255.
type RGB struct {
R, G, B int
}
// Colour is a pair of RGB colours for foreground and background.
type Colour struct {
Foreground RGB
Background RGB
}
// Colourise wraps the input between colours.
func Colourise(input string, c Colour) string {
if c.Background == NoRGB && c.Foreground == NoRGB {
return input
}
var fg, bg string
if c.Foreground != NoRGB {
fg = foreground(c.Foreground)
}
if c.Background != NoRGB {
bg = background(c.Background)
}
return fg + bg + input + unformat()
}
func foreground(c RGB) string {
return fmt.Sprintf("\033[38;5;%dm", colour(c.R, c.G, c.B))
}
func background(c RGB) string {
return fmt.Sprintf("\033[48;5;%dm", colour(c.R, c.G, c.B))
}
func unformat() string {
return "\033[0m"
}
func colour(red, green, blue int) int {
return 16 + baseColor(red, 36) + baseColor(green, 6) + baseColor(blue, 1)
}
func baseColor(value, factor int) int {
return int(6*float64(value)/256) * factor
}
func colorFromArg(colour string) Colour {
if strings.HasPrefix(colour, "#") {
return hexColour(colour)
}
if grouping.MatchString(colour) {
if c := colourGroup(colour); c != NoColour {
return c
}
}
return stockColour(colour)
}
func colourGroup(colour string) Colour {
g := grouping.FindStringSubmatch(colour)
group, err := strconv.Atoi(g[2])
if err != nil {
return NoColour
}
c := stockColour(g[1])
switch group % 8 {
case 0:
c.Background = BgRed
case 1:
c.Background = BgBlue
case 2:
c.Background = BgGreen
case 3:
c.Background = BgBlack
case 4:
c.Background = BgWhite
case 5:
c.Background = BgCyan
case 6:
c.Background = BgMagenta
case 7:
c.Background = BgYellow
}
return c
}
func stockColour(colour string) Colour {
c := DefaultColour
switch colour {
case "r", "red":
c = Red
case "b", "blue":
c = Blue
case "g", "green":
c = Green
case "bl", "black":
c = Black
case "w", "white":
c = White
case "cy", "cyan":
c = Cyan
case "mg", "magenta":
c = Magenta
case "yl", "yellow":
c = Yellow
case "no-colour", "no-color": // nolint:misspell // it's ok.
c = NoColour
}
return c
}
func hexColour(colour string) Colour {
var r, g, b int
colour = strings.TrimPrefix(colour, "#")
switch len(colour) {
case 3:
c := strings.Split(colour, "")
r = getInt(c[0] + c[0])
g = getInt(c[1] + c[1])
b = getInt(c[2] + c[2])
case 6:
c := strings.Split(colour, "")
r = getInt(c[0] + c[1])
g = getInt(c[2] + c[3])
b = getInt(c[4] + c[5])
default:
return DefaultColour
}
for _, n := range []int{r, g, b} {
if n < 0 {
return DefaultColour
}
}
return Colour{RGB{R: r, G: g, B: b}, NoRGB}
}
// getInt returns a number between 0-255 from a hex code. If the hex is not
// between 00 and ff, it returns -1.
func getInt(hex string) int {
d, err := strconv.ParseInt("0x"+hex, 0, 64)
if err != nil || d > 255 || d < 0 {
return -99
}
return int(d)
}