diff --git a/docs/Config.md b/docs/Config.md index c46c9c0f6..883a47ff7 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -252,7 +252,8 @@ os: For color attributes you can choose an array of attributes (with max one color attribute) The available attributes are: -- default +**Colors** + - black - red - green @@ -261,7 +262,12 @@ The available attributes are: - magenta - cyan - white +- '#ff00ff' # can't be used on text + +**Modifiers** + - bold +- default - reverse # useful for high-contrast - underline diff --git a/pkg/theme/theme.go b/pkg/theme/theme.go index 3b8e65c8a..218ee4b19 100644 --- a/pkg/theme/theme.go +++ b/pkg/theme/theme.go @@ -1,6 +1,8 @@ package theme import ( + "encoding/hex" + "github.com/fatih/color" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/config" @@ -59,8 +61,33 @@ func UpdateTheme(themeConfig config.ThemeConfig) { } } +// getHexColorValues returns the rgb values of a hex color +func getHexColorValues(v string) (r int32, g int32, b int32, valid bool) { + if len(v) == 4 { + v = string([]byte{v[0], v[1], v[1], v[2], v[2], v[3], v[3]}) + } else if len(v) != 7 { + return + } + + if v[0] != '#' { + return + } + + rgb, err := hex.DecodeString(v[1:]) + if err != nil { + return + } + + return int32(rgb[0]), int32(rgb[1]), int32(rgb[2]), true +} + // GetAttribute gets the gocui color attribute from the string func GetGocuiAttribute(key string) gocui.Attribute { + r, g, b, validHexColor := getHexColorValues(key) + if validHexColor { + return gocui.NewRGBColor(r, g, b) + } + colorMap := map[string]gocui.Attribute{ "default": gocui.ColorDefault, "black": gocui.ColorBlack, diff --git a/pkg/theme/theme_test.go b/pkg/theme/theme_test.go new file mode 100644 index 000000000..311289408 --- /dev/null +++ b/pkg/theme/theme_test.go @@ -0,0 +1,63 @@ +package theme + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetHexColorValues(t *testing.T) { + scenarios := []struct { + name string + hexColor string + rgb []int32 + valid bool + }{ + { + name: "valid uppercase hex color", + hexColor: "#00FF00", + rgb: []int32{0, 255, 0}, + valid: true, + }, + { + name: "valid lowercase hex color", + hexColor: "#00ff00", + rgb: []int32{0, 255, 0}, + valid: true, + }, + { + name: "valid short hex color", + hexColor: "#0bf", + rgb: []int32{0, 187, 255}, + valid: true, + }, + { + name: "invalid hex value", + hexColor: "#zz00ff", + valid: false, + }, + { + name: "invalid length hex color", + hexColor: "#", + valid: false, + }, + { + name: "invalid length hex color", + hexColor: "#aaaaaaaaaaa", + valid: false, + }, + } + + for _, s := range scenarios { + s := s + t.Run(s.name, func(t *testing.T) { + r, g, b, valid := getHexColorValues(s.hexColor) + assert.EqualValues(t, s.valid, valid, s.hexColor) + if valid { + assert.EqualValues(t, s.rgb[0], r, s.hexColor) + assert.EqualValues(t, s.rgb[1], g, s.hexColor) + assert.EqualValues(t, s.rgb[2], b, s.hexColor) + } + }) + } +}