turbo-editors/turbo-corepublic Fork 0
v1.0.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

parser_sgr.go · 174 lines · 6.1 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 15h ago1package terminal
2
3import "github.com/gdamore/tcell/v2"
4
5// ansiColors are the sixteen colours a program names by number.
6//
7// They are given as tcell's own named colours rather than as fixed hex values,
8// so what appears on screen is the palette the user chose for their terminal —
9// which is what they expect a program's "red" to look like.
10var ansiColors = [16]tcell.Color{
11 tcell.ColorBlack, tcell.ColorMaroon, tcell.ColorGreen, tcell.ColorOlive,
12 tcell.ColorNavy, tcell.ColorPurple, tcell.ColorTeal, tcell.ColorSilver,
13 tcell.ColorGray, tcell.ColorRed, tcell.ColorLime, tcell.ColorYellow,
14 tcell.ColorBlue, tcell.ColorFuchsia, tcell.ColorAqua, tcell.ColorWhite,
15}
16
17// runSGR performs a Select Graphic Rendition sequence, ESC [ … m, which is how
18// a program sets colours and attributes.
19//
20// The parameters are read in order because two of them — 38 and 48 — swallow
21// the ones that follow to spell out a colour.
22func (p *Parser) runSGR() {
23 if len(p.params) == 0 {
24 p.screen.SetStyle(tcell.StyleDefault)
25 return
26 }
27
28 style := p.screen.Style()
29 for i := 0; i < len(p.params); i++ {
30 style, i = applySGR(style, p.params, i)
31 }
32 p.screen.SetStyle(style)
33}
34
35// directColors are the SGR codes that name one of the sixteen colours outright,
36// as four ranges of eight: normal foreground, normal background, and the two
37// bright halves that offset into the top of the palette.
38var directColors = []struct {
39 low, high int
40 offset int
41 which side
42}{
43 {low: 30, high: 37, offset: 0, which: foreground},
44 {low: 40, high: 47, offset: 0, which: background},
45 {low: 90, high: 97, offset: 8, which: foreground},
46 {low: 100, high: 107, offset: 8, which: background},
47}
48
49// applySGR applies the parameter at index i and returns the style and the
50// index of the last parameter it consumed.
51func applySGR(style tcell.Style, params []int, i int) (tcell.Style, int) {
52 code := params[i]
53
54 // 38 and 48 are the only codes that read the parameters after them, which
55 // is why applying a run of them cannot simply be a loop over each one.
56 switch code {
57 case 38:
58 return extendedColor(style, params, i, foreground)
59 case 48:
60 return extendedColor(style, params, i, background)
61 }
62
63 if coloured, ok := directColor(style, code); ok {
64 return coloured, i
65 }
66 return applyAttribute(style, code), i
67}
68
69// directColor applies a code that names one of the sixteen colours, and reports
70// whether the code was one of them.
71func directColor(style tcell.Style, code int) (tcell.Style, bool) {
72 for _, r := range directColors {
73 if code >= r.low && code <= r.high {
74 return paint(style, r.which, ansiColors[code-r.low+r.offset]), true
75 }
76 }
77 return style, false
78}
79
80// styleAttributes are the SGR codes that are not colours, and what each does to
81// a style.
82//
83// A table rather than a switch because a table is what it is — a list out of
84// ECMA-48 — and because the setting and the clearing of an attribute then sit
85// one line apart, where a wrong pairing is visible.
86var styleAttributes = map[int]func(tcell.Style) tcell.Style{
87 1: func(s tcell.Style) tcell.Style { return s.Bold(true) },
88 22: func(s tcell.Style) tcell.Style { return s.Bold(false).Dim(false) },
89 2: func(s tcell.Style) tcell.Style { return s.Dim(true) },
90 3: func(s tcell.Style) tcell.Style { return s.Italic(true) },
91 23: func(s tcell.Style) tcell.Style { return s.Italic(false) },
92 4: func(s tcell.Style) tcell.Style { return s.Underline(true) },
93 24: func(s tcell.Style) tcell.Style { return s.Underline(false) },
94 5: func(s tcell.Style) tcell.Style { return s.Blink(true) },
95 6: func(s tcell.Style) tcell.Style { return s.Blink(true) }, // "rapid" blink, blinked the same
96 25: func(s tcell.Style) tcell.Style { return s.Blink(false) },
97 7: func(s tcell.Style) tcell.Style { return s.Reverse(true) },
98 27: func(s tcell.Style) tcell.Style { return s.Reverse(false) },
99 9: func(s tcell.Style) tcell.Style { return s.StrikeThrough(true) },
100 29: func(s tcell.Style) tcell.Style { return s.StrikeThrough(false) },
101 39: func(s tcell.Style) tcell.Style { return s.Foreground(tcell.ColorDefault) },
102 49: func(s tcell.Style) tcell.Style { return s.Background(tcell.ColorDefault) },
103}
104
105// applyAttribute applies the SGR codes that are not colours.
106//
107// A code this emulator does not implement leaves the style as it was, which is
108// what a terminal is expected to do with one it does not know.
109func applyAttribute(style tcell.Style, code int) tcell.Style {
110 if code == 0 {
111 return tcell.StyleDefault // reset, which no table entry could express
112 }
113 if apply, ok := styleAttributes[code]; ok {
114 return apply(style)
115 }
116 return style
117}
118
119// side says which half of a style an extended colour sets.
120type side int
121
122const (
123 foreground side = iota
124 background
125)
126
127// extendedColor reads a 256-colour or 24-bit colour spelled out across several
128// parameters, and returns the style and the index of the last one used.
129//
130// 38;5;n one of the 256 palette colours
131// 38;2;r;g;b a 24-bit colour
132//
133// A sequence that runs out of parameters partway leaves the style alone: half
134// a colour is not a colour.
135func extendedColor(style tcell.Style, params []int, i int, which side) (tcell.Style, int) {
136 if i+1 >= len(params) {
137 return style, i
138 }
139
140 switch params[i+1] {
141 case 5:
142 return paletteColor(style, params, i, which)
143 case 2:
144 return rgbColor(style, params, i, which)
145 default:
146 return style, i + 1
147 }
148}
149
150// paletteColor reads the 38;5;n form, one of the 256 palette colours.
151func paletteColor(style tcell.Style, params []int, i int, which side) (tcell.Style, int) {
152 if i+2 >= len(params) {
153 return style, len(params) - 1
154 }
155 return paint(style, which, tcell.PaletteColor(params[i+2]&0xff)), i + 2
156}
157
158// rgbColor reads the 38;2;r;g;b form, a 24-bit colour.
159func rgbColor(style tcell.Style, params []int, i int, which side) (tcell.Style, int) {
160 if i+4 >= len(params) {
161 return style, len(params) - 1
162 }
163
164 colour := tcell.NewRGBColor(int32(params[i+2]), int32(params[i+3]), int32(params[i+4]))
165 return paint(style, which, colour), i + 4
166}
167
168// paint sets one half of a style to a colour.
169func paint(style tcell.Style, which side, colour tcell.Color) tcell.Style {
170 if which == foreground {
171 return style.Foreground(colour)
172 }
173 return style.Background(colour)
174}