1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-20 05:19:24 +02:00

more efficient

This commit is contained in:
Jesse Duffield 2021-07-31 15:34:45 +10:00
parent 117c0bd4f7
commit 0bc0e4ac88
5 changed files with 170 additions and 125 deletions

View File

@ -4,10 +4,33 @@ import (
"github.com/gookit/color"
)
// A TextStyle contains a foreground color, background color, and
// decorations (bold/underline/reverse).
//
// Colors may each be either 16-bit or 256-bit RGB colors. When
// we need to produce a string with a TextStyle, if either foreground or
// background color is RGB, we'll promote the other color component to RGB as well.
// We could simplify this code by forcing everything to be RGB, but we're not
// sure how compatible or efficient that would be with various terminals.
// Lazygit will typically stick to 16-bit colors, but users may configure RGB colors.
//
// TextStyles are value objects, not entities, so for example if you want to
// add the bold decoration to a TextStyle, we'll create a new TextStyle with
// that decoration applied.
//
// Decorations are additive, so when we merge two TextStyles, if either is bold
// then the resulting style will also be bold.
//
// So that we aren't rederiving the underlying style each time we want to print
// a string, we derive it when a new TextStyle is created and store it in the
// `style` field.
type TextStyle struct {
fg *Color
bg *Color
decoration Decoration
style Sprinter
}
type Sprinter interface {
@ -15,74 +38,66 @@ type Sprinter interface {
Sprintf(format string, a ...interface{}) string
}
func New() TextStyle {
s := TextStyle{}
s.style = s.deriveStyle()
return s
}
func FromBasicFg(fg color.Color) TextStyle {
s := New()
c := NewBasicColor(fg)
s.fg = &c
s.style = s.deriveStyle()
return s
}
func FromBasicBg(bg color.Color) TextStyle {
s := New()
c := NewBasicColor(bg)
s.bg = &c
s.style = s.deriveStyle()
return s
}
func (b TextStyle) Sprint(a ...interface{}) string {
return b.deriveStyle().Sprint(a...)
return b.style.Sprint(a...)
}
func (b TextStyle) Sprintf(format string, a ...interface{}) string {
return b.deriveStyle().Sprintf(format, a...)
return b.style.Sprintf(format, a...)
}
func (b TextStyle) SetBold() TextStyle {
b.decoration.SetBold()
b.style = b.deriveStyle()
return b
}
func (b TextStyle) SetUnderline() TextStyle {
b.decoration.SetUnderline()
b.style = b.deriveStyle()
return b
}
func (b TextStyle) SetReverse() TextStyle {
b.decoration.SetReverse()
b.style = b.deriveStyle()
return b
}
func (b TextStyle) deriveStyle() Sprinter {
// TODO: consider caching
return deriveStyle(b.fg, b.bg, b.decoration)
func (b TextStyle) SetBg(color Color) TextStyle {
b.bg = &color
b.style = b.deriveStyle()
return b
}
func deriveStyle(fg *Color, bg *Color, decoration Decoration) Sprinter {
if fg == nil && bg == nil {
return color.Style(decoration.ToOpts())
}
isRgb := (fg != nil && fg.IsRGB()) || (bg != nil && bg.IsRGB())
if isRgb {
s := &color.RGBStyle{}
if fg != nil {
s.SetFg(*fg.ToRGB().rgb)
}
if bg != nil {
s.SetBg(*bg.ToRGB().rgb)
}
s.SetOpts(decoration.ToOpts())
return s
}
style := make([]color.Color, 0, 5)
if fg != nil {
style = append(style, *fg.basic)
}
if bg != nil {
style = append(style, *bg.basic)
}
style = append(style, decoration.ToOpts()...)
return color.Style(style)
func (b TextStyle) SetFg(color Color) TextStyle {
b.fg = &color
b.style = b.deriveStyle()
return b
}
// // Need to convert bg to fg otherwise .RGB wont work
// // for more info see https://github.com/gookit/color/issues/39
// rgbBg := (*b.bg - 10).RGB()
// rgbBg[3] = 1
// *res.bg = rgbBg
// res.style = *color.NewRGBStyle(*res.fg, rgbBg)
func (b TextStyle) MergeStyle(other TextStyle) TextStyle {
b.decoration = b.decoration.Merge(other.decoration)
@ -94,5 +109,52 @@ func (b TextStyle) MergeStyle(other TextStyle) TextStyle {
b.bg = other.bg
}
b.style = b.deriveStyle()
return b
}
func (b TextStyle) deriveStyle() Sprinter {
if b.fg == nil && b.bg == nil {
return color.Style(b.decoration.ToOpts())
}
isRgb := (b.fg != nil && b.fg.IsRGB()) || (b.bg != nil && b.bg.IsRGB())
if isRgb {
return b.deriveRGBStyle()
}
return b.deriveBasicStyle()
}
func (b TextStyle) deriveBasicStyle() color.Style {
style := make([]color.Color, 0, 5)
if b.fg != nil {
style = append(style, *b.fg.basic)
}
if b.bg != nil {
style = append(style, *b.bg.basic)
}
style = append(style, b.decoration.ToOpts()...)
return color.Style(style)
}
func (b TextStyle) deriveRGBStyle() *color.RGBStyle {
style := &color.RGBStyle{}
if b.fg != nil {
style.SetFg(*b.fg.ToRGB().rgb)
}
if b.bg != nil {
style.SetBg(*b.bg.ToRGB().rgb)
}
style.SetOpts(b.decoration.ToOpts())
return style
}

View File

@ -19,17 +19,14 @@ func NewBasicColor(cl color.Color) Color {
return c
}
func (c *Color) IsRGB() bool {
func (c Color) IsRGB() bool {
return c.rgb != nil
}
func (c *Color) ToRGB() Color {
func (c Color) ToRGB() Color {
if c.IsRGB() {
return *c
return c
}
rgb := c.basic.RGB()
c.rgb = &rgb
return NewRGBColor(rgb)
return NewRGBColor(c.basic.RGB())
}

View File

@ -42,9 +42,11 @@ func (d Decoration) Merge(other Decoration) Decoration {
if other.bold {
d.bold = true
}
if other.underline {
d.underline = true
}
if other.reverse {
d.reverse = true
}

View File

@ -2,11 +2,9 @@ package style
import (
"github.com/gookit/color"
"github.com/jesseduffield/lazygit/pkg/utils"
)
var (
// FgWhite = New(pointerTo(color.FgWhite), nil)
FgWhite = FromBasicFg(color.FgWhite)
FgLightWhite = FromBasicFg(color.FgLightWhite)
FgBlack = FromBasicFg(color.FgBlack)
@ -30,67 +28,3 @@ var (
AttrUnderline = New().SetUnderline()
AttrBold = New().SetBold()
)
func New() TextStyle {
return TextStyle{}
}
func FromBasicFg(fg color.Color) TextStyle {
s := New()
c := NewBasicColor(fg)
s.fg = &c
return s
}
func FromBasicBg(bg color.Color) TextStyle {
s := New()
c := NewBasicColor(bg)
s.bg = &c
return s
}
var colorMap = map[string]struct {
forground TextStyle
background TextStyle
}{
"default": {FgWhite, BgBlack},
"black": {FgBlack, BgBlack},
"red": {FgRed, BgRed},
"green": {FgGreen, BgGreen},
"yellow": {FgYellow, BgYellow},
"blue": {FgBlue, BgBlue},
"magenta": {FgMagenta, BgMagenta},
"cyan": {FgCyan, BgCyan},
"white": {FgWhite, BgWhite},
}
func SetConfigStyles(keys []string, background bool) TextStyle {
s := New()
for _, key := range keys {
switch key {
case "bold":
s = s.SetBold()
case "reverse":
s = s.SetReverse()
case "underline":
s = s.SetUnderline()
default:
value, present := colorMap[key]
if present {
var c TextStyle
if background {
c = value.background
} else {
c = value.forground
}
s = s.MergeStyle(c)
} else if utils.IsValidHexValue(key) {
c := NewRGBColor(color.HEX(key, background))
s.bg = &c
}
}
}
return s
}

View File

@ -42,13 +42,13 @@ var (
// UpdateTheme updates all theme variables
func UpdateTheme(themeConfig config.ThemeConfig) {
ActiveBorderColor = GetGocuiColor(themeConfig.ActiveBorderColor)
InactiveBorderColor = GetGocuiColor(themeConfig.InactiveBorderColor)
SelectedLineBgColor = style.SetConfigStyles(themeConfig.SelectedLineBgColor, true)
SelectedRangeBgColor = style.SetConfigStyles(themeConfig.SelectedRangeBgColor, true)
GocuiSelectedLineBgColor = GetGocuiColor(themeConfig.SelectedLineBgColor)
OptionsColor = GetGocuiColor(themeConfig.OptionsTextColor)
OptionsFgColor = style.SetConfigStyles(themeConfig.OptionsTextColor, false)
ActiveBorderColor = GetGocuiStyle(themeConfig.ActiveBorderColor)
InactiveBorderColor = GetGocuiStyle(themeConfig.InactiveBorderColor)
SelectedLineBgColor = GetTextStyle(themeConfig.SelectedLineBgColor, true)
SelectedRangeBgColor = GetTextStyle(themeConfig.SelectedRangeBgColor, true)
GocuiSelectedLineBgColor = GetGocuiStyle(themeConfig.SelectedLineBgColor)
OptionsColor = GetGocuiStyle(themeConfig.OptionsTextColor)
OptionsFgColor = GetTextStyle(themeConfig.OptionsTextColor, false)
isLightTheme := themeConfig.LightTheme
if isLightTheme {
@ -90,11 +90,61 @@ func GetGocuiAttribute(key string) gocui.Attribute {
return gocui.ColorWhite
}
// GetGocuiColor bitwise OR's a list of attributes obtained via the given keys
func GetGocuiColor(keys []string) gocui.Attribute {
// GetGocuiStyle bitwise OR's a list of attributes obtained via the given keys
func GetGocuiStyle(keys []string) gocui.Attribute {
var attribute gocui.Attribute
for _, key := range keys {
attribute |= GetGocuiAttribute(key)
}
return attribute
}
var colorMap = map[string]struct {
foreground style.TextStyle
background style.TextStyle
}{
"default": {style.FgWhite, style.BgBlack},
"black": {style.FgBlack, style.BgBlack},
"red": {style.FgRed, style.BgRed},
"green": {style.FgGreen, style.BgGreen},
"yellow": {style.FgYellow, style.BgYellow},
"blue": {style.FgBlue, style.BgBlue},
"magenta": {style.FgMagenta, style.BgMagenta},
"cyan": {style.FgCyan, style.BgCyan},
"white": {style.FgWhite, style.BgWhite},
}
func GetTextStyle(keys []string, background bool) style.TextStyle {
s := style.New()
for _, key := range keys {
switch key {
case "bold":
s = s.SetBold()
case "reverse":
s = s.SetReverse()
case "underline":
s = s.SetUnderline()
default:
value, present := colorMap[key]
if present {
var c style.TextStyle
if background {
c = value.background
} else {
c = value.foreground
}
s = s.MergeStyle(c)
} else if utils.IsValidHexValue(key) {
c := style.NewRGBColor(color.HEX(key, background))
if background {
s.SetBg(c)
} else {
s.SetFg(c)
}
}
}
}
return s
}