summaryrefslogtreecommitdiffstats
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)
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
}