From f66d94c6b06c58141c40935bfb18a45858cf79da Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 18 Apr 2015 02:52:30 +0900 Subject: [PATCH] Add `--color=[dark|light|16|bw]` option - dark: the current default for 256-color terminal - light: color scheme for 256-color terminal with light background - 16: the default color scheme for 16-color terminal (`+2`) - bw: no colors (`+c`) --- CHANGELOG.md | 4 ++ fzf | 4 +- man/man1/fzf.1 | 20 ++++++--- src/core.go | 2 +- src/curses/curses.go | 98 ++++++++++++++++++++++++++++++-------------- src/options.go | 41 ++++++++++++++---- src/terminal.go | 2 +- 7 files changed, 124 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d87876d..8f15f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ CHANGELOG - Performance optimization - Less aggressive memoization to limit memory usage +### New features + +- Added color scheme for light background: `--color=light` + 0.9.9 ----- diff --git a/fzf b/fzf index 1bc5673..f1838b8 100755 --- a/fzf +++ b/fzf @@ -206,10 +206,10 @@ class FZF @expect = true when /^--expect=(.*)$/ @expect = true - when '--toggle-sort', '--tiebreak' + when '--toggle-sort', '--tiebreak', '--color' argv.shift when '--tac', '--no-tac', '--sync', '--no-sync', '--hscroll', '--no-hscroll', - /^--toggle-sort=(.*)$/, /^--tiebreak=(.*)$/ + /^--color=(.*)$/, /^--toggle-sort=(.*)$/, /^--tiebreak=(.*)$/ # XXX else usage 1, "illegal option: #{o}" diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index cff8160..4626eba 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -91,11 +91,21 @@ Enable processing of ANSI color codes .B "--no-mouse" Disable mouse .TP -.B "+c, --no-color" -Disable colors -.TP -.B "+2, --no-256" -Disable 256-color +.B "--color=COL" +Color scheme: [dark|light|16|bw] +.br +(default: dark on 256-color terminal, otherwise 16) +.br +.R "" +.br +.BR dark " Dark color scheme for 256-color terminal" +.br +.BR light " Light color scheme for 256-color terminal" +.br +.BR 16 " Default color scheme for 16-color terminal" +.br +.BR bw " No colors" +.br .TP .B "--black" Use black background diff --git a/src/core.go b/src/core.go index 7230cff..d7a6a0b 100644 --- a/src/core.go +++ b/src/core.go @@ -68,7 +68,7 @@ func Run(options *Options) { return data, nil } if opts.Ansi { - if opts.Color { + if opts.Theme != nil { ansiProcessor = func(data *string) (*string, []ansiOffset) { return extractColor(data) } diff --git a/src/curses/curses.go b/src/curses/curses.go index 3340a5c..f326361 100644 --- a/src/curses/curses.go +++ b/src/curses/curses.go @@ -95,6 +95,18 @@ const ( doubleClickDuration = 500 * time.Millisecond ) +type ColorTheme struct { + darkBg C.short + prompt C.short + match C.short + current C.short + currentMatch C.short + spinner C.short + info C.short + cursor C.short + selected C.short +} + type Event struct { Type int Char rune @@ -117,6 +129,9 @@ var ( _colorMap map[int]int _prevDownTime time.Time _clickY []int + Default16 *ColorTheme + Dark256 *ColorTheme + Light256 *ColorTheme DarkBG C.short ) @@ -124,6 +139,36 @@ func init() { _prevDownTime = time.Unix(0, 0) _clickY = []int{} _colorMap = make(map[int]int) + Default16 = &ColorTheme{ + darkBg: C.COLOR_BLACK, + prompt: C.COLOR_BLUE, + match: C.COLOR_GREEN, + current: C.COLOR_YELLOW, + currentMatch: C.COLOR_GREEN, + spinner: C.COLOR_GREEN, + info: C.COLOR_WHITE, + cursor: C.COLOR_RED, + selected: C.COLOR_MAGENTA} + Dark256 = &ColorTheme{ + darkBg: 236, + prompt: 110, + match: 108, + current: 254, + currentMatch: 151, + spinner: 148, + info: 144, + cursor: 161, + selected: 168} + Light256 = &ColorTheme{ + darkBg: 251, + prompt: 25, + match: 66, + current: 237, + currentMatch: 23, + spinner: 65, + info: 101, + cursor: 161, + selected: 168} } func attrColored(pair int, bold bool) C.int { @@ -173,7 +218,7 @@ func getch(nonblock bool) int { return int(b[0]) } -func Init(color bool, color256 bool, black bool, mouse bool) { +func Init(theme *ColorTheme, black bool, mouse bool) { { in, err := os.OpenFile("/dev/tty", syscall.O_RDONLY, 0) if err != nil { @@ -203,42 +248,35 @@ func Init(color bool, color256 bool, black bool, mouse bool) { os.Exit(1) }() - if color { + if theme != nil { C.start_color() - var bg C.short - if black { - bg = C.COLOR_BLACK - } else { - C.use_default_colors() - bg = -1 - } - if color256 { - DarkBG = 236 - C.init_pair(ColPrompt, 110, bg) - C.init_pair(ColMatch, 108, bg) - C.init_pair(ColCurrent, 254, DarkBG) - C.init_pair(ColCurrentMatch, 151, DarkBG) - C.init_pair(ColSpinner, 148, bg) - C.init_pair(ColInfo, 144, bg) - C.init_pair(ColCursor, 161, DarkBG) - C.init_pair(ColSelected, 168, DarkBG) - } else { - DarkBG = C.COLOR_BLACK - C.init_pair(ColPrompt, C.COLOR_BLUE, bg) - C.init_pair(ColMatch, C.COLOR_GREEN, bg) - C.init_pair(ColCurrent, C.COLOR_YELLOW, DarkBG) - C.init_pair(ColCurrentMatch, C.COLOR_GREEN, DarkBG) - C.init_pair(ColSpinner, C.COLOR_GREEN, bg) - C.init_pair(ColInfo, C.COLOR_WHITE, bg) - C.init_pair(ColCursor, C.COLOR_RED, DarkBG) - C.init_pair(ColSelected, C.COLOR_MAGENTA, DarkBG) - } + initPairs(theme, black) _color = attrColored } else { _color = attrMono } } +func initPairs(theme *ColorTheme, black bool) { + var bg C.short + if black { + bg = C.COLOR_BLACK + } else { + C.use_default_colors() + bg = -1 + } + + DarkBG = theme.darkBg + C.init_pair(ColPrompt, theme.prompt, bg) + C.init_pair(ColMatch, theme.match, bg) + C.init_pair(ColCurrent, theme.current, DarkBG) + C.init_pair(ColCurrentMatch, theme.currentMatch, DarkBG) + C.init_pair(ColSpinner, theme.spinner, bg) + C.init_pair(ColInfo, theme.info, bg) + C.init_pair(ColCursor, theme.cursor, DarkBG) + C.init_pair(ColSelected, theme.selected, DarkBG) +} + func Close() { C.endwin() C.swapOutput() diff --git a/src/options.go b/src/options.go index d13a53b..173dee9 100644 --- a/src/options.go +++ b/src/options.go @@ -35,8 +35,8 @@ const usage = `usage: fzf [options] -m, --multi Enable multi-select with tab/shift-tab --ansi Enable processing of ANSI color codes --no-mouse Disable mouse - +c, --no-color Disable colors - +2, --no-256 Disable 256-color + --color=COL Color scheme [dark|light|16|bw] + (default: dark on 256-color terminal, otherwise 16) --black Use black background --reverse Reverse orientation --no-hscroll Disable horizontal scroll @@ -101,8 +101,7 @@ type Options struct { Multi bool Ansi bool Mouse bool - Color bool - Color256 bool + Theme *curses.ColorTheme Black bool Reverse bool Hscroll bool @@ -119,6 +118,13 @@ type Options struct { } func defaultOptions() *Options { + var defaultTheme *curses.ColorTheme + if strings.Contains(os.Getenv("TERM"), "256") { + defaultTheme = curses.Dark256 + } else { + defaultTheme = curses.Default16 + } + return &Options{ Mode: ModeFuzzy, Case: CaseSmart, @@ -131,8 +137,7 @@ func defaultOptions() *Options { Multi: false, Ansi: false, Mouse: true, - Color: true, - Color256: strings.Contains(os.Getenv("TERM"), "256"), + Theme: defaultTheme, Black: false, Reverse: false, Hscroll: true, @@ -266,6 +271,22 @@ func parseTiebreak(str string) tiebreak { return byLength } +func parseTheme(str string) *curses.ColorTheme { + switch strings.ToLower(str) { + case "dark": + return curses.Dark256 + case "light": + return curses.Light256 + case "16": + return curses.Default16 + case "bw", "off", "no", "none": + return nil + default: + errorExit("invalid color scheme: " + str) + } + return nil +} + func checkToggleSort(str string) int { keys := parseKeyChords(str, "key name required") if len(keys) != 1 { @@ -295,6 +316,8 @@ func parseOptions(opts *Options, allArgs []string) { opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required") case "--tiebreak": opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required")) + case "--color": + opts.Theme = parseTheme(nextString(allArgs, &i, "color scheme name required")) case "--toggle-sort": opts.ToggleSort = checkToggleSort(nextString(allArgs, &i, "key name required")) case "-d", "--delimiter": @@ -326,9 +349,9 @@ func parseOptions(opts *Options, allArgs []string) { case "--no-mouse": opts.Mouse = false case "+c", "--no-color": - opts.Color = false + opts.Theme = nil case "+2", "--no-256": - opts.Color256 = false + opts.Theme = curses.Default16 case "--black": opts.Black = true case "--no-black": @@ -384,6 +407,8 @@ func parseOptions(opts *Options, allArgs []string) { opts.Expect = parseKeyChords(value, "key names required") } else if match, value := optString(arg, "--tiebreak="); match { opts.Tiebreak = parseTiebreak(value) + } else if match, value := optString(arg, "--color="); match { + opts.Theme = parseTheme(value) } else { errorExit("unknown option: " + arg) } diff --git a/src/terminal.go b/src/terminal.go index eaec9a3..959c84d 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -105,7 +105,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { suppress: true, startChan: make(chan bool, 1), initFunc: func() { - C.Init(opts.Color, opts.Color256, opts.Black, opts.Mouse) + C.Init(opts.Theme, opts.Black, opts.Mouse) }} }