-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathparse.go
178 lines (150 loc) · 4.32 KB
/
parse.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
package sgr
import (
"bytes"
"fmt"
"strconv"
"strings"
)
var blockCodes = make(map[string]string)
func init() {
// Options
blockCodes["reset"] = Reset
blockCodes["fg-reset"] = ResetForegroundColor
blockCodes["bg-reset"] = ResetBackgroundColor
blockCodes["bold"] = Bold
blockCodes["boldOff"] = BoldOff
blockCodes["underline"] = Underline
blockCodes["underlineOff"] = UnderlineOff
blockCodes["blink"] = Blink
blockCodes["blinkOff"] = BlinkOff
blockCodes["imageNegative"] = ImageNegative
blockCodes["imagePositive"] = ImagePositive
blockCodes["framed"] = Framed
blockCodes["encircled"] = Encircled
blockCodes["framedEncircledOff"] = FramedEncircledOff
blockCodes["overlined"] = Overlined
blockCodes["overlinedOff"] = OverlinedOff
// Foreground Colors
blockCodes["fg-black"] = FgBlack
blockCodes["fg-red"] = FgRed
blockCodes["fg-green"] = FgGreen
blockCodes["fg-yellow"] = FgYellow
blockCodes["fg-blue"] = FgBlue
blockCodes["fg-magenta"] = FgMagenta
blockCodes["fg-cyan"] = FgCyan
blockCodes["fg-grey"] = FgGrey
blockCodes["fg-white"] = FgWhite
// Background Colors
blockCodes["bg-black"] = BgBlack
blockCodes["bg-red"] = BgRed
blockCodes["bg-green"] = BgGreen
blockCodes["bg-yellow"] = BgYellow
blockCodes["bg-blue"] = BgBlue
blockCodes["bg-magenta"] = BgMagenta
blockCodes["bg-cyan"] = BgCyan
blockCodes["bg-grey"] = BgGrey
blockCodes["bg-white"] = BgWhite
}
func MustParse(format string) string {
str, err := parse(true, false, format)
if err != nil {
panic(err)
}
return str
}
func MustParseln(format string) string {
str, err := parse(true, true, format)
if err != nil {
panic(err)
}
return str
}
func MustParseWithoutReset(format string) string {
str, err := parse(false, false, format)
if err != nil {
panic(err)
}
return str
}
func Parse(format string) (string, error) {
return parse(true, false, format)
}
func Parseln(format string) (string, error) {
return parse(true, true, format)
}
func ParseWithoutReset(format string) (string, error) {
return parse(false, false, format)
}
func parse(reset bool, newline bool, format string) (string, error) {
// Builder used to build the colored string.
buf := new(bytes.Buffer)
// position in the parsing process
pos := 0
// index of the currenctly processing square bracket start
idxStart := 0
idxEnd := 0
for {
// Find next square bracket, break loop when none was found.
relBlockOpen := strings.IndexRune(format[pos:], '[')
if relBlockOpen == -1 {
buf.WriteString(format[pos:])
break
}
idxStart = pos + relBlockOpen
// Test for escaped square bracket
if format[idxStart+1] == '[' {
buf.WriteString(format[pos : idxStart+1])
pos = idxStart + 2
continue
}
// Add skipped string (if any)
if idxStart > pos { //idxStart > pos+1 ???
buf.WriteString(format[pos:idxStart])
}
// Find square bracket end
relBlockClose := strings.IndexRune(format[idxStart:], ']')
if relBlockClose == -1 {
return "", fmt.Errorf("Opened square bracket never closed at pos %d. If you want a literal bracket escape it: [[", idxStart)
}
idxEnd = idxStart + relBlockClose
// found a block
block := format[idxStart+1 : idxEnd]
fields := strings.Fields(block)
for _, field := range fields {
if sgrString, blockCodeExists := blockCodes[field]; blockCodeExists {
buf.WriteString(sgrString)
continue
}
isFgColor := strings.HasPrefix(field, "fg-")
isBgColor := strings.HasPrefix(field, "bg-")
if isFgColor || isBgColor {
// Check if given number is valid.
clr, err := strconv.Atoi(field[3:])
if err != nil {
return "", fmt.Errorf("Invalid code '%s' in block at position %d.", field, idxStart)
}
if clr < 0 || clr > 255 {
return "", fmt.Errorf("Invalid color code %s. Expecting 0-255 or a defined color.", field[3:])
}
// Add color sequence to the buffer
if isFgColor {
buf.WriteString(sgrStart + fgColorStart + field[3:] + sgrEnd)
} else {
buf.WriteString(sgrStart + bgColorStart + field[3:] + sgrEnd)
}
continue // next field
}
// Not a valid blockCode and not a fgColor or bgColor
return "", fmt.Errorf("Invalid code '%s' in block at position %d.", field, idxStart)
}
// Change starting position for next iteration.
pos = idxEnd + 1
}
if reset {
buf.WriteString(Reset)
}
if newline {
buf.WriteString("\n")
}
return buf.String(), nil
}