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`)
This commit is contained in:
Junegunn Choi 2015-04-18 02:52:30 +09:00
parent 2fe1e28220
commit f66d94c6b0
7 changed files with 124 additions and 47 deletions

View File

@ -9,6 +9,10 @@ CHANGELOG
- Performance optimization - Performance optimization
- Less aggressive memoization to limit memory usage - Less aggressive memoization to limit memory usage
### New features
- Added color scheme for light background: `--color=light`
0.9.9 0.9.9
----- -----

4
fzf
View File

@ -206,10 +206,10 @@ class FZF
@expect = true @expect = true
when /^--expect=(.*)$/ when /^--expect=(.*)$/
@expect = true @expect = true
when '--toggle-sort', '--tiebreak' when '--toggle-sort', '--tiebreak', '--color'
argv.shift argv.shift
when '--tac', '--no-tac', '--sync', '--no-sync', '--hscroll', '--no-hscroll', when '--tac', '--no-tac', '--sync', '--no-sync', '--hscroll', '--no-hscroll',
/^--toggle-sort=(.*)$/, /^--tiebreak=(.*)$/ /^--color=(.*)$/, /^--toggle-sort=(.*)$/, /^--tiebreak=(.*)$/
# XXX # XXX
else else
usage 1, "illegal option: #{o}" usage 1, "illegal option: #{o}"

View File

@ -91,11 +91,21 @@ Enable processing of ANSI color codes
.B "--no-mouse" .B "--no-mouse"
Disable mouse Disable mouse
.TP .TP
.B "+c, --no-color" .B "--color=COL"
Disable colors Color scheme: [dark|light|16|bw]
.TP .br
.B "+2, --no-256" (default: dark on 256-color terminal, otherwise 16)
Disable 256-color .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 .TP
.B "--black" .B "--black"
Use black background Use black background

View File

@ -68,7 +68,7 @@ func Run(options *Options) {
return data, nil return data, nil
} }
if opts.Ansi { if opts.Ansi {
if opts.Color { if opts.Theme != nil {
ansiProcessor = func(data *string) (*string, []ansiOffset) { ansiProcessor = func(data *string) (*string, []ansiOffset) {
return extractColor(data) return extractColor(data)
} }

View File

@ -95,6 +95,18 @@ const (
doubleClickDuration = 500 * time.Millisecond 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 Event struct {
Type int Type int
Char rune Char rune
@ -117,6 +129,9 @@ var (
_colorMap map[int]int _colorMap map[int]int
_prevDownTime time.Time _prevDownTime time.Time
_clickY []int _clickY []int
Default16 *ColorTheme
Dark256 *ColorTheme
Light256 *ColorTheme
DarkBG C.short DarkBG C.short
) )
@ -124,6 +139,36 @@ func init() {
_prevDownTime = time.Unix(0, 0) _prevDownTime = time.Unix(0, 0)
_clickY = []int{} _clickY = []int{}
_colorMap = make(map[int]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 { func attrColored(pair int, bold bool) C.int {
@ -173,7 +218,7 @@ func getch(nonblock bool) int {
return int(b[0]) 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) in, err := os.OpenFile("/dev/tty", syscall.O_RDONLY, 0)
if err != nil { if err != nil {
@ -203,42 +248,35 @@ func Init(color bool, color256 bool, black bool, mouse bool) {
os.Exit(1) os.Exit(1)
}() }()
if color { if theme != nil {
C.start_color() C.start_color()
var bg C.short initPairs(theme, black)
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)
}
_color = attrColored _color = attrColored
} else { } else {
_color = attrMono _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() { func Close() {
C.endwin() C.endwin()
C.swapOutput() C.swapOutput()

View File

@ -35,8 +35,8 @@ const usage = `usage: fzf [options]
-m, --multi Enable multi-select with tab/shift-tab -m, --multi Enable multi-select with tab/shift-tab
--ansi Enable processing of ANSI color codes --ansi Enable processing of ANSI color codes
--no-mouse Disable mouse --no-mouse Disable mouse
+c, --no-color Disable colors --color=COL Color scheme [dark|light|16|bw]
+2, --no-256 Disable 256-color (default: dark on 256-color terminal, otherwise 16)
--black Use black background --black Use black background
--reverse Reverse orientation --reverse Reverse orientation
--no-hscroll Disable horizontal scroll --no-hscroll Disable horizontal scroll
@ -101,8 +101,7 @@ type Options struct {
Multi bool Multi bool
Ansi bool Ansi bool
Mouse bool Mouse bool
Color bool Theme *curses.ColorTheme
Color256 bool
Black bool Black bool
Reverse bool Reverse bool
Hscroll bool Hscroll bool
@ -119,6 +118,13 @@ type Options struct {
} }
func defaultOptions() *Options { func defaultOptions() *Options {
var defaultTheme *curses.ColorTheme
if strings.Contains(os.Getenv("TERM"), "256") {
defaultTheme = curses.Dark256
} else {
defaultTheme = curses.Default16
}
return &Options{ return &Options{
Mode: ModeFuzzy, Mode: ModeFuzzy,
Case: CaseSmart, Case: CaseSmart,
@ -131,8 +137,7 @@ func defaultOptions() *Options {
Multi: false, Multi: false,
Ansi: false, Ansi: false,
Mouse: true, Mouse: true,
Color: true, Theme: defaultTheme,
Color256: strings.Contains(os.Getenv("TERM"), "256"),
Black: false, Black: false,
Reverse: false, Reverse: false,
Hscroll: true, Hscroll: true,
@ -266,6 +271,22 @@ func parseTiebreak(str string) tiebreak {
return byLength 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 { func checkToggleSort(str string) int {
keys := parseKeyChords(str, "key name required") keys := parseKeyChords(str, "key name required")
if len(keys) != 1 { 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") opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required")
case "--tiebreak": case "--tiebreak":
opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required")) opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
case "--color":
opts.Theme = parseTheme(nextString(allArgs, &i, "color scheme name required"))
case "--toggle-sort": case "--toggle-sort":
opts.ToggleSort = checkToggleSort(nextString(allArgs, &i, "key name required")) opts.ToggleSort = checkToggleSort(nextString(allArgs, &i, "key name required"))
case "-d", "--delimiter": case "-d", "--delimiter":
@ -326,9 +349,9 @@ func parseOptions(opts *Options, allArgs []string) {
case "--no-mouse": case "--no-mouse":
opts.Mouse = false opts.Mouse = false
case "+c", "--no-color": case "+c", "--no-color":
opts.Color = false opts.Theme = nil
case "+2", "--no-256": case "+2", "--no-256":
opts.Color256 = false opts.Theme = curses.Default16
case "--black": case "--black":
opts.Black = true opts.Black = true
case "--no-black": case "--no-black":
@ -384,6 +407,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Expect = parseKeyChords(value, "key names required") opts.Expect = parseKeyChords(value, "key names required")
} else if match, value := optString(arg, "--tiebreak="); match { } else if match, value := optString(arg, "--tiebreak="); match {
opts.Tiebreak = parseTiebreak(value) opts.Tiebreak = parseTiebreak(value)
} else if match, value := optString(arg, "--color="); match {
opts.Theme = parseTheme(value)
} else { } else {
errorExit("unknown option: " + arg) errorExit("unknown option: " + arg)
} }

View File

@ -105,7 +105,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
suppress: true, suppress: true,
startChan: make(chan bool, 1), startChan: make(chan bool, 1),
initFunc: func() { initFunc: func() {
C.Init(opts.Color, opts.Color256, opts.Black, opts.Mouse) C.Init(opts.Theme, opts.Black, opts.Mouse)
}} }}
} }