-
Notifications
You must be signed in to change notification settings - Fork 1
/
ansi.go
233 lines (197 loc) · 6.26 KB
/
ansi.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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package loading
import (
"fmt"
"strconv"
)
type profile uint8
const (
ASCII profile = iota
ANSI
)
const (
RESET = "0"
// Escape character (Exmaple: \x1b[30m)
ESC = "\x1b"
// Bell
BEL = "\a"
// Control Sequence Introducer
CSI = ESC + "["
// Operating System Command
OSC = ESC + "]"
// String Terminator
ST = ESC + `\`
)
// TODO: Eventually use our ANSI library potentially
func render(value string) string { return (CSI + value) }
func EraseDisplay(code int) string { return render(strconv.Itoa(code) + "2J") }
func EraseLine(code int) string { return render(strconv.Itoa(code) + "K") }
func HideCursor() string { return render("?25l") }
func ShowCursor() string { return render("?25h") }
func CursorUp(n int) string { return render(strconv.Itoa(n) + "A") }
func CursorDown(n int) string { return render(strconv.Itoa(n) + "B") }
func CursorForward(n int) string { return render(strconv.Itoa(n) + "C") }
func CursorBackward(n int) string { return render(strconv.Itoa(n) + "D") }
func CursorStart(n int) string { return render(strconv.Itoa(n) + "G") }
func SaveCursorPosition() string { return render("s") }
func RestoreCursorPosition() string { return render("u") }
func GetCursorPosition() string { return render("6n") }
// ////////////////////////////////////////////////////////////////////////////
// Attribute defines a single SGR Code
type sgr uint8
// Base attributes
const (
//Reset sgr = 0
Bold sgr = 1
Faint sgr = 2
Italic sgr = 3
Underline sgr = 4
Blink sgr = 5
//BlinkRapid sgr = 6
Reverse sgr = 7
//Concealed sgr = 8
CrossedOut sgr = 9
Overline sgr = 53
)
// 10 Primary (default) font
// 11–19 Alternative font Select alternative font n − 10
// 20 Fraktur (Gothic) Rarely supported
// 21 Doubly underlined; or: not bold Double-underline per ECMA-48,[5]: 8.3.117 but instead disables bold intensity on several terminals, including in the Linux kernel's console before version 4.17.[44]
// 22 Normal intensity Neither bold nor faint; color changes where intensity is implemented as such.
// 23 Neither italic, nor blackletter
// 24 Not underlined Neither singly nor doubly underlined
// 25 Not blinking Turn blinking off
// 26 Proportional spacing ITU T.61 and T.416, not known to be used on terminals
// 27 Not reversed
// 28 Reveal Not concealed
// 29 Not crossed out
// ///// This is simple 3-bit/4-bit color
const (
Black sgr = iota
Red
Green
Yellow
Blue
Magenta
Cyan
White
)
// 39 Default foreground color Implementation defined (according to standard)
// 49 Default background color Implementation defined (according to standard)
const (
//Normal sgr = 0
Bright sgr = 60
)
const (
Foreground sgr = 30
Background sgr = 40
//HiForeground sgr = 90
//HiBackground sgr = 100
)
////////////TODO not as important honestly, we can wait until its ANSI
// only lib again
//////////////////////////////////////////////
// 8-bit color (256 colors) is done with 38/48
//As 256-color lookup tables became common on graphic cards, escape sequences were added to select from a pre-defined set of 256 colors:[citation needed]
//
//ESC[38;5;⟨n⟩m Select foreground color where n is a number from the table below
//ESC[48;5;⟨n⟩m Select background color
// 0- 7: standard colors (as in ESC [ 30–37 m)
// 8- 15: high intensity colors (as in ESC [ 90–97 m)
// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
//232-255: grayscale from dark to light in 24 steps
//Grayscale colors
//232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
////////////TODO LAST
//
//24-bit
//As "true color" graphic cards with 16 to 24 bits of color became common, applications began to support 24-bit colors. Terminal emulators supporting setting 24-bit foreground and background colors with escape sequences include Xterm,[29] KDE's Konsole,[50][51] and iTerm, as well as all libvte based terminals,[52] including GNOME Terminal.[citation needed]
//
//ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
//ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color
// ////////////////////////////////////////////////////////////////////////////
// ANSI(Black, Bright, Background) => 0+60+40=100
func style(attrs ...sgr) (sum sgr) {
// TODO
// Here check for below 10, and if its below 10, only allow it, then
// maybe have color only support values within sensible color ranges
// so style will be below 10 only doing underline overline bold etc
for _, attr := range attrs {
// NOTE
// No failure on != 0, we auto provide reset
if attr != 0 {
sum += attr
}
}
return sum
}
//TODO
// or maybe decoration and color that check for their specific ranges, and they
// feed into style so you can put bold and black bckground by apssing each
// decoration() and color() through style()
// TODO
// and color will only support feasible color numbers
func decoration(dName sgr) sgr {
if (dName <= 1 && dName <= 9) &&
dName == 53 {
return style(dName)
}
return 0
}
func color(cName, cType sgr) sgr {
cStyle := cName + cType
// Normal Foreground + Background
if (30 <= cStyle && cStyle <= 50) ||
(90 <= cStyle && cStyle <= 110) {
return style(cName, cType)
}
return 0
}
///////////////////////////////////////////////////////////////////////////////
// NOTE
// This enables us to do, and it works!
type text struct {
string
attrs []sgr
}
func Text(str string, sgrs ...sgr) *text {
return &text{
string: str,
attrs: sgrs,
}
}
func (txt *text) sgr(attrs ...sgr) *text {
txt.attrs = append(txt.attrs, attrs...)
return txt
}
func (txt *text) Color(cName sgr) *text {
return txt.sgr(color(cName, Foreground))
}
func (txt *text) Background(cName sgr) *text {
return txt.sgr(color(cName, Background))
}
func (txt text) profile() profile {
if len(txt.attrs) == 0 {
return ASCII
} else {
return ANSI
}
}
func (txt text) ansi() (str string) {
// NOTE
// Semicolon is only used when combining multiple ANSI styles
for index, attr := range txt.attrs {
str += fmt.Sprintf("%v", attr)
if index != 0 {
str += ";"
}
}
return str
}
func (txt text) String() string {
if txt.profile() == ASCII {
return txt.string
}
// TODO
// This uses universal reset we want specific reset
return fmt.Sprintf("%s%sm%s%sm", CSI, txt.ansi(), txt.string, CSI+RESET)
}