diff options
author | Junegunn Choi <junegunn.c@gmail.com> | 2015-03-29 02:59:32 +0900 |
---|---|---|
committer | Junegunn Choi <junegunn.c@gmail.com> | 2015-03-29 02:59:32 +0900 |
commit | 2a167aa030b244060fc479d2b88fdb9b9171d026 (patch) | |
tree | 69b994e5b97ad9a07107569fcb1227de7c886cbb /src | |
parent | 9cfecf7f0bb52441c27b769427fdf05f45b3110d (diff) |
Implement --expect option to support simple key bindings (#163)
Diffstat (limited to 'src')
-rw-r--r-- | src/curses/curses.go | 25 | ||||
-rw-r--r-- | src/options.go | 33 | ||||
-rw-r--r-- | src/options_test.go | 25 | ||||
-rw-r--r-- | src/terminal.go | 24 | ||||
-rw-r--r-- | src/util/util.go | 4 |
5 files changed, 108 insertions, 3 deletions
diff --git a/src/curses/curses.go b/src/curses/curses.go index dfd7cf51..d6aafd71 100644 --- a/src/curses/curses.go +++ b/src/curses/curses.go @@ -61,10 +61,20 @@ const ( PgUp PgDn + F1 + F2 + F3 + F4 + + AltBS + AltA AltB - AltF + AltC AltD - AltBS + AltE + AltF + + AltZ = AltA + 'z' - 'a' ) // Pallete @@ -324,6 +334,14 @@ func escSequence(sz *int) Event { return Event{CtrlE, 0, nil} case 77: return mouseSequence(sz) + case 80: + return Event{F1, 0, nil} + case 81: + return Event{F2, 0, nil} + case 82: + return Event{F3, 0, nil} + case 83: + return Event{F4, 0, nil} case 49, 50, 51, 52, 53, 54: if len(_buf) < 4 { return Event{Invalid, 0, nil} @@ -369,6 +387,9 @@ func escSequence(sz *int) Event { } // _buf[2] } // _buf[2] } // _buf[1] + if _buf[1] >= 'a' && _buf[1] <= 'z' { + return Event{AltA + int(_buf[1]) - 'a', 0, nil} + } return Event{Invalid, 0, nil} } diff --git a/src/options.go b/src/options.go index 73c9e188..89b1c368 100644 --- a/src/options.go +++ b/src/options.go @@ -5,6 +5,9 @@ import ( "os" "regexp" "strings" + "unicode/utf8" + + "github.com/junegunn/fzf/src/curses" "github.com/junegunn/go-shellwords" ) @@ -43,6 +46,7 @@ const usage = `usage: fzf [options] -0, --exit-0 Exit immediately when there's no match -f, --filter=STR Filter mode. Do not start interactive finder. --print-query Print query as the first line + --expect=KEYS Comma-separated list of keys to complete fzf --sync Synchronous search for multi-staged filtering (e.g. 'fzf --multi | fzf --sync') @@ -93,6 +97,7 @@ type Options struct { Select1 bool Exit0 bool Filter *string + Expect []int PrintQuery bool Sync bool Version bool @@ -119,6 +124,7 @@ func defaultOptions() *Options { Select1: false, Exit0: false, Filter: nil, + Expect: []int{}, PrintQuery: false, Sync: false, Version: false} @@ -191,6 +197,29 @@ func delimiterRegexp(str string) *regexp.Regexp { return rx } +func isAlphabet(char uint8) bool { + return char >= 'a' && char <= 'z' +} + +func parseKeyChords(str string) []int { + var chords []int + for _, key := range strings.Split(str, ",") { + lkey := strings.ToLower(key) + if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) { + chords = append(chords, curses.CtrlA+int(lkey[5])-'a') + } else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) { + chords = append(chords, curses.AltA+int(lkey[4])-'a') + } else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '4' { + chords = append(chords, curses.F1+int(key[1])-'1') + } else if utf8.RuneCountInString(key) == 1 { + chords = append(chords, curses.AltZ+int([]rune(key)[0])) + } else { + errorExit("unsupported key: " + key) + } + } + return chords +} + func parseOptions(opts *Options, allArgs []string) { for i := 0; i < len(allArgs); i++ { arg := allArgs[i] @@ -208,6 +237,8 @@ func parseOptions(opts *Options, allArgs []string) { case "-f", "--filter": filter := nextString(allArgs, &i, "query string required") opts.Filter = &filter + case "--expect": + opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required")) case "-d", "--delimiter": opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required")) case "-n", "--nth": @@ -285,6 +316,8 @@ func parseOptions(opts *Options, allArgs []string) { opts.WithNth = splitNth(value) } else if match, _ := optString(arg, "-s|--sort="); match { opts.Sort = 1 // Don't care + } else if match, value := optString(arg, "--expect="); match { + opts.Expect = parseKeyChords(value) } else { errorExit("unknown option: " + arg) } diff --git a/src/options_test.go b/src/options_test.go index 782ad791..b20cd6a3 100644 --- a/src/options_test.go +++ b/src/options_test.go @@ -1,6 +1,10 @@ package fzf -import "testing" +import ( + "testing" + + "github.com/junegunn/fzf/src/curses" +) func TestDelimiterRegex(t *testing.T) { rx := delimiterRegexp("*") @@ -65,3 +69,22 @@ func TestIrrelevantNth(t *testing.T) { } } } + +func TestExpectKeys(t *testing.T) { + keys := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g") + check := func(key int, expected int) { + if key != expected { + t.Errorf("%d != %d", key, expected) + } + } + check(len(keys), 9) + check(keys[0], curses.CtrlZ) + check(keys[1], curses.AltZ) + check(keys[2], curses.F2) + check(keys[3], curses.AltZ+'@') + check(keys[4], curses.AltA) + check(keys[5], curses.AltZ+'!') + check(keys[6], curses.CtrlA+'g'-'a') + check(keys[7], curses.AltZ+'J') + check(keys[8], curses.AltZ+'g') +} diff --git a/src/terminal.go b/src/terminal.go index 5570f8d1..2d191a90 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -28,6 +28,8 @@ type Terminal struct { yanked []rune input []rune multi bool + expect []int + pressed int printQuery bool count int progress int @@ -91,6 +93,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { yanked: []rune{}, input: input, multi: opts.Multi, + expect: opts.Expect, + pressed: 0, printQuery: opts.PrintQuery, merger: EmptyMerger, selected: make(map[*string]selectedItem), @@ -150,6 +154,19 @@ func (t *Terminal) output() { if t.printQuery { fmt.Println(string(t.input)) } + if len(t.expect) > 0 { + if t.pressed == 0 { + fmt.Println() + } else if util.Between(t.pressed, C.AltA, C.AltZ) { + fmt.Printf("alt-%c\n", t.pressed+'a'-C.AltA) + } else if util.Between(t.pressed, C.F1, C.F4) { + fmt.Printf("f%c\n", t.pressed+'1'-C.F1) + } else if util.Between(t.pressed, C.CtrlA, C.CtrlZ) { + fmt.Printf("ctrl-%c\n", t.pressed+'a'-C.CtrlA) + } else { + fmt.Printf("%c\n", t.pressed-C.AltZ) + } + } if len(t.selected) == 0 { cnt := t.merger.Length() if cnt > 0 && cnt > t.cy { @@ -535,6 +552,13 @@ func (t *Terminal) Loop() { req(reqInfo) } } + for _, key := range t.expect { + if event.Type == key || event.Type == C.Rune && int(event.Char) == key-C.AltZ { + t.pressed = key + req(reqClose) + break + } + } switch event.Type { case C.Invalid: t.mutex.Unlock() diff --git a/src/util/util.go b/src/util/util.go index 1f53cc76..2d680b1a 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -61,6 +61,10 @@ func DurWithin( return val } +func Between(val int, min int, max int) bool { + return val >= min && val <= max +} + // IsTty returns true is stdin is a terminal func IsTty() bool { return int(C.isatty(C.int(os.Stdin.Fd()))) != 0 |