diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b1fa82..ee88c41f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,10 +44,10 @@ Non-matching items are displayed in a dimmed color by default, but you can change it with the `--color hidden:...` option. ```sh +fzf --raw --color hidden:red fzf --raw --color hidden:red:strikethrough - -# To unset the default 'dim' attribute, prefix the color spec with 'regular' -fzf --raw --color hidden:regular:red:strikethrough +fzf --raw --color hidden:red:strikethrough:dim +fzf --raw --color hidden:red:strikethrough:dim:italic ``` #### Conditional actions for raw mode @@ -104,7 +104,37 @@ fzf --gutter ' ' --color gutter:reverse As noted above, the `--gutter-raw CHAR` option was also added for customizing the gutter column in raw mode. -### Compatibility changes +### Breaking changes + +#### Hiding the gutter column + +In the previous versions, the recommended way to hide the gutter column was to +set `--color gutter:-1`. That's because the gutter column was just a space +character, reversed. But now that it's using a visible character (`▌`), applying +the default color is no longer enough to hide it. Instead, you can set it to +a space character. + +```sh +# Hide the gutter column +fzf --gutter ' ' + +# Classic style +fzf --gutter ' ' --color gutter:reverse +``` + +#### `--color` option + +In the previous versions, some elements had default style attributes applied and +you would have to explicitly unset them with `regular` attribute if you wanted +to reset them. This is no longer needed now, as the default style attributes +are applied only when you do not specify any color or style for that element. + +```sh +# No 'dim', just red and italic. +fzf --ghost 'Type to search' --color ghost:red:italic +``` + +#### Compatibility changes Starting with this release, fzf is built with Go 1.23. Support for some old OS versions has been dropped. diff --git a/src/options.go b/src/options.go index d3be6821..79e016b6 100644 --- a/src/options.go +++ b/src/options.go @@ -572,6 +572,7 @@ type Options struct { Multi int Ansi bool Mouse bool + BaseTheme *tui.ColorTheme Theme *tui.ColorTheme Black bool Bold bool @@ -672,9 +673,9 @@ func defaultPreviewOpts(command string) previewOpts { func defaultOptions() *Options { var theme *tui.ColorTheme if os.Getenv("NO_COLOR") != "" { - theme = tui.NoColorTheme() + theme = tui.NoColorTheme } else { - theme = tui.EmptyTheme() + theme = tui.EmptyTheme } return &Options{ @@ -1317,8 +1318,9 @@ func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme { return &dupe } -func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, error) { +func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui.ColorTheme, error) { var err error + var baseTheme *tui.ColorTheme theme := dupeTheme(defaultTheme) rrggbb := regexp.MustCompile("^#[0-9a-fA-F]{6}$") comma := regexp.MustCompile(`[\s,]+`) @@ -1329,13 +1331,17 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro } switch str { case "dark": + baseTheme = tui.Dark256 theme = dupeTheme(tui.Dark256) case "light": + baseTheme = tui.Light256 theme = dupeTheme(tui.Light256) case "base16", "16": + baseTheme = tui.Default16 theme = dupeTheme(tui.Default16) case "bw", "no": - theme = tui.NoColorTheme() + baseTheme = tui.NoColorTheme + theme = dupeTheme(tui.NoColorTheme) default: fail := func() { // Let the code proceed to simplify the error handling @@ -1514,7 +1520,7 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, erro } } } - return theme, err + return baseTheme, theme, err } func parseWalkerOpts(str string) (walkerOpts, error) { @@ -2649,11 +2655,15 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { case "--color": _, spec := optionalNextString() if len(spec) == 0 { - opts.Theme = tui.EmptyTheme() + opts.Theme = tui.EmptyTheme } else { - if opts.Theme, err = parseTheme(opts.Theme, spec); err != nil { + var baseTheme *tui.ColorTheme + if baseTheme, opts.Theme, err = parseTheme(opts.Theme, spec); err != nil { return err } + if baseTheme != nil { + opts.BaseTheme = baseTheme + } } case "--toggle-sort": str, err := nextString("key name required") @@ -2739,7 +2749,8 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { case "--no-mouse": opts.Mouse = false case "+c", "--no-color": - opts.Theme = tui.NoColorTheme() + opts.BaseTheme = tui.NoColorTheme + opts.Theme = tui.NoColorTheme case "+2", "--no-256": opts.Theme = tui.Default16 case "--black": @@ -3616,23 +3627,6 @@ func postProcessOptions(opts *Options) error { } } - if opts.Bold { - theme := opts.Theme - boldify := func(c tui.ColorAttr) tui.ColorAttr { - dup := c - if (c.Attr & tui.AttrRegular) == 0 { - dup.Attr |= tui.BoldForce - } - return dup - } - theme.Current = boldify(theme.Current) - theme.CurrentMatch = boldify(theme.CurrentMatch) - theme.Prompt = boldify(theme.Prompt) - theme.Input = boldify(theme.Input) - theme.Cursor = boldify(theme.Cursor) - theme.Spinner = boldify(theme.Spinner) - } - // If --height option is not supported on the platform, just ignore it if !tui.IsLightRendererSupported() && opts.Height.size > 0 { opts.Height = heightSpec{} diff --git a/src/options_test.go b/src/options_test.go index 7b152d75..1494d960 100644 --- a/src/options_test.go +++ b/src/options_test.go @@ -300,8 +300,12 @@ func TestBind(t *testing.T) { } func TestColorSpec(t *testing.T) { + var base *tui.ColorTheme theme := tui.Dark256 - dark, _ := parseTheme(theme, "dark") + base, dark, _ := parseTheme(theme, "dark") + if *dark != *base { + t.Errorf("incorrect base theme returned") + } if *dark != *theme { t.Errorf("colors should be equivalent") } @@ -309,7 +313,10 @@ func TestColorSpec(t *testing.T) { t.Errorf("point should not be equivalent") } - light, _ := parseTheme(theme, "dark,light") + base, light, _ := parseTheme(theme, "dark,light") + if *light != *base { + t.Errorf("incorrect base theme returned") + } if *light == *theme { t.Errorf("should not be equivalent") } @@ -320,7 +327,7 @@ func TestColorSpec(t *testing.T) { t.Errorf("point should not be equivalent") } - customized, _ := parseTheme(theme, "fg:231,bg:232") + _, customized, _ := parseTheme(theme, "fg:231,bg:232") if customized.Fg.Color != 231 || customized.Bg.Color != 232 { t.Errorf("color not customized") } @@ -333,7 +340,7 @@ func TestColorSpec(t *testing.T) { t.Errorf("colors should now be equivalent: %v, %v", tui.Dark256, customized) } - customized, _ = parseTheme(theme, "fg:231,dark bg:232") + _, customized, _ = parseTheme(theme, "fg:231,dark bg:232") if customized.Fg != tui.Dark256.Fg || customized.Bg == tui.Dark256.Bg { t.Errorf("color not customized") } diff --git a/src/terminal.go b/src/terminal.go index f3b9ccda..5d578540 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -1109,14 +1109,18 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor t.acceptNth = opts.AcceptNth(t.delimiter) } + baseTheme := opts.BaseTheme + if baseTheme == nil { + baseTheme = renderer.DefaultTheme() + } // This should be called before accessing tui.Color* - tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible()) + tui.InitTheme(opts.Theme, baseTheme, opts.Bold, opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible()) // Gutter character var gutterChar, gutterRawChar string if opts.Gutter != nil { gutterChar = *opts.Gutter - } else if t.unicode && !t.theme.Gutter.Color.IsDefault() { + } else if t.unicode { gutterChar = "▌" } else { gutterChar = " " @@ -1125,7 +1129,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor if opts.GutterRaw != nil { gutterRawChar = *opts.GutterRaw - } else if t.unicode && !t.theme.Gutter.Color.IsDefault() { + } else if t.unicode { gutterRawChar = "▖" } else { gutterRawChar = ":" diff --git a/src/tui/dummy.go b/src/tui/dummy.go index a9888036..cb0ce0da 100644 --- a/src/tui/dummy.go +++ b/src/tui/dummy.go @@ -2,30 +2,7 @@ package tui -type Attr int32 - -func HasFullscreenRenderer() bool { - return false -} - -var DefaultBorderShape = BorderRounded - -func (a Attr) Merge(b Attr) Attr { - if b&AttrRegular > 0 { - // Only keep bold attribute set by the system - return (b &^ AttrRegular) | (a & BoldForce) - } - - return (a &^ AttrRegular) | b -} - const ( - AttrUndefined = Attr(0) - AttrRegular = Attr(1 << 8) - AttrClear = Attr(1 << 9) - BoldForce = Attr(1 << 10) - FullBg = Attr(1 << 11) - Bold = Attr(1) Dim = Attr(1 << 1) Italic = Attr(1 << 2) @@ -36,6 +13,12 @@ const ( StrikeThrough = Attr(1 << 7) ) +func HasFullscreenRenderer() bool { + return false +} + +var DefaultBorderShape = BorderRounded + func (r *FullscreenRenderer) Init() error { return nil } func (r *FullscreenRenderer) DefaultTheme() *ColorTheme { return nil } func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {} diff --git a/src/tui/tcell.go b/src/tui/tcell.go index e4a6a988..f7c6884f 100644 --- a/src/tui/tcell.go +++ b/src/tui/tcell.go @@ -36,8 +36,6 @@ func (p ColorPair) style() tcell.Style { return style.Foreground(asTcellColor(p.Fg())).Background(asTcellColor(p.Bg())) } -type Attr int32 - type TcellWindow struct { color bool windowType WindowType @@ -98,14 +96,6 @@ const ( Italic = Attr(tcell.AttrItalic) ) -const ( - AttrUndefined = Attr(0) - AttrRegular = Attr(1 << 7) - AttrClear = Attr(1 << 8) - BoldForce = Attr(1 << 10) - FullBg = Attr(1 << 11) -) - func (r *FullscreenRenderer) Bell() { _screen.Beep() } @@ -159,15 +149,6 @@ func (c Color) Style() tcell.Color { } } -func (a Attr) Merge(b Attr) Attr { - if b&AttrRegular > 0 { - // Only keep bold attribute set by the system - return (b &^ AttrRegular) | (a & BoldForce) - } - - return (a &^ AttrRegular) | b -} - // handle the following as private members of FullscreenRenderer instance // they are declared here to prevent introducing tcell library in non-windows builds var ( diff --git a/src/tui/tui.go b/src/tui/tui.go index ae9e8c49..563d3c2a 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -8,6 +8,25 @@ import ( "github.com/rivo/uniseg" ) +type Attr int32 + +const ( + AttrUndefined = Attr(0) + AttrRegular = Attr(1 << 8) + AttrClear = Attr(1 << 9) + BoldForce = Attr(1 << 10) + FullBg = Attr(1 << 11) +) + +func (a Attr) Merge(b Attr) Attr { + if b&AttrRegular > 0 { + // Only keep bold attribute set by the system + return (b &^ AttrRegular) | (a & BoldForce) + } + + return (a &^ AttrRegular) | b +} + // Types of user action // //go:generate stringer -type=EventType @@ -275,6 +294,13 @@ func (a ColorAttr) IsColorDefined() bool { return a.Color != colUndefined } +func (a ColorAttr) IsAttrDefined() bool { + return a.Attr != AttrUndefined +} +func (a ColorAttr) IsUndefined() bool { + return !a.IsColorDefined() && !a.IsAttrDefined() +} + func NewColorAttr() ColorAttr { return ColorAttr{Color: colUndefined, Attr: AttrUndefined} } @@ -779,9 +805,11 @@ func NewFullscreenRenderer(theme *ColorTheme, forceBlack bool, mouse bool) Rende } var ( - Default16 *ColorTheme - Dark256 *ColorTheme - Light256 *ColorTheme + NoColorTheme *ColorTheme + EmptyTheme *ColorTheme + Default16 *ColorTheme + Dark256 *ColorTheme + Light256 *ColorTheme ColPrompt ColorPair ColNormal ColorPair @@ -825,121 +853,120 @@ var ( ColInputLabel ColorPair ) -func EmptyTheme() *ColorTheme { - return &ColorTheme{ - Colored: true, - Input: ColorAttr{colUndefined, AttrUndefined}, - Fg: ColorAttr{colUndefined, AttrUndefined}, - Bg: ColorAttr{colUndefined, AttrUndefined}, - ListFg: ColorAttr{colUndefined, AttrUndefined}, - ListBg: ColorAttr{colUndefined, AttrUndefined}, - AltBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedFg: ColorAttr{colUndefined, AttrUndefined}, - SelectedBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, - DarkBg: ColorAttr{colUndefined, AttrUndefined}, - Prompt: ColorAttr{colUndefined, AttrUndefined}, - Match: ColorAttr{colUndefined, AttrUndefined}, - Current: ColorAttr{colUndefined, AttrUndefined}, - CurrentMatch: ColorAttr{colUndefined, AttrUndefined}, - Spinner: ColorAttr{colUndefined, AttrUndefined}, - Info: ColorAttr{colUndefined, AttrUndefined}, - Cursor: ColorAttr{colUndefined, AttrUndefined}, - Marker: ColorAttr{colUndefined, AttrUndefined}, - Header: ColorAttr{colUndefined, AttrUndefined}, - Footer: ColorAttr{colUndefined, AttrUndefined}, - Border: ColorAttr{colUndefined, AttrUndefined}, - BorderLabel: ColorAttr{colUndefined, AttrUndefined}, - ListLabel: ColorAttr{colUndefined, AttrUndefined}, - ListBorder: ColorAttr{colUndefined, AttrUndefined}, - Ghost: ColorAttr{colUndefined, Dim}, - Disabled: ColorAttr{colUndefined, AttrUndefined}, - PreviewFg: ColorAttr{colUndefined, AttrUndefined}, - PreviewBg: ColorAttr{colUndefined, AttrUndefined}, - Gutter: ColorAttr{colUndefined, AttrUndefined}, - PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, - PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, - PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, - Separator: ColorAttr{colUndefined, AttrUndefined}, - Scrollbar: ColorAttr{colUndefined, AttrUndefined}, - InputBg: ColorAttr{colUndefined, AttrUndefined}, - InputBorder: ColorAttr{colUndefined, AttrUndefined}, - InputLabel: ColorAttr{colUndefined, AttrUndefined}, - HeaderBg: ColorAttr{colUndefined, AttrUndefined}, - HeaderBorder: ColorAttr{colUndefined, AttrUndefined}, - HeaderLabel: ColorAttr{colUndefined, AttrUndefined}, - FooterBg: ColorAttr{colUndefined, AttrUndefined}, - FooterBorder: ColorAttr{colUndefined, AttrUndefined}, - FooterLabel: ColorAttr{colUndefined, AttrUndefined}, - GapLine: ColorAttr{colUndefined, AttrUndefined}, - Nth: ColorAttr{colUndefined, AttrUndefined}, - Hidden: ColorAttr{colUndefined, Dim}, - } -} - -func NoColorTheme() *ColorTheme { - return &ColorTheme{ - Colored: false, - Input: ColorAttr{colDefault, AttrUndefined}, - Fg: ColorAttr{colDefault, AttrUndefined}, - Bg: ColorAttr{colDefault, AttrUndefined}, - ListFg: ColorAttr{colDefault, AttrUndefined}, - ListBg: ColorAttr{colDefault, AttrUndefined}, - AltBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedFg: ColorAttr{colDefault, AttrUndefined}, - SelectedBg: ColorAttr{colDefault, AttrUndefined}, - SelectedMatch: ColorAttr{colDefault, AttrUndefined}, - DarkBg: ColorAttr{colDefault, AttrUndefined}, - Prompt: ColorAttr{colDefault, AttrUndefined}, - Match: ColorAttr{colDefault, Underline}, - Current: ColorAttr{colDefault, Reverse}, - CurrentMatch: ColorAttr{colDefault, Reverse | Underline}, - Spinner: ColorAttr{colDefault, AttrUndefined}, - Info: ColorAttr{colDefault, AttrUndefined}, - Cursor: ColorAttr{colDefault, AttrUndefined}, - Marker: ColorAttr{colDefault, AttrUndefined}, - Header: ColorAttr{colDefault, AttrUndefined}, - Border: ColorAttr{colDefault, AttrUndefined}, - BorderLabel: ColorAttr{colDefault, AttrUndefined}, - Ghost: ColorAttr{colDefault, Dim}, - Disabled: ColorAttr{colDefault, AttrUndefined}, - PreviewFg: ColorAttr{colDefault, AttrUndefined}, - PreviewBg: ColorAttr{colDefault, AttrUndefined}, - Gutter: ColorAttr{colDefault, AttrUndefined}, - PreviewBorder: ColorAttr{colDefault, AttrUndefined}, - PreviewScrollbar: ColorAttr{colDefault, AttrUndefined}, - PreviewLabel: ColorAttr{colDefault, AttrUndefined}, - ListLabel: ColorAttr{colDefault, AttrUndefined}, - ListBorder: ColorAttr{colDefault, AttrUndefined}, - Separator: ColorAttr{colDefault, AttrUndefined}, - Scrollbar: ColorAttr{colDefault, AttrUndefined}, - InputBg: ColorAttr{colDefault, AttrUndefined}, - InputBorder: ColorAttr{colDefault, AttrUndefined}, - InputLabel: ColorAttr{colDefault, AttrUndefined}, - HeaderBg: ColorAttr{colDefault, AttrUndefined}, - HeaderBorder: ColorAttr{colDefault, AttrUndefined}, - HeaderLabel: ColorAttr{colDefault, AttrUndefined}, - FooterBg: ColorAttr{colDefault, AttrUndefined}, - FooterBorder: ColorAttr{colDefault, AttrUndefined}, - FooterLabel: ColorAttr{colDefault, AttrUndefined}, - GapLine: ColorAttr{colDefault, AttrUndefined}, - Nth: ColorAttr{colUndefined, AttrUndefined}, - Hidden: ColorAttr{colUndefined, Dim}, - } -} - func init() { + defaultColor := ColorAttr{colDefault, AttrUndefined} + undefined := ColorAttr{colUndefined, AttrUndefined} + + NoColorTheme = &ColorTheme{ + Colored: false, + Input: defaultColor, + Fg: defaultColor, + Bg: defaultColor, + ListFg: defaultColor, + ListBg: defaultColor, + AltBg: undefined, + SelectedFg: defaultColor, + SelectedBg: defaultColor, + SelectedMatch: defaultColor, + DarkBg: defaultColor, + Prompt: defaultColor, + Match: defaultColor, + Current: undefined, + CurrentMatch: undefined, + Spinner: defaultColor, + Info: defaultColor, + Cursor: defaultColor, + Marker: defaultColor, + Header: defaultColor, + Border: undefined, + BorderLabel: defaultColor, + Ghost: undefined, + Disabled: defaultColor, + PreviewFg: defaultColor, + PreviewBg: defaultColor, + Gutter: undefined, + PreviewBorder: defaultColor, + PreviewScrollbar: defaultColor, + PreviewLabel: defaultColor, + ListLabel: defaultColor, + ListBorder: defaultColor, + Separator: defaultColor, + Scrollbar: defaultColor, + InputBg: defaultColor, + InputBorder: defaultColor, + InputLabel: defaultColor, + HeaderBg: defaultColor, + HeaderBorder: defaultColor, + HeaderLabel: defaultColor, + FooterBg: defaultColor, + FooterBorder: defaultColor, + FooterLabel: defaultColor, + GapLine: defaultColor, + Nth: undefined, + Hidden: undefined, + } + + EmptyTheme = &ColorTheme{ + Colored: true, + Input: undefined, + Fg: undefined, + Bg: undefined, + ListFg: undefined, + ListBg: undefined, + AltBg: undefined, + SelectedFg: undefined, + SelectedBg: undefined, + SelectedMatch: undefined, + DarkBg: undefined, + Prompt: undefined, + Match: undefined, + Current: undefined, + CurrentMatch: undefined, + Spinner: undefined, + Info: undefined, + Cursor: undefined, + Marker: undefined, + Header: undefined, + Footer: undefined, + Border: undefined, + BorderLabel: undefined, + ListLabel: undefined, + ListBorder: undefined, + Ghost: undefined, + Disabled: undefined, + PreviewFg: undefined, + PreviewBg: undefined, + Gutter: undefined, + PreviewBorder: undefined, + PreviewScrollbar: undefined, + PreviewLabel: undefined, + Separator: undefined, + Scrollbar: undefined, + InputBg: undefined, + InputBorder: undefined, + InputLabel: undefined, + HeaderBg: undefined, + HeaderBorder: undefined, + HeaderLabel: undefined, + FooterBg: undefined, + FooterBorder: undefined, + FooterLabel: undefined, + GapLine: undefined, + Nth: undefined, + Hidden: undefined, + } + Default16 = &ColorTheme{ Colored: true, - Input: ColorAttr{colDefault, AttrUndefined}, - Fg: ColorAttr{colDefault, AttrUndefined}, - Bg: ColorAttr{colDefault, AttrUndefined}, - ListFg: ColorAttr{colUndefined, AttrUndefined}, - ListBg: ColorAttr{colUndefined, AttrUndefined}, - AltBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedFg: ColorAttr{colUndefined, AttrUndefined}, - SelectedBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, + Input: defaultColor, + Fg: defaultColor, + Bg: defaultColor, + ListFg: undefined, + ListBg: undefined, + AltBg: undefined, + SelectedFg: undefined, + SelectedBg: undefined, + SelectedMatch: undefined, DarkBg: ColorAttr{colGrey, AttrUndefined}, Prompt: ColorAttr{colBlue, AttrUndefined}, Match: ColorAttr{colGreen, AttrUndefined}, @@ -951,44 +978,45 @@ func init() { Marker: ColorAttr{colMagenta, AttrUndefined}, Header: ColorAttr{colCyan, AttrUndefined}, Footer: ColorAttr{colCyan, AttrUndefined}, - Border: ColorAttr{colDefault, Dim}, - BorderLabel: ColorAttr{colDefault, AttrUndefined}, - Ghost: ColorAttr{colUndefined, Dim}, - Disabled: ColorAttr{colUndefined, AttrUndefined}, - PreviewFg: ColorAttr{colUndefined, AttrUndefined}, - PreviewBg: ColorAttr{colUndefined, AttrUndefined}, - Gutter: ColorAttr{colUndefined, AttrUndefined}, - PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, - PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, - PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, - ListLabel: ColorAttr{colUndefined, AttrUndefined}, - ListBorder: ColorAttr{colUndefined, AttrUndefined}, - Separator: ColorAttr{colUndefined, AttrUndefined}, - Scrollbar: ColorAttr{colUndefined, AttrUndefined}, - InputBg: ColorAttr{colUndefined, AttrUndefined}, - InputBorder: ColorAttr{colUndefined, AttrUndefined}, - InputLabel: ColorAttr{colUndefined, AttrUndefined}, - HeaderBg: ColorAttr{colUndefined, AttrUndefined}, - HeaderBorder: ColorAttr{colUndefined, AttrUndefined}, - HeaderLabel: ColorAttr{colUndefined, AttrUndefined}, - FooterBg: ColorAttr{colUndefined, AttrUndefined}, - FooterBorder: ColorAttr{colUndefined, AttrUndefined}, - FooterLabel: ColorAttr{colUndefined, AttrUndefined}, - GapLine: ColorAttr{colUndefined, AttrUndefined}, - Nth: ColorAttr{colUndefined, AttrUndefined}, - Hidden: ColorAttr{colUndefined, Dim}, + Border: undefined, + BorderLabel: defaultColor, + Ghost: undefined, + Disabled: undefined, + PreviewFg: undefined, + PreviewBg: undefined, + Gutter: undefined, + PreviewBorder: undefined, + PreviewScrollbar: undefined, + PreviewLabel: undefined, + ListLabel: undefined, + ListBorder: undefined, + Separator: undefined, + Scrollbar: undefined, + InputBg: undefined, + InputBorder: undefined, + InputLabel: undefined, + HeaderBg: undefined, + HeaderBorder: undefined, + HeaderLabel: undefined, + FooterBg: undefined, + FooterBorder: undefined, + FooterLabel: undefined, + GapLine: undefined, + Nth: undefined, + Hidden: undefined, } + Dark256 = &ColorTheme{ Colored: true, - Input: ColorAttr{colDefault, AttrUndefined}, - Fg: ColorAttr{colDefault, AttrUndefined}, - Bg: ColorAttr{colDefault, AttrUndefined}, - ListFg: ColorAttr{colUndefined, AttrUndefined}, - ListBg: ColorAttr{colUndefined, AttrUndefined}, - AltBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedFg: ColorAttr{colUndefined, AttrUndefined}, - SelectedBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, + Input: defaultColor, + Fg: defaultColor, + Bg: defaultColor, + ListFg: undefined, + ListBg: undefined, + AltBg: undefined, + SelectedFg: undefined, + SelectedBg: undefined, + SelectedMatch: undefined, DarkBg: ColorAttr{236, AttrUndefined}, Prompt: ColorAttr{110, AttrUndefined}, Match: ColorAttr{108, AttrUndefined}, @@ -1002,42 +1030,43 @@ func init() { Footer: ColorAttr{109, AttrUndefined}, Border: ColorAttr{59, AttrUndefined}, BorderLabel: ColorAttr{145, AttrUndefined}, - Ghost: ColorAttr{colUndefined, Dim}, - Disabled: ColorAttr{colUndefined, AttrUndefined}, - PreviewFg: ColorAttr{colUndefined, AttrUndefined}, - PreviewBg: ColorAttr{colUndefined, AttrUndefined}, - Gutter: ColorAttr{colUndefined, AttrUndefined}, - PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, - PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, - PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, - ListLabel: ColorAttr{colUndefined, AttrUndefined}, - ListBorder: ColorAttr{colUndefined, AttrUndefined}, - Separator: ColorAttr{colUndefined, AttrUndefined}, - Scrollbar: ColorAttr{colUndefined, AttrUndefined}, - InputBg: ColorAttr{colUndefined, AttrUndefined}, - InputBorder: ColorAttr{colUndefined, AttrUndefined}, - InputLabel: ColorAttr{colUndefined, AttrUndefined}, - HeaderBg: ColorAttr{colUndefined, AttrUndefined}, - HeaderBorder: ColorAttr{colUndefined, AttrUndefined}, - HeaderLabel: ColorAttr{colUndefined, AttrUndefined}, - FooterBg: ColorAttr{colUndefined, AttrUndefined}, - FooterBorder: ColorAttr{colUndefined, AttrUndefined}, - FooterLabel: ColorAttr{colUndefined, AttrUndefined}, - GapLine: ColorAttr{colUndefined, AttrUndefined}, - Nth: ColorAttr{colUndefined, AttrUndefined}, - Hidden: ColorAttr{colUndefined, Dim}, + Ghost: undefined, + Disabled: undefined, + PreviewFg: undefined, + PreviewBg: undefined, + Gutter: undefined, + PreviewBorder: undefined, + PreviewScrollbar: undefined, + PreviewLabel: undefined, + ListLabel: undefined, + ListBorder: undefined, + Separator: undefined, + Scrollbar: undefined, + InputBg: undefined, + InputBorder: undefined, + InputLabel: undefined, + HeaderBg: undefined, + HeaderBorder: undefined, + HeaderLabel: undefined, + FooterBg: undefined, + FooterBorder: undefined, + FooterLabel: undefined, + GapLine: undefined, + Nth: undefined, + Hidden: undefined, } + Light256 = &ColorTheme{ Colored: true, - Input: ColorAttr{colDefault, AttrUndefined}, - Fg: ColorAttr{colDefault, AttrUndefined}, - Bg: ColorAttr{colDefault, AttrUndefined}, - ListFg: ColorAttr{colUndefined, AttrUndefined}, - ListBg: ColorAttr{colUndefined, AttrUndefined}, - AltBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedFg: ColorAttr{colUndefined, AttrUndefined}, - SelectedBg: ColorAttr{colUndefined, AttrUndefined}, - SelectedMatch: ColorAttr{colUndefined, AttrUndefined}, + Input: defaultColor, + Fg: defaultColor, + Bg: defaultColor, + ListFg: undefined, + ListBg: undefined, + AltBg: undefined, + SelectedFg: undefined, + SelectedBg: undefined, + SelectedMatch: undefined, DarkBg: ColorAttr{251, AttrUndefined}, Prompt: ColorAttr{25, AttrUndefined}, Match: ColorAttr{66, AttrUndefined}, @@ -1051,34 +1080,34 @@ func init() { Footer: ColorAttr{31, AttrUndefined}, Border: ColorAttr{145, AttrUndefined}, BorderLabel: ColorAttr{59, AttrUndefined}, - Ghost: ColorAttr{colUndefined, Dim}, - Disabled: ColorAttr{colUndefined, AttrUndefined}, - PreviewFg: ColorAttr{colUndefined, AttrUndefined}, - PreviewBg: ColorAttr{colUndefined, AttrUndefined}, - Gutter: ColorAttr{colUndefined, AttrUndefined}, - PreviewBorder: ColorAttr{colUndefined, AttrUndefined}, - PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined}, - PreviewLabel: ColorAttr{colUndefined, AttrUndefined}, - ListLabel: ColorAttr{colUndefined, AttrUndefined}, - ListBorder: ColorAttr{colUndefined, AttrUndefined}, - Separator: ColorAttr{colUndefined, AttrUndefined}, - Scrollbar: ColorAttr{colUndefined, AttrUndefined}, - InputBg: ColorAttr{colUndefined, AttrUndefined}, - InputBorder: ColorAttr{colUndefined, AttrUndefined}, - InputLabel: ColorAttr{colUndefined, AttrUndefined}, - HeaderBg: ColorAttr{colUndefined, AttrUndefined}, - HeaderBorder: ColorAttr{colUndefined, AttrUndefined}, - HeaderLabel: ColorAttr{colUndefined, AttrUndefined}, - FooterBg: ColorAttr{colUndefined, AttrUndefined}, - FooterBorder: ColorAttr{colUndefined, AttrUndefined}, - FooterLabel: ColorAttr{colUndefined, AttrUndefined}, - GapLine: ColorAttr{colUndefined, AttrUndefined}, - Nth: ColorAttr{colUndefined, AttrUndefined}, - Hidden: ColorAttr{colUndefined, Dim}, + Ghost: undefined, + Disabled: undefined, + PreviewFg: undefined, + PreviewBg: undefined, + Gutter: undefined, + PreviewBorder: undefined, + PreviewScrollbar: undefined, + PreviewLabel: undefined, + ListLabel: undefined, + ListBorder: undefined, + Separator: undefined, + Scrollbar: undefined, + InputBg: undefined, + InputBorder: undefined, + InputLabel: undefined, + HeaderBg: undefined, + HeaderBorder: undefined, + HeaderLabel: undefined, + FooterBg: undefined, + FooterBorder: undefined, + FooterLabel: undefined, + GapLine: undefined, + Nth: undefined, + Hidden: undefined, } } -func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInputWindow bool, hasHeaderWindow bool) { +func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, boldify bool, forceBlack bool, hasInputWindow bool, hasHeaderWindow bool) { if forceBlack { theme.Bg = ColorAttr{colBlack, AttrUndefined} } @@ -1098,18 +1127,36 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp theme.Bg = o(baseTheme.Bg, theme.Bg) theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg) theme.Prompt = o(baseTheme.Prompt, theme.Prompt) - theme.Match = o(baseTheme.Match, theme.Match) + match := theme.Match + if !baseTheme.Colored && match.IsUndefined() { + match.Attr = Underline + } + theme.Match = o(baseTheme.Match, match) // Inherit from 'fg', so that we don't have to write 'current-fg:dim' // e.g. fzf --delimiter / --nth -1 --color fg:dim,nth:regular - theme.Current = theme.Fg.Merge(o(baseTheme.Current, theme.Current)) - theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch) + current := theme.Current + if !baseTheme.Colored && current.IsUndefined() { + current.Attr = Reverse + } + theme.Current = theme.Fg.Merge(o(baseTheme.Current, current)) + currentMatch := theme.CurrentMatch + if !baseTheme.Colored && currentMatch.IsUndefined() { + currentMatch.Attr = Reverse | Underline + } + theme.CurrentMatch = o(baseTheme.CurrentMatch, currentMatch) theme.Spinner = o(baseTheme.Spinner, theme.Spinner) theme.Info = o(baseTheme.Info, theme.Info) theme.Cursor = o(baseTheme.Cursor, theme.Cursor) theme.Marker = o(baseTheme.Marker, theme.Marker) theme.Header = o(baseTheme.Header, theme.Header) theme.Footer = o(baseTheme.Footer, theme.Footer) - theme.Border = o(baseTheme.Border, theme.Border) + + // If border color is undefined, set it to default color with dim attribute. + border := theme.Border + if baseTheme.Border.IsUndefined() && border.IsUndefined() { + border.Attr = Dim + } + theme.Border = o(baseTheme.Border, border) theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel) undefined := NewColorAttr() @@ -1122,9 +1169,23 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp theme.SelectedFg = o(theme.ListFg, theme.SelectedFg) theme.SelectedBg = o(theme.ListBg, theme.SelectedBg) theme.SelectedMatch = o(theme.Match, theme.SelectedMatch) - theme.Ghost = o(theme.Input, theme.Ghost) + + ghost := theme.Ghost + if ghost.IsUndefined() { + ghost.Attr = Dim + } else if ghost.IsColorDefined() && !ghost.IsAttrDefined() { + // Don't want to inherit 'bold' from 'input' + ghost.Attr = AttrRegular + } + theme.Ghost = o(theme.Input, ghost) theme.Disabled = o(theme.Input, theme.Disabled) - theme.Gutter = o(theme.DarkBg, theme.Gutter) + + // Use dim gutter on non-colored themes if undefined + gutter := theme.Gutter + if !baseTheme.Colored && gutter.IsUndefined() { + gutter.Attr = Dim + } + theme.Gutter = o(theme.DarkBg, gutter) theme.PreviewFg = o(theme.Fg, theme.PreviewFg) theme.PreviewBg = o(theme.Bg, theme.PreviewBg) theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel) @@ -1166,6 +1227,26 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInp theme.FooterBorder = o(theme.Border, theme.FooterBorder) theme.FooterLabel = o(theme.BorderLabel, theme.FooterLabel) + if boldify { + boldify := func(c ColorAttr) ColorAttr { + dup := c + if (c.Attr & AttrRegular) == 0 { + dup.Attr |= BoldForce + } + return dup + } + theme.Current = boldify(theme.Current) + theme.CurrentMatch = boldify(theme.CurrentMatch) + theme.Prompt = boldify(theme.Prompt) + theme.Input = boldify(theme.Input) + theme.Cursor = boldify(theme.Cursor) + theme.Spinner = boldify(theme.Spinner) + } + + if theme.Hidden.IsUndefined() { + theme.Hidden.Attr = Dim + } + initPalette(theme) }