mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-21 12:16:54 +02:00
Merge pull request #1409 from jesseduffield/jesse-switch-text-color-library
This commit is contained in:
commit
3c78ba7ed3
@ -98,35 +98,35 @@ func (l *PatchLine) render(selected bool, included bool) string {
|
|||||||
return coloredString(style.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
|
return coloredString(style.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
colorAttr := theme.DefaultTextColor
|
textStyle := theme.DefaultTextColor
|
||||||
switch l.Kind {
|
switch l.Kind {
|
||||||
case PATCH_HEADER:
|
case PATCH_HEADER:
|
||||||
colorAttr = colorAttr.SetBold(true)
|
textStyle = textStyle.SetBold()
|
||||||
case ADDITION:
|
case ADDITION:
|
||||||
colorAttr = colorAttr.SetColor(style.FgGreen)
|
textStyle = style.FgGreen
|
||||||
case DELETION:
|
case DELETION:
|
||||||
colorAttr = colorAttr.SetColor(style.FgRed)
|
textStyle = style.FgRed
|
||||||
case COMMIT_SHA:
|
case COMMIT_SHA:
|
||||||
colorAttr = colorAttr.SetColor(style.FgYellow)
|
textStyle = style.FgYellow
|
||||||
}
|
}
|
||||||
|
|
||||||
return coloredString(colorAttr, content, selected, included)
|
return coloredString(textStyle, content, selected, included)
|
||||||
}
|
}
|
||||||
|
|
||||||
func coloredString(colorAttr style.TextStyle, str string, selected bool, included bool) string {
|
func coloredString(textStyle style.TextStyle, str string, selected bool, included bool) string {
|
||||||
if selected {
|
if selected {
|
||||||
colorAttr = colorAttr.SetColor(theme.SelectedRangeBgColor)
|
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(str) < 2 {
|
if len(str) < 2 {
|
||||||
return colorAttr.Sprint(str)
|
return textStyle.Sprint(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
res := colorAttr.Sprint(str[:1])
|
res := textStyle.Sprint(str[:1])
|
||||||
if included {
|
if included {
|
||||||
return res + colorAttr.SetColor(style.BgGreen).Sprint(str[1:])
|
return res + textStyle.MergeStyle(style.BgGreen).Sprint(str[1:])
|
||||||
}
|
}
|
||||||
return res + colorAttr.Sprint(str[1:])
|
return res + textStyle.Sprint(str[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePatch(patch string) ([]int, []int, []*PatchLine) {
|
func parsePatch(patch string) ([]int, []int, []*PatchLine) {
|
||||||
|
@ -28,13 +28,13 @@ func (gui *Gui) GetOnRunCommand() func(entry oscommands.CmdLogEntry) {
|
|||||||
currentSpan = entry.GetSpan()
|
currentSpan = entry.GetSpan()
|
||||||
}
|
}
|
||||||
|
|
||||||
clrAttr := theme.DefaultTextColor
|
textStyle := theme.DefaultTextColor
|
||||||
if !entry.GetCommandLine() {
|
if !entry.GetCommandLine() {
|
||||||
clrAttr = clrAttr.SetColor(style.FgMagenta)
|
textStyle = style.FgMagenta
|
||||||
}
|
}
|
||||||
gui.CmdLog = append(gui.CmdLog, entry.GetCmdStr())
|
gui.CmdLog = append(gui.CmdLog, entry.GetCmdStr())
|
||||||
indentedCmdStr := " " + strings.Replace(entry.GetCmdStr(), "\n", "\n ", -1)
|
indentedCmdStr := " " + strings.Replace(entry.GetCmdStr(), "\n", "\n ", -1)
|
||||||
fmt.Fprint(gui.Views.Extras, "\n"+clrAttr.Sprint(indentedCmdStr))
|
fmt.Fprint(gui.Views.Extras, "\n"+textStyle.Sprint(indentedCmdStr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ func (gui *Gui) informationStr() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gui.g.Mouse {
|
if gui.g.Mouse {
|
||||||
donate := style.FgMagenta.SetUnderline(true).Sprint(gui.Tr.Donate)
|
donate := style.FgMagenta.SetUnderline().Sprint(gui.Tr.Donate)
|
||||||
askQuestion := style.FgYellow.SetUnderline(true).Sprint(gui.Tr.AskQuestion)
|
askQuestion := style.FgYellow.SetUnderline().Sprint(gui.Tr.AskQuestion)
|
||||||
return fmt.Sprintf("%s %s %s", donate, askQuestion, gui.Config.GetVersion())
|
return fmt.Sprintf("%s %s %s", donate, askQuestion, gui.Config.GetVersion())
|
||||||
} else {
|
} else {
|
||||||
return gui.Config.GetVersion()
|
return gui.Config.GetVersion()
|
||||||
|
@ -15,18 +15,18 @@ func ColoredConflictFile(content string, state *State, hasFocus bool) string {
|
|||||||
conflict, remainingConflicts := shiftConflict(state.conflicts)
|
conflict, remainingConflicts := shiftConflict(state.conflicts)
|
||||||
var outputBuffer bytes.Buffer
|
var outputBuffer bytes.Buffer
|
||||||
for i, line := range utils.SplitLines(content) {
|
for i, line := range utils.SplitLines(content) {
|
||||||
colour := theme.DefaultTextColor
|
textStyle := theme.DefaultTextColor
|
||||||
if i == conflict.start || i == conflict.middle || i == conflict.end {
|
if i == conflict.start || i == conflict.middle || i == conflict.end {
|
||||||
colour.SetColor(style.FgRed)
|
textStyle = style.FgRed
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictTop) {
|
if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictTop) {
|
||||||
colour = theme.SelectedRangeBgColor.SetBold(true)
|
textStyle = theme.SelectedRangeBgColor.SetBold()
|
||||||
}
|
}
|
||||||
if i == conflict.end && len(remainingConflicts) > 0 {
|
if i == conflict.end && len(remainingConflicts) > 0 {
|
||||||
conflict, remainingConflicts = shiftConflict(remainingConflicts)
|
conflict, remainingConflicts = shiftConflict(remainingConflicts)
|
||||||
}
|
}
|
||||||
outputBuffer.WriteString(colour.Sprint(line) + "\n")
|
outputBuffer.WriteString(textStyle.Sprint(line) + "\n")
|
||||||
}
|
}
|
||||||
return outputBuffer.String()
|
return outputBuffer.String()
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
|||||||
{
|
{
|
||||||
isActive: gui.GitCommand.PatchManager.Active,
|
isActive: gui.GitCommand.PatchManager.Active,
|
||||||
description: func() string {
|
description: func() string {
|
||||||
return style.FgYellow.SetBold(true).Sprintf(
|
return style.FgYellow.SetBold().Sprintf(
|
||||||
"%s %s",
|
"%s %s",
|
||||||
gui.Tr.LcBuildingPatch,
|
gui.Tr.LcBuildingPatch,
|
||||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||||
@ -38,7 +38,7 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
|||||||
{
|
{
|
||||||
isActive: gui.State.Modes.Filtering.Active,
|
isActive: gui.State.Modes.Filtering.Active,
|
||||||
description: func() string {
|
description: func() string {
|
||||||
return style.FgRed.SetBold(true).Sprintf(
|
return style.FgRed.SetBold().Sprintf(
|
||||||
"%s '%s' %s",
|
"%s '%s' %s",
|
||||||
gui.Tr.LcFilteringBy,
|
gui.Tr.LcFilteringBy,
|
||||||
gui.State.Modes.Filtering.GetPath(),
|
gui.State.Modes.Filtering.GetPath(),
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"github.com/kyokomi/emoji/v2"
|
"github.com/kyokomi/emoji/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cherryPickedCommitTextStyle = style.FgCyan.MergeStyle(style.BgBlue)
|
||||||
|
|
||||||
func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string, parseEmoji bool) [][]string {
|
func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string, parseEmoji bool) [][]string {
|
||||||
lines := make([][]string, len(commits))
|
lines := make([][]string, len(commits))
|
||||||
|
|
||||||
@ -49,7 +51,7 @@ func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCom
|
|||||||
// for some reason, setting the background to blue pads out the other commits
|
// for some reason, setting the background to blue pads out the other commits
|
||||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||||
// not a bug
|
// not a bug
|
||||||
shaColor = style.FgCyan.SetColor(style.BgBlue)
|
shaColor = cherryPickedCommitTextStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
tagString := ""
|
tagString := ""
|
||||||
@ -57,7 +59,7 @@ func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCom
|
|||||||
if c.Action != "" {
|
if c.Action != "" {
|
||||||
secondColumnString = actionColorMap(c.Action).Sprint(c.Action)
|
secondColumnString = actionColorMap(c.Action).Sprint(c.Action)
|
||||||
} else if c.ExtraInfo != "" {
|
} else if c.ExtraInfo != "" {
|
||||||
tagString = theme.DiffTerminalColor.SetBold(true).Sprint(c.ExtraInfo) + " "
|
tagString = theme.DiffTerminalColor.SetBold().Sprint(c.ExtraInfo) + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
|
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
|
||||||
@ -96,7 +98,7 @@ func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[s
|
|||||||
// for some reason, setting the background to blue pads out the other commits
|
// for some reason, setting the background to blue pads out the other commits
|
||||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||||
// not a bug
|
// not a bug
|
||||||
shaColor = style.FgCyan.SetColor(style.BgBlue)
|
shaColor = cherryPickedCommitTextStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
actionString := ""
|
actionString := ""
|
||||||
@ -104,7 +106,7 @@ func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[s
|
|||||||
if c.Action != "" {
|
if c.Action != "" {
|
||||||
actionString = actionColorMap(c.Action).Sprint(utils.WithPadding(c.Action, 7)) + " "
|
actionString = actionColorMap(c.Action).Sprint(utils.WithPadding(c.Action, 7)) + " "
|
||||||
} else if len(c.Tags) > 0 {
|
} else if len(c.Tags) > 0 {
|
||||||
tagString = theme.DiffTerminalColor.SetBold(true).Sprint(strings.Join(c.Tags, " ")) + " "
|
tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(c.Tags, " ")) + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
name := c.Name
|
name := c.Name
|
||||||
|
@ -29,7 +29,7 @@ func GetReflogCommitListDisplayStrings(commits []*models.Commit, fullDescription
|
|||||||
func coloredReflogSha(c *models.Commit, cherryPickedCommitShaMap map[string]bool) string {
|
func coloredReflogSha(c *models.Commit, cherryPickedCommitShaMap map[string]bool) string {
|
||||||
shaColor := style.FgBlue
|
shaColor := style.FgBlue
|
||||||
if cherryPickedCommitShaMap[c.Sha] {
|
if cherryPickedCommitShaMap[c.Sha] {
|
||||||
shaColor = style.FgCyan.SetColor(style.BgBlue)
|
shaColor = cherryPickedCommitTextStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
return shaColor.Sprint(c.ShortSha())
|
return shaColor.Sprint(c.ShortSha())
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
package style
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gookit/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BasicTextStyle struct {
|
|
||||||
fg color.Color
|
|
||||||
bg color.Color
|
|
||||||
opts []color.Color
|
|
||||||
|
|
||||||
style color.Style
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) Sprint(a ...interface{}) string {
|
|
||||||
return b.style.Sprint(a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) Sprintf(format string, a ...interface{}) string {
|
|
||||||
return b.style.Sprintf(format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) deriveStyle() BasicTextStyle {
|
|
||||||
// b.style[:0] makes sure to use the same slice memory
|
|
||||||
if b.fg == 0 {
|
|
||||||
// Fg is most of the time defined so we reverse the check
|
|
||||||
b.style = b.style[:0]
|
|
||||||
} else {
|
|
||||||
b.style = append(b.style[:0], b.fg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.bg != 0 {
|
|
||||||
b.style = append(b.style, b.bg)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.style = append(b.style, b.opts...)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) setOpt(opt color.Color, v bool, deriveIfChanged bool) BasicTextStyle {
|
|
||||||
if v {
|
|
||||||
// Add value
|
|
||||||
for _, listOpt := range b.opts {
|
|
||||||
if listOpt == opt {
|
|
||||||
// Option already added
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.opts = append(b.opts, opt)
|
|
||||||
} else {
|
|
||||||
// Remove value
|
|
||||||
for idx, listOpt := range b.opts {
|
|
||||||
if listOpt == opt {
|
|
||||||
b.opts = append(b.opts[:idx], b.opts[idx+1:]...)
|
|
||||||
|
|
||||||
if deriveIfChanged {
|
|
||||||
return b.deriveStyle()
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if deriveIfChanged {
|
|
||||||
return b.deriveStyle()
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) SetBold(v bool) TextStyle {
|
|
||||||
return b.setOpt(color.OpBold, v, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) SetReverse(v bool) TextStyle {
|
|
||||||
return b.setOpt(color.OpReverse, v, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) SetUnderline(v bool) TextStyle {
|
|
||||||
return b.setOpt(color.OpUnderscore, v, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle {
|
|
||||||
return b.convertToRGB().SetRGBColor(red, green, blue, background)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) convertToRGB() RGBTextStyle {
|
|
||||||
res := RGBTextStyle{
|
|
||||||
fg: b.fg.RGB(),
|
|
||||||
fgSet: b.fg != 0,
|
|
||||||
opts: b.opts,
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.bg != 0 {
|
|
||||||
// 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)
|
|
||||||
} else {
|
|
||||||
res.style = *color.NewRGBStyle(res.fg)
|
|
||||||
}
|
|
||||||
res.style.SetOpts(b.opts)
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BasicTextStyle) SetColor(other TextStyle) TextStyle {
|
|
||||||
switch typedOther := other.(type) {
|
|
||||||
case BasicTextStyle:
|
|
||||||
if typedOther.fg != 0 {
|
|
||||||
b.fg = typedOther.fg
|
|
||||||
}
|
|
||||||
if typedOther.bg != 0 {
|
|
||||||
b.bg = typedOther.bg
|
|
||||||
}
|
|
||||||
for _, opt := range typedOther.opts {
|
|
||||||
b = b.setOpt(opt, true, false)
|
|
||||||
}
|
|
||||||
return b.deriveStyle()
|
|
||||||
case RGBTextStyle:
|
|
||||||
bAsRGB := b.convertToRGB()
|
|
||||||
|
|
||||||
for _, opt := range typedOther.opts {
|
|
||||||
bAsRGB.setOpt(opt, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if typedOther.fgSet {
|
|
||||||
bAsRGB.fg = typedOther.fg
|
|
||||||
bAsRGB.style.SetFg(typedOther.fg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if typedOther.bg != nil {
|
|
||||||
// Making sure to copy the value
|
|
||||||
bAsRGB.bg = &color.RGBColor{}
|
|
||||||
*bAsRGB.bg = *typedOther.bg
|
|
||||||
bAsRGB.style.SetBg(*typedOther.bg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bAsRGB
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedOther))
|
|
||||||
}
|
|
||||||
}
|
|
38
pkg/gui/style/basic_styles.go
Normal file
38
pkg/gui/style/basic_styles.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package style
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gookit/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FgWhite = FromBasicFg(color.FgWhite)
|
||||||
|
FgLightWhite = FromBasicFg(color.FgLightWhite)
|
||||||
|
FgBlack = FromBasicFg(color.FgBlack)
|
||||||
|
FgBlackLighter = FromBasicFg(color.FgBlack.Light())
|
||||||
|
FgCyan = FromBasicFg(color.FgCyan)
|
||||||
|
FgRed = FromBasicFg(color.FgRed)
|
||||||
|
FgGreen = FromBasicFg(color.FgGreen)
|
||||||
|
FgBlue = FromBasicFg(color.FgBlue)
|
||||||
|
FgYellow = FromBasicFg(color.FgYellow)
|
||||||
|
FgMagenta = FromBasicFg(color.FgMagenta)
|
||||||
|
|
||||||
|
BgWhite = FromBasicBg(color.BgWhite)
|
||||||
|
BgBlack = FromBasicBg(color.BgBlack)
|
||||||
|
BgRed = FromBasicBg(color.BgRed)
|
||||||
|
BgGreen = FromBasicBg(color.BgGreen)
|
||||||
|
BgYellow = FromBasicBg(color.BgYellow)
|
||||||
|
BgBlue = FromBasicBg(color.BgBlue)
|
||||||
|
BgMagenta = FromBasicBg(color.BgMagenta)
|
||||||
|
BgCyan = FromBasicBg(color.BgCyan)
|
||||||
|
|
||||||
|
AttrUnderline = New().SetUnderline()
|
||||||
|
AttrBold = New().SetBold()
|
||||||
|
)
|
||||||
|
|
||||||
|
func FromBasicFg(fg color.Color) TextStyle {
|
||||||
|
return New().SetFg(NewBasicColor(fg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromBasicBg(bg color.Color) TextStyle {
|
||||||
|
return New().SetBg(NewBasicColor(bg))
|
||||||
|
}
|
32
pkg/gui/style/color.go
Normal file
32
pkg/gui/style/color.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package style
|
||||||
|
|
||||||
|
import "github.com/gookit/color"
|
||||||
|
|
||||||
|
type Color struct {
|
||||||
|
rgb *color.RGBColor
|
||||||
|
basic *color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRGBColor(cl color.RGBColor) Color {
|
||||||
|
c := Color{}
|
||||||
|
c.rgb = &cl
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasicColor(cl color.Color) Color {
|
||||||
|
c := Color{}
|
||||||
|
c.basic = &cl
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Color) IsRGB() bool {
|
||||||
|
return c.rgb != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Color) ToRGB() Color {
|
||||||
|
if c.IsRGB() {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewRGBColor(c.basic.RGB())
|
||||||
|
}
|
55
pkg/gui/style/decoration.go
Normal file
55
pkg/gui/style/decoration.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package style
|
||||||
|
|
||||||
|
import "github.com/gookit/color"
|
||||||
|
|
||||||
|
type Decoration struct {
|
||||||
|
bold bool
|
||||||
|
underline bool
|
||||||
|
reverse bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Decoration) SetBold() {
|
||||||
|
d.bold = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Decoration) SetUnderline() {
|
||||||
|
d.underline = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Decoration) SetReverse() {
|
||||||
|
d.reverse = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Decoration) ToOpts() color.Opts {
|
||||||
|
opts := make([]color.Color, 0, 3)
|
||||||
|
|
||||||
|
if d.bold {
|
||||||
|
opts = append(opts, color.OpBold)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.underline {
|
||||||
|
opts = append(opts, color.OpUnderscore)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.reverse {
|
||||||
|
opts = append(opts, color.OpReverse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
@ -1,111 +0,0 @@
|
|||||||
package style
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gookit/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RGBTextStyle struct {
|
|
||||||
opts color.Opts
|
|
||||||
fgSet bool
|
|
||||||
fg color.RGBColor
|
|
||||||
bg *color.RGBColor
|
|
||||||
style color.RGBStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) Sprint(a ...interface{}) string {
|
|
||||||
return b.style.Sprint(a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) Sprintf(format string, a ...interface{}) string {
|
|
||||||
return b.style.Sprintf(format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) setOpt(opt color.Color, v bool) RGBTextStyle {
|
|
||||||
if v {
|
|
||||||
// Add value
|
|
||||||
for _, listOpt := range b.opts {
|
|
||||||
if listOpt == opt {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.opts = append(b.opts, opt)
|
|
||||||
} else {
|
|
||||||
// Remove value
|
|
||||||
for idx, listOpt := range b.opts {
|
|
||||||
if listOpt == opt {
|
|
||||||
b.opts = append(b.opts[:idx], b.opts[idx+1:]...)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) SetBold(v bool) TextStyle {
|
|
||||||
b = b.setOpt(color.OpBold, v)
|
|
||||||
b.style.SetOpts(b.opts)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) SetReverse(v bool) TextStyle {
|
|
||||||
b = b.setOpt(color.OpReverse, v)
|
|
||||||
b.style.SetOpts(b.opts)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) SetUnderline(v bool) TextStyle {
|
|
||||||
b = b.setOpt(color.OpUnderscore, v)
|
|
||||||
b.style.SetOpts(b.opts)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) SetColor(style TextStyle) TextStyle {
|
|
||||||
var rgbStyle RGBTextStyle
|
|
||||||
|
|
||||||
switch typedStyle := style.(type) {
|
|
||||||
case BasicTextStyle:
|
|
||||||
rgbStyle = typedStyle.convertToRGB()
|
|
||||||
case RGBTextStyle:
|
|
||||||
rgbStyle = typedStyle
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedStyle))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, opt := range rgbStyle.GetOpts() {
|
|
||||||
b = b.setOpt(opt, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rgbStyle.fgSet {
|
|
||||||
b.fg = rgbStyle.fg
|
|
||||||
b.style.SetFg(rgbStyle.fg)
|
|
||||||
b.fgSet = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if rgbStyle.bg != nil {
|
|
||||||
// Making sure to copy value
|
|
||||||
b.bg = &color.RGBColor{}
|
|
||||||
*b.bg = *rgbStyle.bg
|
|
||||||
b.style.SetBg(*rgbStyle.bg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle {
|
|
||||||
parsedColor := color.Rgb(red, green, blue, background)
|
|
||||||
if background {
|
|
||||||
b.bg = &parsedColor
|
|
||||||
b.style.SetBg(parsedColor)
|
|
||||||
} else {
|
|
||||||
b.fg = parsedColor
|
|
||||||
b.style.SetFg(parsedColor)
|
|
||||||
b.fgSet = true
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b RGBTextStyle) GetOpts() color.Opts {
|
|
||||||
return b.opts
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
package style
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gookit/color"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TextStyle interface {
|
|
||||||
Sprint(a ...interface{}) string
|
|
||||||
Sprintf(format string, a ...interface{}) string
|
|
||||||
SetBold(v bool) TextStyle
|
|
||||||
SetReverse(v bool) TextStyle
|
|
||||||
SetUnderline(v bool) TextStyle
|
|
||||||
SetColor(style TextStyle) TextStyle
|
|
||||||
SetRGBColor(r, g, b uint8, background bool) TextStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
FgWhite = New(color.FgWhite, 0)
|
|
||||||
FgLightWhite = New(color.FgLightWhite, 0)
|
|
||||||
FgBlack = New(color.FgBlack, 0)
|
|
||||||
FgBlackLighter = New(color.FgBlack.Light(), 0)
|
|
||||||
FgCyan = New(color.FgCyan, 0)
|
|
||||||
FgRed = New(color.FgRed, 0)
|
|
||||||
FgGreen = New(color.FgGreen, 0)
|
|
||||||
FgBlue = New(color.FgBlue, 0)
|
|
||||||
FgYellow = New(color.FgYellow, 0)
|
|
||||||
FgMagenta = New(color.FgMagenta, 0)
|
|
||||||
|
|
||||||
BgWhite = New(0, color.BgWhite)
|
|
||||||
BgBlack = New(0, color.BgBlack)
|
|
||||||
BgRed = New(0, color.BgRed)
|
|
||||||
BgGreen = New(0, color.BgGreen)
|
|
||||||
BgYellow = New(0, color.BgYellow)
|
|
||||||
BgBlue = New(0, color.BgBlue)
|
|
||||||
BgMagenta = New(0, color.BgMagenta)
|
|
||||||
BgCyan = New(0, color.BgCyan)
|
|
||||||
|
|
||||||
AttrUnderline = New(0, 0).SetUnderline(true)
|
|
||||||
AttrBold = New(0, 0).SetUnderline(true)
|
|
||||||
)
|
|
||||||
|
|
||||||
func New(fg color.Color, bg color.Color, opts ...color.Color) TextStyle {
|
|
||||||
return BasicTextStyle{
|
|
||||||
fg: fg,
|
|
||||||
bg: bg,
|
|
||||||
opts: opts,
|
|
||||||
style: color.Style{},
|
|
||||||
}.deriveStyle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetConfigStyles(s TextStyle, keys []string, background bool) TextStyle {
|
|
||||||
for _, key := range keys {
|
|
||||||
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},
|
|
||||||
}
|
|
||||||
value, present := colorMap[key]
|
|
||||||
if present {
|
|
||||||
if background {
|
|
||||||
s = s.SetColor(value.background)
|
|
||||||
} else {
|
|
||||||
s = s.SetColor(value.forground)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if key == "bold" {
|
|
||||||
s = s.SetBold(true)
|
|
||||||
continue
|
|
||||||
} else if key == "reverse" {
|
|
||||||
s = s.SetReverse(true)
|
|
||||||
continue
|
|
||||||
} else if key == "underline" {
|
|
||||||
s = s.SetUnderline(true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
r, g, b, validHexColor := utils.GetHexColorValues(key)
|
|
||||||
if validHexColor {
|
|
||||||
s = s.SetRGBColor(r, g, b, background)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
@ -53,39 +53,39 @@ func TestNewStyle(t *testing.T) {
|
|||||||
func TestBasicSetColor(t *testing.T) {
|
func TestBasicSetColor(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
name string
|
name string
|
||||||
colorToSet BasicTextStyle
|
colorToSet TextStyle
|
||||||
expect BasicTextStyle
|
expect TextStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"empty color",
|
"empty color",
|
||||||
BasicTextStyle{},
|
TextStyle{},
|
||||||
BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}},
|
TextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}},
|
||||||
{
|
{
|
||||||
"set new fg color",
|
"set new fg color",
|
||||||
BasicTextStyle{fg: color.FgCyan},
|
TextStyle{fg: color.FgCyan},
|
||||||
BasicTextStyle{fg: color.FgCyan, bg: color.BgBlue, opts: []color.Color{color.OpBold}},
|
TextStyle{fg: color.FgCyan, bg: color.BgBlue, opts: []color.Color{color.OpBold}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"set new bg color",
|
"set new bg color",
|
||||||
BasicTextStyle{bg: color.BgGray},
|
TextStyle{bg: color.BgGray},
|
||||||
BasicTextStyle{fg: color.FgRed, bg: color.BgGray, opts: []color.Color{color.OpBold}},
|
TextStyle{fg: color.FgRed, bg: color.BgGray, opts: []color.Color{color.OpBold}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"set new fg and bg color",
|
"set new fg and bg color",
|
||||||
BasicTextStyle{fg: color.FgCyan, bg: color.BgGray},
|
TextStyle{fg: color.FgCyan, bg: color.BgGray},
|
||||||
BasicTextStyle{fg: color.FgCyan, bg: color.BgGray, opts: []color.Color{color.OpBold}},
|
TextStyle{fg: color.FgCyan, bg: color.BgGray, opts: []color.Color{color.OpBold}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"add options",
|
"add options",
|
||||||
BasicTextStyle{opts: []color.Color{color.OpUnderscore}},
|
TextStyle{opts: []color.Color{color.OpUnderscore}},
|
||||||
BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold, color.OpUnderscore}},
|
TextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold, color.OpUnderscore}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"add options that already exists",
|
"add options that already exists",
|
||||||
BasicTextStyle{opts: []color.Color{color.OpBold}},
|
TextStyle{opts: []color.Color{color.OpBold}},
|
||||||
BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}},
|
TextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,12 +127,12 @@ func TestRGBSetColor(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"empty BasicTextStyle input",
|
"empty BasicTextStyle input",
|
||||||
BasicTextStyle{},
|
TextStyle{},
|
||||||
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold}},
|
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"set fg and bg color using BasicTextStyle",
|
"set fg and bg color using BasicTextStyle",
|
||||||
BasicTextStyle{fg: color.FgCyan, bg: color.BgGray},
|
TextStyle{fg: color.FgCyan, bg: color.BgGray},
|
||||||
RGBTextStyle{fgSet: true, fg: cyan, bg: toBg(gray), opts: []color.Color{color.OpBold}},
|
RGBTextStyle{fgSet: true, fg: cyan, bg: toBg(gray), opts: []color.Color{color.OpBold}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -147,7 +147,7 @@ func TestRGBSetColor(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"add options using BasicTextStyle",
|
"add options using BasicTextStyle",
|
||||||
BasicTextStyle{opts: []color.Color{color.OpUnderscore}},
|
TextStyle{opts: []color.Color{color.OpUnderscore}},
|
||||||
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold, color.OpUnderscore}},
|
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold, color.OpUnderscore}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -159,10 +159,10 @@ func TestRGBSetColor(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
style, ok := New(color.FgRed, color.BgBlue).SetBold(true).(BasicTextStyle)
|
style, ok := New(color.FgRed, color.BgBlue).SetBold().(BasicTextStyle)
|
||||||
assert.True(t, ok, "SetBold should return a interface of type BasicTextStyle")
|
assert.True(t, ok, "SetBold should return a interface of type BasicTextStyle")
|
||||||
|
|
||||||
rgbStyle, ok := style.convertToRGB().SetColor(s.colorToSet).(RGBTextStyle)
|
rgbStyle, ok := style.convertToRGB().MergeStyle(s.colorToSet).(RGBTextStyle)
|
||||||
assert.True(t, ok, "SetColor should return a interface of type RGBTextColor")
|
assert.True(t, ok, "SetColor should return a interface of type RGBTextColor")
|
||||||
|
|
||||||
rgbStyle.style = color.RGBStyle{}
|
rgbStyle.style = color.RGBStyle{}
|
||||||
|
147
pkg/gui/style/text_style.go
Normal file
147
pkg/gui/style/text_style.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package style
|
||||||
|
|
||||||
|
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 {
|
||||||
|
Sprint(a ...interface{}) string
|
||||||
|
Sprintf(format string, a ...interface{}) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() TextStyle {
|
||||||
|
s := TextStyle{}
|
||||||
|
s.style = s.deriveStyle()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b TextStyle) Sprint(a ...interface{}) string {
|
||||||
|
return b.style.Sprint(a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b TextStyle) Sprintf(format string, a ...interface{}) string {
|
||||||
|
return b.style.Sprintf(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// note that our receiver here is not a pointer which means we're receiving a
|
||||||
|
// copy of the original TextStyle. This allows us to mutate and return that
|
||||||
|
// TextStyle receiver without actually modifying the original.
|
||||||
|
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) SetBg(color Color) TextStyle {
|
||||||
|
b.bg = &color
|
||||||
|
b.style = b.deriveStyle()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b TextStyle) SetFg(color Color) TextStyle {
|
||||||
|
b.fg = &color
|
||||||
|
b.style = b.deriveStyle()
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b TextStyle) MergeStyle(other TextStyle) TextStyle {
|
||||||
|
b.decoration = b.decoration.Merge(other.decoration)
|
||||||
|
|
||||||
|
if other.fg != nil {
|
||||||
|
b.fg = other.fg
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.bg != nil {
|
||||||
|
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
|
||||||
|
}
|
45
pkg/theme/gocui.go
Normal file
45
pkg/theme/gocui.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package theme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gookit/color"
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gocuiColorMap = map[string]gocui.Attribute{
|
||||||
|
"default": gocui.ColorDefault,
|
||||||
|
"black": gocui.ColorBlack,
|
||||||
|
"red": gocui.ColorRed,
|
||||||
|
"green": gocui.ColorGreen,
|
||||||
|
"yellow": gocui.ColorYellow,
|
||||||
|
"blue": gocui.ColorBlue,
|
||||||
|
"magenta": gocui.ColorMagenta,
|
||||||
|
"cyan": gocui.ColorCyan,
|
||||||
|
"white": gocui.ColorWhite,
|
||||||
|
"bold": gocui.AttrBold,
|
||||||
|
"reverse": gocui.AttrReverse,
|
||||||
|
"underline": gocui.AttrUnderline,
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttribute gets the gocui color attribute from the string
|
||||||
|
func GetGocuiAttribute(key string) gocui.Attribute {
|
||||||
|
if utils.IsValidHexValue(key) {
|
||||||
|
values := color.HEX(key).Values()
|
||||||
|
return gocui.NewRGBColor(int32(values[0]), int32(values[1]), int32(values[2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
value, present := gocuiColorMap[key]
|
||||||
|
if present {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return gocui.ColorWhite
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
57
pkg/theme/style.go
Normal file
57
pkg/theme/style.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package theme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gookit/color"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
package theme
|
package theme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gookit/color"
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -24,31 +22,31 @@ var (
|
|||||||
OptionsColor gocui.Attribute
|
OptionsColor gocui.Attribute
|
||||||
|
|
||||||
// DefaultTextColor is the default text color
|
// DefaultTextColor is the default text color
|
||||||
DefaultTextColor = style.New(color.FgWhite, 0)
|
DefaultTextColor = style.FgWhite
|
||||||
|
|
||||||
// DefaultHiTextColor is the default highlighted text color
|
// DefaultHiTextColor is the default highlighted text color
|
||||||
DefaultHiTextColor = style.New(color.FgLightWhite, 0)
|
DefaultHiTextColor = style.FgLightWhite
|
||||||
|
|
||||||
// SelectedLineBgColor is the background color for the selected line
|
// SelectedLineBgColor is the background color for the selected line
|
||||||
SelectedLineBgColor = style.New(0, 0)
|
SelectedLineBgColor = style.New()
|
||||||
|
|
||||||
// SelectedRangeBgColor is the background color of the selected range of lines
|
// SelectedRangeBgColor is the background color of the selected range of lines
|
||||||
SelectedRangeBgColor = style.New(0, 0)
|
SelectedRangeBgColor = style.New()
|
||||||
|
|
||||||
OptionsFgColor = style.New(0, 0)
|
OptionsFgColor = style.New()
|
||||||
|
|
||||||
DiffTerminalColor = style.New(color.FgMagenta, 0)
|
DiffTerminalColor = style.FgMagenta
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateTheme updates all theme variables
|
// UpdateTheme updates all theme variables
|
||||||
func UpdateTheme(themeConfig config.ThemeConfig) {
|
func UpdateTheme(themeConfig config.ThemeConfig) {
|
||||||
ActiveBorderColor = GetGocuiColor(themeConfig.ActiveBorderColor)
|
ActiveBorderColor = GetGocuiStyle(themeConfig.ActiveBorderColor)
|
||||||
InactiveBorderColor = GetGocuiColor(themeConfig.InactiveBorderColor)
|
InactiveBorderColor = GetGocuiStyle(themeConfig.InactiveBorderColor)
|
||||||
SelectedLineBgColor = style.SetConfigStyles(SelectedLineBgColor, themeConfig.SelectedLineBgColor, true)
|
SelectedLineBgColor = GetTextStyle(themeConfig.SelectedLineBgColor, true)
|
||||||
SelectedRangeBgColor = style.SetConfigStyles(SelectedRangeBgColor, themeConfig.SelectedRangeBgColor, true)
|
SelectedRangeBgColor = GetTextStyle(themeConfig.SelectedRangeBgColor, true)
|
||||||
GocuiSelectedLineBgColor = GetGocuiColor(themeConfig.SelectedLineBgColor)
|
GocuiSelectedLineBgColor = GetGocuiStyle(themeConfig.SelectedLineBgColor)
|
||||||
OptionsColor = GetGocuiColor(themeConfig.OptionsTextColor)
|
OptionsColor = GetGocuiStyle(themeConfig.OptionsTextColor)
|
||||||
OptionsFgColor = style.SetConfigStyles(OptionsFgColor, themeConfig.OptionsTextColor, false)
|
OptionsFgColor = GetTextStyle(themeConfig.OptionsTextColor, false)
|
||||||
|
|
||||||
isLightTheme := themeConfig.LightTheme
|
isLightTheme := themeConfig.LightTheme
|
||||||
if isLightTheme {
|
if isLightTheme {
|
||||||
@ -61,40 +59,3 @@ func UpdateTheme(themeConfig config.ThemeConfig) {
|
|||||||
GocuiDefaultTextColor = gocui.ColorWhite
|
GocuiDefaultTextColor = gocui.ColorWhite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttribute gets the gocui color attribute from the string
|
|
||||||
func GetGocuiAttribute(key string) gocui.Attribute {
|
|
||||||
r, g, b, validHexColor := utils.GetHexColorValues(key)
|
|
||||||
if validHexColor {
|
|
||||||
return gocui.NewRGBColor(int32(r), int32(g), int32(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
colorMap := map[string]gocui.Attribute{
|
|
||||||
"default": gocui.ColorDefault,
|
|
||||||
"black": gocui.ColorBlack,
|
|
||||||
"red": gocui.ColorRed,
|
|
||||||
"green": gocui.ColorGreen,
|
|
||||||
"yellow": gocui.ColorYellow,
|
|
||||||
"blue": gocui.ColorBlue,
|
|
||||||
"magenta": gocui.ColorMagenta,
|
|
||||||
"cyan": gocui.ColorCyan,
|
|
||||||
"white": gocui.ColorWhite,
|
|
||||||
"bold": gocui.AttrBold,
|
|
||||||
"reverse": gocui.AttrReverse,
|
|
||||||
"underline": gocui.AttrUnderline,
|
|
||||||
}
|
|
||||||
value, present := colorMap[key]
|
|
||||||
if present {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return gocui.ColorWhite
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGocuiColor bitwise OR's a list of attributes obtained via the given keys
|
|
||||||
func GetGocuiColor(keys []string) gocui.Attribute {
|
|
||||||
var attribute gocui.Attribute
|
|
||||||
for _, key := range keys {
|
|
||||||
attribute |= GetGocuiAttribute(key)
|
|
||||||
}
|
|
||||||
return attribute
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,22 +32,23 @@ func getPadWidths(stringArrays [][]string) []int {
|
|||||||
return padWidths
|
return padWidths
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHexColorValues returns the rgb values of a hex color
|
func IsValidHexValue(v string) bool {
|
||||||
func GetHexColorValues(v string) (r uint8, g uint8, b uint8, valid bool) {
|
if len(v) != 4 && len(v) != 7 {
|
||||||
if len(v) == 4 {
|
return false
|
||||||
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] != '#' {
|
if v[0] != '#' {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
rgb, err := hex.DecodeString(v[1:])
|
for _, char := range v[1:] {
|
||||||
if err != nil {
|
switch char {
|
||||||
return
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F':
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rgb[0], rgb[1], rgb[2], true
|
return true
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user