Support 24-bit colors in themes, both inline and view-level

pull/232/head
Simon Roberts 3 years ago
parent 7cd10a22eb
commit 87df00752d
No known key found for this signature in database
GPG Key ID: 0F30F99E6B771FD4

@ -246,13 +246,39 @@ func (c *Colorscheme) ToSprintf(name string) ISprintf {
if v, ok := c.colors[name+"_fg"].(string); ok {
if fg, ok := c.ToFgAttr(v); ok {
attrs = append(attrs, fg)
} else {
color := tcell.GetColor(v)
if color != tcell.ColorDefault {
// 24-bit foreground 38;2;⟨r⟩;⟨g⟩;⟨b⟩
r, g, b := color.RGB()
attrs = append(attrs, 38)
attrs = append(attrs, 2)
attrs = append(attrs, fcolor.Attribute(r))
attrs = append(attrs, fcolor.Attribute(g))
attrs = append(attrs, fcolor.Attribute(b))
// log.Debugf("XXX added FG color %s", attrs)
}
}
}
if v, ok := c.colors[name+"_bg"].(string); ok {
if bg, ok := c.ToBgAttr(v); ok {
attrs = append(attrs, bg)
} else {
color := tcell.GetColor(v)
if color != tcell.ColorDefault {
// 24-bit background 48;2;⟨r⟩;⟨g⟩;⟨b⟩
r, g, b := color.RGB()
attrs = append(attrs, 48)
attrs = append(attrs, 2)
attrs = append(attrs, fcolor.Attribute(r))
attrs = append(attrs, fcolor.Attribute(g))
attrs = append(attrs, fcolor.Attribute(b))
// log.Debugf("XXX added BG color %s", attrs)
}
}
}
if v, ok := c.colors[name+"_bold"].(bool); ok {
if bold, ok := c.ToBoldAttr(v); ok {
attrs = append(attrs, bold)
@ -283,7 +309,6 @@ func (c *Colorscheme) Style(name string) tcell.Style {
st = st.Underline(v)
}
// TODO: Blink Dim Italic Reverse Strikethrough
// log.Debugf("XXX Style(%s) = %s", name, st)
return st
}
@ -307,10 +332,11 @@ func (c *Colorscheme) tcellColor(name string) tcell.Color {
return color
}
if code, ok := HexToAnsi(v); ok {
// log.Debugf("XXX tcellColor(%s => %s) HEX %s", name, v, code)
return tcell.PaletteColor(int(code) & 0xff)
}
// find closest X11 color to RGB
// if code, ok := HexToAnsi(v); ok {
// // log.Debugf("XXX tcellColor(%s => %s) HEX %s", name, v, code)
// return tcell.PaletteColor(int(code) & 0xff)
// }
// log.Debugf("XXX tcellColor(%s => %s) FALLTHROUGH %s", name, v, color)
return color
}
@ -320,9 +346,10 @@ func (c *Colorscheme) ToFgAttr(v string) (fcolor.Attribute, bool) {
return attr, true
}
if code, ok := HexToAnsi(v); ok {
return fcolor.Attribute(code), true
}
// find closest X11 color to RGB
// if code, ok := HexToAnsi(v); ok {
// return fcolor.Attribute(code), true
// }
return 0, false
}
@ -332,9 +359,10 @@ func (c *Colorscheme) ToBgAttr(v string) (fcolor.Attribute, bool) {
return attr, true
}
if code, ok := HexToAnsi(v); ok {
return fcolor.Attribute(code), true
}
// find closest X11 color to RGB
// if code, ok := HexToAnsi(v); ok {
// return fcolor.Attribute(code), true
// }
return 0, false
}
@ -362,6 +390,7 @@ func HexToAnsi(h string) (uint8, bool) {
}
}
// TODO: only use if exact, otherwise use 24-bit version
code, err := xtermcolor.FromHexStr(h)
if err != nil {
return 0, false

@ -120,7 +120,7 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
return true, nil
case ch == 'm':
var err error
err = ei.output256()
err = ei.parseEscapeParams()
// switch ei.mode {
// case OutputNormal:
// err = ei.outputNormal()
@ -141,17 +141,34 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
return false, nil
}
// outputNormal provides 8 different colors:
// black, red, green, yellow, blue, magenta, cyan, white
func (ei *escapeInterpreter) outputNormal() error {
for _, param := range ei.csiParam {
p, err := strconv.Atoi(param)
if err != nil {
// parseEscapeParams interprets an escape sequence as a style modifier
// allows you to leverage the 256-colors terminal mode:
// 0x01 - 0x08: the 8 colors as in OutputNormal (black, red, green, yellow, blue, magenta, cyan, white)
// 0x09 - 0x10: Color* | AttrBold
// 0x11 - 0xe8: 216 different colors
// 0xe9 - 0x1ff: 24 different shades of grey
// see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
// see https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
// 256-colors: ESC[ 38;5;${ID}m # foreground
// 256-colors: ESC[ 48;5;${ID}m # background
// 24-bit ESC[ 38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
// 24-bit ESC[ 48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color
func (ei *escapeInterpreter) parseEscapeParams() error {
// TODO: cache escape -> Style
// convert params to int
params := make([]int, len(ei.csiParam))
for i, param := range ei.csiParam {
if p, err := strconv.Atoi(param); err == nil {
params[i] = p
} else {
return errCSIParseError
}
}
// see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
// see https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
// consume elements of params until done
pos := 0
for ok := true; ok; ok = pos < len(params) {
p := params[pos]
switch {
case p >= 30 && p <= 37:
ei.curStyle = ei.curStyle.Foreground(tcell.PaletteColor(p - 30))
@ -169,63 +186,27 @@ func (ei *escapeInterpreter) outputNormal() error {
ei.curStyle = ei.curStyle.Reverse(true)
case p == 0:
ei.curStyle = tcell.StyleDefault
}
}
return nil
}
// output256 allows you to leverage the 256-colors terminal mode:
// 0x01 - 0x08: the 8 colors as in OutputNormal
// 0x09 - 0x10: Color* | AttrBold
// 0x11 - 0xe8: 216 different colors
// 0xe9 - 0x1ff: 24 different shades of grey
func (ei *escapeInterpreter) output256() error {
if len(ei.csiParam) < 3 {
return ei.outputNormal()
}
mode, err := strconv.Atoi(ei.csiParam[1])
if err != nil {
return errCSIParseError
}
if mode != 5 {
return ei.outputNormal()
}
fgbg, err := strconv.Atoi(ei.csiParam[0])
if err != nil {
return errCSIParseError
}
color, err := strconv.Atoi(ei.csiParam[2])
if err != nil {
return errCSIParseError
}
switch fgbg {
case 38:
ei.curStyle = ei.curStyle.Foreground(tcell.PaletteColor(color + 1))
for _, param := range ei.csiParam[3:] {
p, err := strconv.Atoi(param)
if err != nil {
return errCSIParseError
case p == 38 || p == 48: // 256-color or 24-bit
// parse mode and additional params to generate a color
mode := params[pos+1] // second param - 2 or 5
var x tcell.Color
if mode == 5 { // 256 color
x = tcell.PaletteColor(params[pos+2] + 1)
pos += 2 // two additional (5+index)
} else if mode == 2 { // 24-bit
x = tcell.NewRGBColor(int32(params[pos+2]), int32(params[pos+3]), int32(params[pos+4]))
pos += 4 // four additional (2+r/g/b)
} else {
return errCSIParseError // invalid mode
}
switch {
case p == 1:
ei.curStyle = ei.curStyle.Bold(true)
case p == 4:
ei.curStyle = ei.curStyle.Underline(true)
case p == 7:
ei.curStyle = ei.curStyle.Reverse(true)
if p == 38 {
ei.curStyle = ei.curStyle.Foreground(x)
} else {
ei.curStyle = ei.curStyle.Background(x)
}
}
case 48:
ei.curStyle = ei.curStyle.Background(tcell.PaletteColor(color + 1))
default:
return errCSIParseError
}
pos += 1 // move along 1 by default
}
return nil
}

Loading…
Cancel
Save