mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-26 14:56:30 +00:00
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.
This commit is contained in:
parent
5e8d8dab82
commit
a8b2c257cd
108
src/options.go
108
src/options.go
@ -117,7 +117,7 @@ type Options struct {
|
|||||||
Exit0 bool
|
Exit0 bool
|
||||||
Filter *string
|
Filter *string
|
||||||
ToggleSort bool
|
ToggleSort bool
|
||||||
Expect []int
|
Expect map[int]string
|
||||||
Keymap map[int]actionType
|
Keymap map[int]actionType
|
||||||
Execmap map[int]string
|
Execmap map[int]string
|
||||||
PrintQuery bool
|
PrintQuery bool
|
||||||
@ -159,7 +159,7 @@ func defaultOptions() *Options {
|
|||||||
Exit0: false,
|
Exit0: false,
|
||||||
Filter: nil,
|
Filter: nil,
|
||||||
ToggleSort: false,
|
ToggleSort: false,
|
||||||
Expect: []int{},
|
Expect: make(map[int]string),
|
||||||
Keymap: defaultKeymap(),
|
Keymap: defaultKeymap(),
|
||||||
Execmap: make(map[int]string),
|
Execmap: make(map[int]string),
|
||||||
PrintQuery: false,
|
PrintQuery: false,
|
||||||
@ -265,7 +265,7 @@ func isAlphabet(char uint8) bool {
|
|||||||
return char >= 'a' && char <= 'z'
|
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 {
|
if len(str) == 0 {
|
||||||
errorExit(message)
|
errorExit(message)
|
||||||
}
|
}
|
||||||
@ -275,54 +275,51 @@ func parseKeyChords(str string, message string, bind bool) []int {
|
|||||||
tokens = append(tokens, ",")
|
tokens = append(tokens, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
var chords []int
|
chords := make(map[int]string)
|
||||||
for _, key := range tokens {
|
for _, key := range tokens {
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
continue // ignore
|
continue // ignore
|
||||||
}
|
}
|
||||||
lkey := strings.ToLower(key)
|
lkey := strings.ToLower(key)
|
||||||
chord := 0
|
chord := 0
|
||||||
if bind {
|
switch lkey {
|
||||||
switch lkey {
|
case "up":
|
||||||
case "up":
|
chord = curses.Up
|
||||||
chord = curses.Up
|
case "down":
|
||||||
case "down":
|
chord = curses.Down
|
||||||
chord = curses.Down
|
case "left":
|
||||||
case "left":
|
chord = curses.Left
|
||||||
chord = curses.Left
|
case "right":
|
||||||
case "right":
|
chord = curses.Right
|
||||||
chord = curses.Right
|
case "enter", "return":
|
||||||
case "enter", "return":
|
chord = curses.CtrlM
|
||||||
chord = curses.CtrlM
|
case "space":
|
||||||
case "space":
|
chord = curses.AltZ + int(' ')
|
||||||
chord = curses.AltZ + int(' ')
|
case "bspace", "bs":
|
||||||
case "bspace":
|
chord = curses.BSpace
|
||||||
chord = curses.BSpace
|
case "alt-bs", "alt-bspace":
|
||||||
case "alt-bs", "alt-bspace":
|
chord = curses.AltBS
|
||||||
chord = curses.AltBS
|
case "tab":
|
||||||
case "tab":
|
chord = curses.Tab
|
||||||
chord = curses.Tab
|
case "btab", "shift-tab":
|
||||||
case "btab":
|
chord = curses.BTab
|
||||||
chord = curses.BTab
|
case "esc":
|
||||||
case "esc":
|
chord = curses.ESC
|
||||||
chord = curses.ESC
|
case "del":
|
||||||
case "del":
|
chord = curses.Del
|
||||||
chord = curses.Del
|
case "home":
|
||||||
case "home":
|
chord = curses.Home
|
||||||
chord = curses.Home
|
case "end":
|
||||||
case "end":
|
chord = curses.End
|
||||||
chord = curses.End
|
case "pgup", "page-up":
|
||||||
case "pgup", "page-up":
|
chord = curses.PgUp
|
||||||
chord = curses.PgUp
|
case "pgdn", "page-down":
|
||||||
case "pgdn", "page-down":
|
chord = curses.PgDn
|
||||||
chord = curses.PgDn
|
case "shift-left":
|
||||||
case "shift-left":
|
chord = curses.SLeft
|
||||||
chord = curses.SLeft
|
case "shift-right":
|
||||||
case "shift-right":
|
chord = curses.SRight
|
||||||
chord = curses.SRight
|
default:
|
||||||
}
|
|
||||||
}
|
|
||||||
if chord == 0 {
|
|
||||||
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
||||||
chord = curses.CtrlA + int(lkey[5]) - 'a'
|
chord = curses.CtrlA + int(lkey[5]) - 'a'
|
||||||
} else if len(key) == 5 && strings.HasPrefix(lkey, "alt-") && isAlphabet(lkey[4]) {
|
} 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 {
|
if chord > 0 {
|
||||||
chords = append(chords, chord)
|
chords[chord] = key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chords
|
return chords
|
||||||
@ -428,6 +425,13 @@ func parseTheme(defaultTheme *curses.ColorTheme, str string) *curses.ColorTheme
|
|||||||
|
|
||||||
var executeRegexp *regexp.Regexp
|
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) {
|
func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort bool, str string) (map[int]actionType, map[int]string, bool) {
|
||||||
if executeRegexp == nil {
|
if executeRegexp == nil {
|
||||||
// Backreferences are not supported.
|
// Backreferences are not supported.
|
||||||
@ -451,11 +455,11 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, toggleSort b
|
|||||||
if len(pair) != 2 {
|
if len(pair) != 2 {
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
keys := parseKeyChords(pair[0], "key name required", true)
|
keys := parseKeyChords(pair[0], "key name required")
|
||||||
if len(keys) != 1 {
|
if len(keys) != 1 {
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
key := keys[0]
|
key := firstKey(keys)
|
||||||
act := strings.ToLower(pair[1])
|
act := strings.ToLower(pair[1])
|
||||||
switch act {
|
switch act {
|
||||||
case "ignore":
|
case "ignore":
|
||||||
@ -551,11 +555,11 @@ func isExecuteAction(str string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkToggleSort(keymap map[int]actionType, str string) map[int]actionType {
|
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 {
|
if len(keys) != 1 {
|
||||||
errorExit("multiple keys specified")
|
errorExit("multiple keys specified")
|
||||||
}
|
}
|
||||||
keymap[keys[0]] = actToggleSort
|
keymap[firstKey(keys)] = actToggleSort
|
||||||
return keymap
|
return keymap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +604,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
filter := nextString(allArgs, &i, "query string required")
|
filter := nextString(allArgs, &i, "query string required")
|
||||||
opts.Filter = &filter
|
opts.Filter = &filter
|
||||||
case "--expect":
|
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":
|
case "--tiebreak":
|
||||||
opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
|
opts.Tiebreak = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
|
||||||
case "--bind":
|
case "--bind":
|
||||||
@ -717,7 +721,7 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
keymap = checkToggleSort(keymap, value)
|
keymap = checkToggleSort(keymap, value)
|
||||||
opts.ToggleSort = true
|
opts.ToggleSort = true
|
||||||
} else if match, value := optString(arg, "--expect="); match {
|
} 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 {
|
} else if match, value := optString(arg, "--tiebreak="); match {
|
||||||
opts.Tiebreak = parseTiebreak(value)
|
opts.Tiebreak = parseTiebreak(value)
|
||||||
} else if match, value := optString(arg, "--color="); match {
|
} else if match, value := optString(arg, "--color="); match {
|
||||||
|
@ -72,77 +72,101 @@ func TestIrrelevantNth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseKeys(t *testing.T) {
|
func TestParseKeys(t *testing.T) {
|
||||||
keys := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g", "", false)
|
pairs := parseKeyChords("ctrl-z,alt-z,f2,@,Alt-a,!,ctrl-G,J,g", "")
|
||||||
check := func(key int, expected int) {
|
check := func(i int, s string) {
|
||||||
if key != expected {
|
if pairs[i] != s {
|
||||||
t.Errorf("%d != %d", key, expected)
|
t.Errorf("%s != %s", pairs[i], s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check(len(keys), 9)
|
if len(pairs) != 9 {
|
||||||
check(keys[0], curses.CtrlZ)
|
t.Error(9)
|
||||||
check(keys[1], curses.AltZ)
|
}
|
||||||
check(keys[2], curses.F2)
|
check(curses.CtrlZ, "ctrl-z")
|
||||||
check(keys[3], curses.AltZ+'@')
|
check(curses.AltZ, "alt-z")
|
||||||
check(keys[4], curses.AltA)
|
check(curses.F2, "f2")
|
||||||
check(keys[5], curses.AltZ+'!')
|
check(curses.AltZ+'@', "@")
|
||||||
check(keys[6], curses.CtrlA+'g'-'a')
|
check(curses.AltA, "Alt-a")
|
||||||
check(keys[7], curses.AltZ+'J')
|
check(curses.AltZ+'!', "!")
|
||||||
check(keys[8], curses.AltZ+'g')
|
check(curses.CtrlA+'g'-'a', "ctrl-G")
|
||||||
|
check(curses.AltZ+'J', "J")
|
||||||
|
check(curses.AltZ+'g', "g")
|
||||||
|
|
||||||
// Synonyms
|
// Synonyms
|
||||||
keys = parseKeyChords("enter,return,space,tab,btab,esc,up,down,left,right", "", true)
|
pairs = parseKeyChords("enter,Return,space,tab,btab,esc,up,down,left,right", "")
|
||||||
check(len(keys), 10)
|
if len(pairs) != 9 {
|
||||||
check(keys[0], curses.CtrlM)
|
t.Error(9)
|
||||||
check(keys[1], curses.CtrlM)
|
}
|
||||||
check(keys[2], curses.AltZ+' ')
|
check(curses.CtrlM, "Return")
|
||||||
check(keys[3], curses.Tab)
|
check(curses.AltZ+' ', "space")
|
||||||
check(keys[4], curses.BTab)
|
check(curses.Tab, "tab")
|
||||||
check(keys[5], curses.ESC)
|
check(curses.BTab, "btab")
|
||||||
check(keys[6], curses.Up)
|
check(curses.ESC, "esc")
|
||||||
check(keys[7], curses.Down)
|
check(curses.Up, "up")
|
||||||
check(keys[8], curses.Left)
|
check(curses.Down, "down")
|
||||||
check(keys[9], curses.Right)
|
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) {
|
func TestParseKeysWithComma(t *testing.T) {
|
||||||
check := func(key int, expected int) {
|
checkN := func(a int, b int) {
|
||||||
if key != expected {
|
if a != b {
|
||||||
t.Errorf("%d != %d", key, expected)
|
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)
|
pairs := parseKeyChords(",", "")
|
||||||
check(len(keys), 1)
|
checkN(len(pairs), 1)
|
||||||
check(keys[0], curses.AltZ+',')
|
check(pairs, curses.AltZ+',', ",")
|
||||||
|
|
||||||
keys = parseKeyChords(",,a,b", "", false)
|
pairs = parseKeyChords(",,a,b", "")
|
||||||
check(len(keys), 3)
|
checkN(len(pairs), 3)
|
||||||
check(keys[0], curses.AltZ+'a')
|
check(pairs, curses.AltZ+'a', "a")
|
||||||
check(keys[1], curses.AltZ+'b')
|
check(pairs, curses.AltZ+'b', "b")
|
||||||
check(keys[2], curses.AltZ+',')
|
check(pairs, curses.AltZ+',', ",")
|
||||||
|
|
||||||
keys = parseKeyChords("a,b,,", "", false)
|
pairs = parseKeyChords("a,b,,", "")
|
||||||
check(len(keys), 3)
|
checkN(len(pairs), 3)
|
||||||
check(keys[0], curses.AltZ+'a')
|
check(pairs, curses.AltZ+'a', "a")
|
||||||
check(keys[1], curses.AltZ+'b')
|
check(pairs, curses.AltZ+'b', "b")
|
||||||
check(keys[2], curses.AltZ+',')
|
check(pairs, curses.AltZ+',', ",")
|
||||||
|
|
||||||
keys = parseKeyChords("a,,,b", "", false)
|
pairs = parseKeyChords("a,,,b", "")
|
||||||
check(len(keys), 3)
|
checkN(len(pairs), 3)
|
||||||
check(keys[0], curses.AltZ+'a')
|
check(pairs, curses.AltZ+'a', "a")
|
||||||
check(keys[1], curses.AltZ+'b')
|
check(pairs, curses.AltZ+'b', "b")
|
||||||
check(keys[2], curses.AltZ+',')
|
check(pairs, curses.AltZ+',', ",")
|
||||||
|
|
||||||
keys = parseKeyChords("a,,,b,c", "", false)
|
pairs = parseKeyChords("a,,,b,c", "")
|
||||||
check(len(keys), 4)
|
checkN(len(pairs), 4)
|
||||||
check(keys[0], curses.AltZ+'a')
|
check(pairs, curses.AltZ+'a', "a")
|
||||||
check(keys[1], curses.AltZ+'b')
|
check(pairs, curses.AltZ+'b', "b")
|
||||||
check(keys[2], curses.AltZ+'c')
|
check(pairs, curses.AltZ+'c', "c")
|
||||||
check(keys[3], curses.AltZ+',')
|
check(pairs, curses.AltZ+',', ",")
|
||||||
|
|
||||||
keys = parseKeyChords(",,,", "", false)
|
pairs = parseKeyChords(",,,", "")
|
||||||
check(len(keys), 1)
|
checkN(len(pairs), 1)
|
||||||
check(keys[0], curses.AltZ+',')
|
check(pairs, curses.AltZ+',', ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBind(t *testing.T) {
|
func TestBind(t *testing.T) {
|
||||||
|
@ -33,10 +33,10 @@ type Terminal struct {
|
|||||||
multi bool
|
multi bool
|
||||||
sort bool
|
sort bool
|
||||||
toggleSort bool
|
toggleSort bool
|
||||||
expect []int
|
expect map[int]string
|
||||||
keymap map[int]actionType
|
keymap map[int]actionType
|
||||||
execmap map[int]string
|
execmap map[int]string
|
||||||
pressed int
|
pressed string
|
||||||
printQuery bool
|
printQuery bool
|
||||||
history *History
|
history *History
|
||||||
cycle bool
|
cycle bool
|
||||||
@ -193,7 +193,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
expect: opts.Expect,
|
expect: opts.Expect,
|
||||||
keymap: opts.Keymap,
|
keymap: opts.Keymap,
|
||||||
execmap: opts.Execmap,
|
execmap: opts.Execmap,
|
||||||
pressed: 0,
|
pressed: "",
|
||||||
printQuery: opts.PrintQuery,
|
printQuery: opts.PrintQuery,
|
||||||
history: opts.History,
|
history: opts.History,
|
||||||
cycle: opts.Cycle,
|
cycle: opts.Cycle,
|
||||||
@ -257,17 +257,7 @@ func (t *Terminal) output() {
|
|||||||
fmt.Println(string(t.input))
|
fmt.Println(string(t.input))
|
||||||
}
|
}
|
||||||
if len(t.expect) > 0 {
|
if len(t.expect) > 0 {
|
||||||
if t.pressed == 0 {
|
fmt.Println(t.pressed)
|
||||||
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 {
|
if len(t.selected) == 0 {
|
||||||
cnt := t.merger.Length()
|
cnt := t.merger.Length()
|
||||||
@ -727,9 +717,9 @@ func (t *Terminal) Loop() {
|
|||||||
req(reqInfo)
|
req(reqInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range t.expect {
|
for key, ret := range t.expect {
|
||||||
if keyMatch(key, event) {
|
if keyMatch(key, event) {
|
||||||
t.pressed = key
|
t.pressed = ret
|
||||||
req(reqClose)
|
req(reqClose)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user