summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2015-06-19 00:31:48 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2015-06-19 00:31:48 +0900
commita8b2c257cdd51181bec67eca80c7879787c49776 (patch)
treeba9b6ec5f54d7616160ebe02089597544fc3781e
parent5e8d8dab824524b83e7146d8ee583dab5e796a95 (diff)
downloadfzf-a8b2c257cdd51181bec67eca80c7879787c49776.tar.gz
Improve handling of key names
Remember the exact string given as the key name so that it's possible to correctly handle synonyms and print the original string.
-rw-r--r--src/options.go108
-rw-r--r--src/options_test.go146
-rw-r--r--src/terminal.go22
3 files changed, 147 insertions, 129 deletions
diff --git a/src/options.go b/src/options.go
index 8281ada4..a3b7cc84 100644
--- a/src/options.go
+++ b/src/options.go
@@ -117,7 +117,7 @@ type Options struct {
Exit0 bool
Filter *string
ToggleSort bool
- Expect []int
+ Expect map[int]string
Keymap map[int]actionType
Execmap map[int]string
PrintQuery bool
@@ -159,7 +159,7 @@ func defaultOptions() *Options {
Exit0: false,
Filter: nil,
ToggleSort: false,
- Expect: []int{},
+ Expect: make(map[int]string),
Keymap: defaultKeymap(),
Execmap: make(map[int]string),
PrintQuery: false,
@@ -265,7 +265,7 @@ func isAlphabet(char uint8) bool {
return char >= 'a' && char <= 'z'
}
-func parseKeyChords(str string, message string, bind bool) []int {
+func parseKeyChords(str string, message string) map[int]string {
if len(str) == 0 {
errorExit(message)
}
@@ -275,54 +275,51 @@ func parseKeyChords(str string, message string, bind bool) []int {
tokens = append(tokens, ",")
}
- var chords []int
+ chords := make(map[int]string)
for _, key := range tokens {
if len(key) == 0 {
continue // ignore
}
lkey := strings.ToLower(key)
chord := 0
- if bind {
- switch lkey {
- case "up":
- chord = curses.Up
- case "down":
- chord = curses.Down
- case "left":
- chord = curses.Left
- case "right":
- chord = curses.Right
- case "enter", "return":
- chord = curses.CtrlM
- case "space":
- chord = curses.AltZ + int(' ')
- case "bspace":
- chord = curses.BSpace
- case "alt-bs", "alt-bspace":
- chord = curses.AltBS
- case "tab":
- chord = curses.Tab
- case "btab":
- chord = curses.BTab
- case "esc":
- chord = curses.ESC
- case "del":
- chord = curses.Del
- case "home":
- chord = curses.Home
- case "end":
- chord = curses.End
- case "pgup", "page-up":
- chord = curses.PgUp
- case "pgdn", "page-down":
- chord = curses.PgDn
- case "shift-left":
- chord = curses.SLeft
- case "shift-right":
- chord = curses.SRight
- }
- }
- if chord == 0 {
+ switch lkey {
+ case "up":
+ chord = curses.Up
+ case "down":
+ chord = curses.Down
+ case "left":
+ chord = curses.Left
+ case "right":
+ chord = curses.Right
+ case "enter", "return":
+ chord = curses.CtrlM
+ case "space":
+ chord = curses.AltZ + int(' ')
+ case "bspace", "bs":
+ chord = curses.BSpace
+ case "alt-bs", "alt-bspace":
+ chord = curses.AltBS
+ case "tab":
+ chord = curses.Tab
+ case "btab", "shift-tab":
+ chord = curses.BTab
+ case "esc":
+ chord = curses.ESC
+ case "del":
+ chord = curses.Del
+ case "home":
+ chord = curses.Home
+ case "end":
+ chord = curses.End
+ case "pgup", "page-up":
+ chord = curses.PgUp
+ case "pgdn", "page-down":
+ chord = curses.PgDn
+ case "shift-left":
+ chord = curses.SLeft
+ case "shift-right":
+ chord = curses.SRight
+ default:
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
chord = curses.CtrlA + int(lkey[5]) - 'a'
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
@@ -336,7 +333,7 @@ func parseKeyChords(str string, message string, bind bool) []int {
}
}
if chord > 0 {
- chords = append(chords, chord)
+ chords[chord] = key
}
}
return chords
@@ -428,6 +425,13 @@ func parseTheme(defaultTheme *curses.ColorTheme, str string) *curses.ColorTheme
var executeRegexp *regexp.Regexp
+func firstKey(keymap map[int]string) int {
+ for k := range keymap {
+ return k
+ }
+ return 0
+}
+
func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort bool, str string) (map[int]actionType, map[int]string, bool) {
if executeRegexp == nil {
// Backreferences are not supported.
@@ -451,11 +455,11 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort b
if len(pair) != 2 {
fail()
}
- keys := parseKeyChords(pair[0], "key name required", true)
+ keys := parseKeyChords(pair[0], "key name required")
if len(keys) != 1 {
fail()
}
- key := keys[0]
+ key := firstKey(keys)
act := strings.ToLower(pair[1])
switch act {
case "ignore":
@@ -551,11 +555,11 @@ func isExecuteAction(str string) bool {
}
func checkToggleSort(keymap map[int]actionType, str string) map[int]actionType {
- keys := parseKeyChords(str, "key name required", true)
+ keys := parseKeyChords(str, "key name required")
if len(keys) != 1 {
errorExit("multiple keys specified")
}
- keymap[keys[0]] = actToggleSort
+ keymap[firstKey(keys)] = actToggleSort
return keymap
}
@@ -600,7 +604,7 @@ func parseOptions(opts *Options, allArgs []string) {
filter := nextString(allArgs, &i, "query string required")
opts.Filter = &filter
case "--expect":
- opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required", false)
+ opts.Expect = parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required")
case "--tiebreak":
opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
case "--bind":
@@ -717,7 +721,7 @@ func parseOptions(opts *Options, allArgs []string) {
keymap = checkToggleSort(keymap, value)
opts.ToggleSort = true
} else if match, value := optString(arg, "--expect="); match {
- opts.Expect = parseKeyChords(value, "key names required", false)
+ 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 {
diff --git a/src/options_test.go b/src/options_test.go
index dee1c0d1..8e44585d 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -72,77 +72,101 @@ func TestIrrelevantNth(t *testing.T) {
}
func TestParseKeys(t *testing.T) {
- keys := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g", "", false)
- check := func(key int, expected int) {
- if key != expected {
- t.Errorf("%d != %d", key, expected)
+ pairs := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g", "")
+ check := func(i int, s string) {
+ if pairs[i] != s {
+ t.Errorf("%s != %s", pairs[i], s)
}
}
- 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')
+ if len(pairs) != 9 {
+ t.Error(9)
+ }
+ check(curses.CtrlZ, "ctrl-z")
+ check(curses.AltZ, "alt-z")
+ check(curses.F2, "f2")
+ check(curses.AltZ+'@', "@")
+ check(curses.AltA, "Alt-a")
+ check(curses.AltZ+'!', "!")
+ check(curses.CtrlA+'g'-'a', "ctrl-G")
+ check(curses.AltZ+'J', "J")
+ check(curses.AltZ+'g', "g")
// Synonyms
- keys = parseKeyChords("enter,return,space,tab,btab,esc,up,down,left,right", "", true)
- check(len(keys), 10)
- check(keys[0], curses.CtrlM)
- check(keys[1], curses.CtrlM)
- check(keys[2], curses.AltZ+' ')
- check(keys[3], curses.Tab)
- check(keys[4], curses.BTab)
- check(keys[5], curses.ESC)
- check(keys[6], curses.Up)
- check(keys[7], curses.Down)
- check(keys[8], curses.Left)
- check(keys[9], curses.Right)
+ pairs = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "")
+ if len(pairs) != 9 {
+ t.Error(9)
+ }
+ check(curses.CtrlM, "Return")
+ check(curses.AltZ+' ', "space")
+ check(curses.Tab, "tab")
+ check(curses.BTab, "btab")
+ check(curses.ESC, "esc")
+ check(curses.Up, "up")
+ check(curses.Down, "down")
+ check(curses.Left, "left")
+ check(curses.Right, "right")
+
+ pairs = parseKeyChords("Tab,Ctrl-I,PgUp,page-up,pgdn,Page-Down,Home,End,Alt-BS,Alt-BSpace,shift-left,shift-right,btab,shift-tab,return,Enter,bspace", "")
+ if len(pairs) != 11 {
+ t.Error(11)
+ }
+ check(curses.Tab, "Ctrl-I")
+ check(curses.PgUp, "page-up")
+ check(curses.PgDn, "Page-Down")
+ check(curses.Home, "Home")
+ check(curses.End, "End")
+ check(curses.AltBS, "Alt-BSpace")
+ check(curses.SLeft, "shift-left")
+ check(curses.SRight, "shift-right")
+ check(curses.BTab, "shift-tab")
+ check(curses.CtrlM, "Enter")
+ check(curses.BSpace, "bspace")
}
func TestParseKeysWithComma(t *testing.T) {
- check := func(key int, expected int) {
- if key != expected {
- t.Errorf("%d != %d", key, expected)
+ checkN := func(a int, b int) {
+ if a != b {
+ t.Errorf("%d != %d", a, b)
+ }
+ }
+ check := func(pairs map[int]string, i int, s string) {
+ if pairs[i] != s {
+ t.Errorf("%s != %s", pairs[i], s)
}
}
- keys := parseKeyChords(",", "", false)
- check(len(keys), 1)
- check(keys[0], curses.AltZ+',')
-
- keys = parseKeyChords(",,a,b", "", false)
- check(len(keys), 3)
- check(keys[0], curses.AltZ+'a')
- check(keys[1], curses.AltZ+'b')
- check(keys[2], curses.AltZ+',')
-
- keys = parseKeyChords("a,b,,", "", false)
- check(len(keys), 3)
- check(keys[0], curses.AltZ+'a')
- check(keys[1], curses.AltZ+'b')
- check(keys[2], curses.AltZ+',')
-
- keys = parseKeyChords("a,,,b", "", false)
- check(len(keys), 3)
- check(keys[0], curses.AltZ+'a')
- check(keys[1], curses.AltZ+'b')
- check(keys[2], curses.AltZ+',')
-
- keys = parseKeyChords("a,,,b,c", "", false)
- check(len(keys), 4)
- check(keys[0], curses.AltZ+'a')
- check(keys[1], curses.AltZ+'b')
- check(keys[2], curses.AltZ+'c')
- check(keys[3], curses.AltZ+',')
-
- keys = parseKeyChords(",,,", "", false)
- check(len(keys), 1)
- check(keys[0], curses.AltZ+',')
+ pairs := parseKeyChords(",", "")
+ checkN(len(pairs), 1)
+ check(pairs, curses.AltZ+',', ",")
+
+ pairs = parseKeyChords(",,a,b", "")
+ checkN(len(pairs), 3)
+ check(pairs, curses.AltZ+'a', "a")
+ check(pairs, curses.AltZ+'b', "b")
+ check(pairs, curses.AltZ+',', ",")
+
+ pairs = parseKeyChords("a,b,,", "")
+ checkN(len(pairs), 3)
+ check(pairs, curses.AltZ+'a', "a")
+ check(pairs, curses.AltZ+'b', "b")
+ check(pairs, curses.AltZ+',', ",")
+
+ pairs = parseKeyChords("a,,,b", "")
+ checkN(len(pairs), 3)
+ check(pairs, curses.AltZ+'a', "a")
+ check(pairs, curses.AltZ+'b', "b")
+ check(pairs, curses.AltZ+',', ",")
+
+ pairs = parseKeyChords("a,,,b,c", "")
+ checkN(len(pairs), 4)
+ check(pairs, curses.AltZ+'a', "a")
+ check(pairs, curses.AltZ+'b', "b")
+ check(pairs, curses.AltZ+'c', "c")
+ check(pairs, curses.AltZ+',', ",")
+
+ pairs = parseKeyChords(",,,", "")
+ checkN(len(pairs), 1)
+ check(pairs, curses.AltZ+',', ",")
}
func TestBind(t *testing.T) {
diff --git a/src/terminal.go b/src/terminal.go
index aca8aad4..9c731977 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -33,10 +33,10 @@ type Terminal struct {
multi bool
sort bool
toggleSort bool
- expect []int
+ expect map[int]string
keymap map[int]actionType
execmap map[int]string
- pressed int
+ pressed string
printQuery bool
history *History
cycle bool
@@ -193,7 +193,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
expect: opts.Expect,
keymap: opts.Keymap,
execmap: opts.Execmap,
- pressed: 0,
+ pressed: "",
printQuery: opts.PrintQuery,
history: opts.History,
cycle: opts.Cycle,
@@ -257,17 +257,7 @@ func (t *Terminal) output() {
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)
- }
+ fmt.Println(t.pressed)
}
if len(t.selected) == 0 {
cnt := t.merger.Length()
@@ -727,9 +717,9 @@ func (t *Terminal) Loop() {
req(reqInfo)
}
}
- for _, key := range t.expect {
+ for key, ret := range t.expect {
if keyMatch(key, event) {
- t.pressed = key
+ t.pressed = ret
req(reqClose)
break
}