mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-20 05:19:24 +02:00
more efficient
This commit is contained in:
parent
117c0bd4f7
commit
0bc0e4ac88
@ -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
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user