mirror of
https://github.com/walles/moar.git
synced 2024-11-22 11:45:50 +03:00
Test looping over all styles' background colors
This commit is contained in:
parent
d13e9d4994
commit
22885dc4b0
13
m/styling.go
13
m/styling.go
@ -55,11 +55,7 @@ func twinStyleFromChroma(chromaStyle *chroma.Style, chromaFormatter *chroma.Form
|
||||
return &cells[0].Style
|
||||
}
|
||||
|
||||
func backgroundStyleFromChroma(chromaStyle *chroma.Style) *twin.Style {
|
||||
if chromaStyle == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
func backgroundStyleFromChroma(chromaStyle chroma.Style) twin.Style {
|
||||
backgroundEntry := chromaStyle.Get(chroma.Background)
|
||||
|
||||
if !backgroundEntry.Background.IsSet() {
|
||||
@ -92,7 +88,7 @@ func backgroundStyleFromChroma(chromaStyle *chroma.Style) *twin.Style {
|
||||
returnMe = returnMe.WithAttr(twin.AttrUnderline)
|
||||
}
|
||||
|
||||
return &returnMe
|
||||
return returnMe
|
||||
}
|
||||
|
||||
// consumeLessTermcapEnvs parses LESS_TERMCAP_xx environment variables and
|
||||
@ -128,9 +124,8 @@ func styleUi(chromaStyle *chroma.Style, chromaFormatter *chroma.Formatter, statu
|
||||
if standoutStyle != nil {
|
||||
statusbarStyle = *standoutStyle
|
||||
} else if statusbarOption == STATUSBAR_STYLE_INVERSE {
|
||||
styleBackground := backgroundStyleFromChroma(chromaStyle)
|
||||
if styleBackground != nil {
|
||||
statusbarStyle = styleBackground.WithAttr(twin.AttrReverse)
|
||||
if chromaStyle != nil {
|
||||
statusbarStyle = backgroundStyleFromChroma(*chromaStyle).WithAttr(twin.AttrReverse)
|
||||
} else {
|
||||
statusbarStyle = twin.StyleDefault.WithAttr(twin.AttrReverse)
|
||||
}
|
||||
|
@ -4,15 +4,30 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"github.com/walles/moar/twin"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestBackgroundStyleFromChromaGithub(t *testing.T) {
|
||||
style := backgroundStyleFromChroma(styles.Get("github"))
|
||||
assert.Check(t, style != nil)
|
||||
style := backgroundStyleFromChroma(*styles.Get("github"))
|
||||
|
||||
assert.Equal(t, style.String(), "Default color on #ffffff")
|
||||
}
|
||||
|
||||
// FIXME: Add a parameterized test looping over all styles and checking that the
|
||||
// contrast we get in backgroundStyleFromChroma is good enough.
|
||||
// Test looping over all styles and checking that the contrast we get in
|
||||
// backgroundStyleFromChroma is good enough.
|
||||
func TestBackgroundStyleContrast(t *testing.T) {
|
||||
for _, style := range styles.Registry {
|
||||
t.Run(style.Name, func(t *testing.T) {
|
||||
backgroundStyle := backgroundStyleFromChroma(*style)
|
||||
assert.Check(t, backgroundStyle.Background().ColorType() == twin.ColorType24bit)
|
||||
|
||||
if backgroundStyle.Foreground().ColorType() == twin.ColorTypeDefault {
|
||||
return
|
||||
}
|
||||
|
||||
distance := backgroundStyle.Background().Distance(backgroundStyle.Foreground())
|
||||
assert.Check(t, distance > 0.5, "distance=%f", distance)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
)
|
||||
|
||||
@ -14,7 +15,7 @@ type ColorType uint8
|
||||
|
||||
const (
|
||||
// Default foreground / background color
|
||||
colorTypeDefault ColorType = iota
|
||||
ColorTypeDefault ColorType = iota
|
||||
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
|
||||
//
|
||||
@ -33,7 +34,7 @@ const (
|
||||
)
|
||||
|
||||
// Reset to default foreground / background color
|
||||
var ColorDefault = newColor(colorTypeDefault, 0)
|
||||
var ColorDefault = newColor(ColorTypeDefault, 0)
|
||||
|
||||
// From: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
|
||||
var colorNames16 = map[int]string{
|
||||
@ -77,7 +78,7 @@ func NewColorHex(rgb uint32) Color {
|
||||
return newColor(ColorType24bit, rgb)
|
||||
}
|
||||
|
||||
func (color Color) colorType() ColorType {
|
||||
func (color Color) ColorType() ColorType {
|
||||
return ColorType(color >> 24)
|
||||
}
|
||||
|
||||
@ -94,13 +95,13 @@ func (color Color) ansiString(foreground bool, terminalColorCount ColorType) str
|
||||
fgBgMarker = "4"
|
||||
}
|
||||
|
||||
if color.colorType() == colorTypeDefault {
|
||||
if color.ColorType() == ColorTypeDefault {
|
||||
return fmt.Sprint("\x1b[", fgBgMarker, "9m")
|
||||
}
|
||||
|
||||
color = color.downsampleTo(terminalColorCount)
|
||||
|
||||
if color.colorType() == ColorType16 {
|
||||
if color.ColorType() == ColorType16 {
|
||||
value := color.colorValue()
|
||||
if value < 8 {
|
||||
return fmt.Sprint("\x1b[", fgBgMarker, value, "m")
|
||||
@ -113,14 +114,14 @@ func (color Color) ansiString(foreground bool, terminalColorCount ColorType) str
|
||||
}
|
||||
}
|
||||
|
||||
if color.colorType() == ColorType256 {
|
||||
if color.ColorType() == ColorType256 {
|
||||
value := color.colorValue()
|
||||
if value <= 255 {
|
||||
return fmt.Sprint("\x1b[", fgBgMarker, "8;5;", value, "m")
|
||||
}
|
||||
}
|
||||
|
||||
if color.colorType() == ColorType24bit {
|
||||
if color.ColorType() == ColorType24bit {
|
||||
value := color.colorValue()
|
||||
red := (value & 0xff0000) >> 16
|
||||
green := (value & 0xff00) >> 8
|
||||
@ -129,7 +130,7 @@ func (color Color) ansiString(foreground bool, terminalColorCount ColorType) str
|
||||
return fmt.Sprint("\x1b[", fgBgMarker, "8;2;", red, ";", green, ";", blue, "m")
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("unhandled color type=%d %s", color.colorType(), color.String()))
|
||||
panic(fmt.Errorf("unhandled color type=%d %s", color.ColorType(), color.String()))
|
||||
}
|
||||
|
||||
func (color Color) ForegroundAnsiString(terminalColorCount ColorType) string {
|
||||
@ -143,8 +144,8 @@ func (color Color) BackgroundAnsiString(terminalColorCount ColorType) string {
|
||||
}
|
||||
|
||||
func (color Color) String() string {
|
||||
switch color.colorType() {
|
||||
case colorTypeDefault:
|
||||
switch color.ColorType() {
|
||||
case ColorTypeDefault:
|
||||
return "Default color"
|
||||
|
||||
case ColorType16:
|
||||
@ -160,15 +161,15 @@ func (color Color) String() string {
|
||||
return fmt.Sprintf("#%06x", color.colorValue())
|
||||
}
|
||||
|
||||
panic(fmt.Errorf("unhandled color type %d", color.colorType()))
|
||||
panic(fmt.Errorf("unhandled color type %d", color.ColorType()))
|
||||
}
|
||||
|
||||
func (color Color) downsampleTo(terminalColorCount ColorType) Color {
|
||||
if color.colorType() == colorTypeDefault || terminalColorCount == colorTypeDefault {
|
||||
if color.ColorType() == ColorTypeDefault || terminalColorCount == ColorTypeDefault {
|
||||
panic(fmt.Errorf("downsampling to or from default color not supported, %s -> %#v", color.String(), terminalColorCount))
|
||||
}
|
||||
|
||||
if color.colorType() <= terminalColorCount {
|
||||
if color.ColorType() <= terminalColorCount {
|
||||
// Already low enough
|
||||
return color
|
||||
}
|
||||
@ -177,7 +178,7 @@ func (color Color) downsampleTo(terminalColorCount ColorType) Color {
|
||||
var targetR float64
|
||||
var targetG float64
|
||||
var targetB float64
|
||||
if color.colorType() == ColorType24bit {
|
||||
if color.ColorType() == ColorType24bit {
|
||||
targetR = float64(color.colorValue()>>16) / 255.0
|
||||
targetG = float64(color.colorValue()>>8&0xff) / 255.0
|
||||
targetB = float64(color.colorValue()&0xff) / 255.0
|
||||
@ -227,3 +228,31 @@ func (color Color) downsampleTo(terminalColorCount ColorType) Color {
|
||||
return NewColor256(uint8(bestMatch))
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for Chroma's color distance function.
|
||||
//
|
||||
// That one says it uses this formula: https://www.compuphase.com/cmetric.htm
|
||||
//
|
||||
// The result from this function has been scaled to 0.0-1.0, where 1.0 is the
|
||||
// distance between black and white.
|
||||
func (c Color) Distance(other Color) float64 {
|
||||
if c.ColorType() != ColorType24bit {
|
||||
panic(fmt.Errorf("contrast only supported for 24 bit colors, got %s vs %s", c.String(), other.String()))
|
||||
}
|
||||
|
||||
baseColor := chroma.NewColour(
|
||||
uint8(c.colorValue()>>16&0xff),
|
||||
uint8(c.colorValue()>>8&0xff),
|
||||
uint8(c.colorValue()&0xff),
|
||||
)
|
||||
|
||||
otherColor := chroma.NewColour(
|
||||
uint8(other.colorValue()>>16&0xff),
|
||||
uint8(other.colorValue()>>8&0xff),
|
||||
uint8(other.colorValue()&0xff),
|
||||
)
|
||||
|
||||
// Magic constant comes from testing
|
||||
maxDistance := 764.8333151739665
|
||||
return baseColor.Distance(otherColor) / maxDistance
|
||||
}
|
||||
|
@ -40,3 +40,17 @@ func TestAnsiStringDefault(t *testing.T) {
|
||||
"\x1b[39m",
|
||||
)
|
||||
}
|
||||
|
||||
func TestDistance(t *testing.T) {
|
||||
// Black -> white
|
||||
assert.Equal(t,
|
||||
NewColor24Bit(0, 0, 0).Distance(NewColor24Bit(255, 255, 255)),
|
||||
1.0,
|
||||
)
|
||||
|
||||
// White -> black
|
||||
assert.Equal(t,
|
||||
NewColor24Bit(255, 255, 255).Distance(NewColor24Bit(0, 0, 0)),
|
||||
1.0,
|
||||
)
|
||||
}
|
||||
|
@ -117,6 +117,14 @@ func (attr AttrMask) has(attrs AttrMask) bool {
|
||||
return attr&attrs != 0
|
||||
}
|
||||
|
||||
func (style Style) Background() Color {
|
||||
return style.bg
|
||||
}
|
||||
|
||||
func (style Style) Foreground() Color {
|
||||
return style.fg
|
||||
}
|
||||
|
||||
func (style Style) WithBackground(color Color) Style {
|
||||
return Style{
|
||||
fg: style.fg,
|
||||
|
Loading…
Reference in New Issue
Block a user