summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2015-03-29 02:59:32 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2015-03-29 02:59:32 +0900
commit2a167aa030b244060fc479d2b88fdb9b9171d026 (patch)
tree69b994e5b97ad9a07107569fcb1227de7c886cbb /src
parent9cfecf7f0bb52441c27b769427fdf05f45b3110d (diff)
Implement --expect option to support simple key bindings (#163)
Diffstat (limited to 'src')
-rw-r--r--src/curses/curses.go25
-rw-r--r--src/options.go33
-rw-r--r--src/options_test.go25
-rw-r--r--src/terminal.go24
-rw-r--r--src/util/util.go4
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