2021-05-31 19:27:03 +03:00
|
|
|
package lipgloss
|
|
|
|
|
|
|
|
import (
|
2022-09-30 05:40:12 +03:00
|
|
|
"image/color"
|
2021-05-31 19:27:03 +03:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/muesli/termenv"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestSetColorProfile(t *testing.T) {
|
2022-06-01 16:38:18 +03:00
|
|
|
input := "hello"
|
|
|
|
|
2021-05-31 19:27:03 +03:00
|
|
|
tt := []struct {
|
2022-06-01 16:38:18 +03:00
|
|
|
name string
|
2021-05-31 19:27:03 +03:00
|
|
|
profile termenv.Profile
|
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
{
|
2022-06-01 16:38:18 +03:00
|
|
|
"ascii",
|
2021-05-31 19:27:03 +03:00
|
|
|
termenv.Ascii,
|
|
|
|
"hello",
|
|
|
|
},
|
|
|
|
{
|
2022-06-01 16:38:18 +03:00
|
|
|
"ansi",
|
2021-05-31 19:27:03 +03:00
|
|
|
termenv.ANSI,
|
2021-06-29 18:54:20 +03:00
|
|
|
"\x1b[94mhello\x1b[0m",
|
2021-05-31 19:27:03 +03:00
|
|
|
},
|
|
|
|
{
|
2022-06-01 16:38:18 +03:00
|
|
|
"ansi256",
|
2021-05-31 19:27:03 +03:00
|
|
|
termenv.ANSI256,
|
|
|
|
"\x1b[38;5;62mhello\x1b[0m",
|
|
|
|
},
|
|
|
|
{
|
2022-06-01 16:38:18 +03:00
|
|
|
"truecolor",
|
2021-05-31 19:27:03 +03:00
|
|
|
termenv.TrueColor,
|
|
|
|
"\x1b[38;2;89;86;224mhello\x1b[0m",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-06-01 16:38:18 +03:00
|
|
|
for _, tc := range tt {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
SetColorProfile(tc.profile)
|
|
|
|
style := NewStyle().Foreground(Color("#5A56E0"))
|
|
|
|
res := style.Render(input)
|
|
|
|
|
|
|
|
if res != tc.expected {
|
|
|
|
t.Errorf("Expected:\n\n`%s`\n`%s`\n\nActual output:\n\n`%s`\n`%s`\n\n",
|
|
|
|
tc.expected, formatEscapes(tc.expected),
|
|
|
|
res, formatEscapes(res))
|
|
|
|
}
|
|
|
|
})
|
2021-05-31 19:27:03 +03:00
|
|
|
}
|
|
|
|
}
|
2022-07-06 02:11:11 +03:00
|
|
|
|
|
|
|
func TestHexToColor(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
tt := []struct {
|
|
|
|
input string
|
|
|
|
expected uint
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
"#FF0000",
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"#00F",
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"#6B50FF",
|
|
|
|
0x6B50FF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"invalid color",
|
|
|
|
0x0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range tt {
|
|
|
|
h := hexToColor(tc.input)
|
|
|
|
o := uint(h.R)<<16 + uint(h.G)<<8 + uint(h.B)
|
|
|
|
if o != tc.expected {
|
2022-09-30 04:58:37 +03:00
|
|
|
t.Errorf("expected %X, got %X (test #%d)", tc.expected, o, i+1)
|
2022-07-06 02:11:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-30 04:56:27 +03:00
|
|
|
|
|
|
|
func TestRGBA(t *testing.T) {
|
|
|
|
tt := []struct {
|
|
|
|
profile termenv.Profile
|
|
|
|
darkBg bool
|
|
|
|
input TerminalColor
|
|
|
|
expected uint
|
|
|
|
}{
|
|
|
|
// lipgloss.Color
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
Color("#FF0000"),
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
Color("9"),
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
Color("21"),
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
// lipgloss.AdaptiveColor
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
AdaptiveColor{Dark: "#FF0000", Light: "#0000FF"},
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
false,
|
|
|
|
AdaptiveColor{Dark: "#FF0000", Light: "#0000FF"},
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
AdaptiveColor{Dark: "9", Light: "21"},
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
false,
|
|
|
|
AdaptiveColor{Dark: "9", Light: "21"},
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
// lipgloss.CompleteColor
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.ANSI256,
|
|
|
|
true,
|
|
|
|
CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
0xFFFFFF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.ANSI,
|
|
|
|
true,
|
|
|
|
CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
// lipgloss.CompleteAdaptiveColor
|
|
|
|
// dark
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
true,
|
|
|
|
CompleteAdaptiveColor{
|
|
|
|
Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
Light: CompleteColor{TrueColor: "#0000FF", ANSI256: "231", ANSI: "12"},
|
|
|
|
},
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.ANSI256,
|
|
|
|
true,
|
|
|
|
CompleteAdaptiveColor{
|
|
|
|
Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "21", ANSI: "12"},
|
|
|
|
},
|
|
|
|
0xFFFFFF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.ANSI,
|
|
|
|
true,
|
|
|
|
CompleteAdaptiveColor{
|
|
|
|
Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "9"},
|
|
|
|
},
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
// light
|
|
|
|
{
|
|
|
|
termenv.TrueColor,
|
|
|
|
false,
|
|
|
|
CompleteAdaptiveColor{
|
|
|
|
Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
Light: CompleteColor{TrueColor: "#0000FF", ANSI256: "231", ANSI: "12"},
|
|
|
|
},
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.ANSI256,
|
|
|
|
false,
|
|
|
|
CompleteAdaptiveColor{
|
|
|
|
Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "21", ANSI: "12"},
|
|
|
|
},
|
|
|
|
0x0000FF,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
termenv.ANSI,
|
|
|
|
false,
|
|
|
|
CompleteAdaptiveColor{
|
|
|
|
Dark: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "12"},
|
|
|
|
Light: CompleteColor{TrueColor: "#FF0000", ANSI256: "231", ANSI: "9"},
|
|
|
|
},
|
|
|
|
0xFF0000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tc := range tt {
|
|
|
|
SetColorProfile(tc.profile)
|
|
|
|
SetHasDarkBackground(tc.darkBg)
|
|
|
|
|
|
|
|
r, g, b, _ := tc.input.RGBA()
|
|
|
|
o := uint(r/256)<<16 + uint(g/256)<<8 + uint(b/256)
|
|
|
|
|
|
|
|
if o != tc.expected {
|
|
|
|
t.Errorf("expected %X, got %X (test #%d)", tc.expected, o, i+1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-30 05:40:12 +03:00
|
|
|
|
|
|
|
// hexToColor translates a hex color string (#RRGGBB or #RGB) into a color.RGB,
|
|
|
|
// which satisfies the color.Color interface. If an invalid string is passed
|
|
|
|
// black with 100% opacity will be returned: or, in hex format, 0x000000FF.
|
|
|
|
func hexToColor(hex string) (c color.RGBA) {
|
|
|
|
c.A = 0xFF
|
|
|
|
|
|
|
|
if hex == "" || hex[0] != '#' {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
fullFormat = 7 // #RRGGBB
|
|
|
|
shortFormat = 4 // #RGB
|
|
|
|
)
|
|
|
|
|
|
|
|
switch len(hex) {
|
|
|
|
case fullFormat:
|
|
|
|
const offset = 4
|
|
|
|
c.R = hexToByte(hex[1])<<offset + hexToByte(hex[2])
|
|
|
|
c.G = hexToByte(hex[3])<<offset + hexToByte(hex[4])
|
|
|
|
c.B = hexToByte(hex[5])<<offset + hexToByte(hex[6])
|
|
|
|
case shortFormat:
|
|
|
|
const offset = 0x11
|
|
|
|
c.R = hexToByte(hex[1]) * offset
|
|
|
|
c.G = hexToByte(hex[2]) * offset
|
|
|
|
c.B = hexToByte(hex[3]) * offset
|
|
|
|
}
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func hexToByte(b byte) byte {
|
|
|
|
const offset = 10
|
|
|
|
switch {
|
|
|
|
case b >= '0' && b <= '9':
|
|
|
|
return b - '0'
|
|
|
|
case b >= 'a' && b <= 'f':
|
|
|
|
return b - 'a' + offset
|
|
|
|
case b >= 'A' && b <= 'F':
|
|
|
|
return b - 'A' + offset
|
|
|
|
}
|
|
|
|
// Invalid, but just return 0.
|
|
|
|
return 0
|
|
|
|
}
|