summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md6
-rw-r--r--man/man1/fzf.112
-rw-r--r--src/options.go288
-rw-r--r--src/options_test.go76
-rw-r--r--src/terminal.go149
5 files changed, 293 insertions, 238 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 473f899f..fac6f347 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+0.16.2
+------
+- Added support for composite actions in `--bind`. Multiple actions can be
+ chained using `+` separator.
+ - e.g. `fzf --bind 'ctrl-y:execute(echo -n {} | pbcopy)+abort'`
+
0.16.1
------
- Fixed `--height` option to properly fill the window with the background
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 5160d6b3..b139dcc0 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -481,17 +481,21 @@ e.g. \fBfzf --bind=ctrl-j:accept,ctrl-k:kill-line\fR
\fBselect-all\fR
\fBtoggle\fR
\fBtoggle-all\fR
- \fBtoggle-down\fR \fIctrl-i (tab)\fR
- \fBtoggle-in\fR (\fB--reverse\fR ? \fBtoggle-up\fR : \fBtoggle-down\fR)
- \fBtoggle-out\fR (\fB--reverse\fR ? \fBtoggle-down\fR : \fBtoggle-up\fR)
+ \fBtoggle+down\fR \fIctrl-i (tab)\fR
+ \fBtoggle-in\fR (\fB--reverse\fR ? \fBtoggle+up\fR : \fBtoggle+down\fR)
+ \fBtoggle-out\fR (\fB--reverse\fR ? \fBtoggle+down\fR : \fBtoggle+up\fR)
\fBtoggle-preview\fR
\fBtoggle-sort\fR (equivalent to \fB--toggle-sort\fR)
- \fBtoggle-up\fR \fIbtab (shift-tab)\fR
+ \fBtoggle+up\fR \fIbtab (shift-tab)\fR
\fBunix-line-discard\fR \fIctrl-u\fR
\fBunix-word-rubout\fR \fIctrl-w\fR
\fBup\fR \fIctrl-k ctrl-p up\fR
\fByank\fR \fIctrl-y\fR
+Multiple actions can be chained using \fB+\fR separator.
+
+ \fBfzf --bind 'ctrl-a:select-all+accept'\fR
+
With \fBexecute(...)\fR action, you can execute arbitrary commands without
leaving fzf. For example, you can turn fzf into a simple file browser by
binding \fBenter\fR key to \fBless\fR command like follows.
diff --git a/src/options.go b/src/options.go
index 16020ddf..eb21ee7a 100644
--- a/src/options.go
+++ b/src/options.go
@@ -171,8 +171,7 @@ type Options struct {
Filter *string
ToggleSort bool
Expect map[int]string
- Keymap map[int]actionType
- Execmap map[int]string
+ Keymap map[int][]action
Preview previewOpts
PrintQuery bool
ReadZero bool
@@ -220,8 +219,7 @@ func defaultOptions() *Options {
Filter: nil,
ToggleSort: false,
Expect: make(map[int]string),
- Keymap: make(map[int]actionType),
- Execmap: make(map[int]string),
+ Keymap: make(map[int][]action),
Preview: previewOpts{"", posRight, sizeSpec{50, true}, false, false},
PrintQuery: false,
ReadZero: false,
@@ -578,23 +576,25 @@ func firstKey(keymap map[int]string) int {
const (
escapedColon = 0
escapedComma = 1
+ escapedPlus = 2
)
-func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string) {
+func parseKeymap(keymap map[int][]action, str string) {
if executeRegexp == nil {
// Backreferences are not supported.
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
executeRegexp = regexp.MustCompile(
- "(?s):execute(-multi)?:.*|:execute(-multi)?(\\([^)]*\\)|\\[[^\\]]*\\]|~[^~]*~|![^!]*!|@[^@]*@|\\#[^\\#]*\\#|\\$[^\\$]*\\$|%[^%]*%|\\^[^\\^]*\\^|&[^&]*&|\\*[^\\*]*\\*|;[^;]*;|/[^/]*/|\\|[^\\|]*\\|)")
+ "(?si):execute(-multi)?:.+|:execute(-multi)?(\\([^)]*\\)|\\[[^\\]]*\\]|~[^~]*~|![^!]*!|@[^@]*@|\\#[^\\#]*\\#|\\$[^\\$]*\\$|%[^%]*%|\\^[^\\^]*\\^|&[^&]*&|\\*[^\\*]*\\*|;[^;]*;|/[^/]*/|\\|[^\\|]*\\|)")
}
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
- if strings.HasPrefix(src, ":execute-multi") {
+ if src[len(":execute")] == '-' {
return ":execute-multi(" + strings.Repeat(" ", len(src)-len(":execute-multi()")) + ")"
}
return ":execute(" + strings.Repeat(" ", len(src)-len(":execute()")) + ")"
})
masked = strings.Replace(masked, "::", string([]rune{escapedColon, ':'}), -1)
masked = strings.Replace(masked, ",:", string([]rune{escapedComma, ':'}), -1)
+ masked = strings.Replace(masked, "+:", string([]rune{escapedPlus, ':'}), -1)
idx := 0
for _, pairStr := range strings.Split(masked, ",") {
@@ -610,151 +610,173 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string)
key = ':' + tui.AltZ
} else if len(pair[0]) == 1 && pair[0][0] == escapedComma {
key = ',' + tui.AltZ
+ } else if len(pair[0]) == 1 && pair[0][0] == escapedPlus {
+ key = '+' + tui.AltZ
} else {
keys := parseKeyChords(pair[0], "key name required")
key = firstKey(keys)
}
- act := origPairStr[len(pair[0])+1 : len(origPairStr)]
- actLower := strings.ToLower(act)
- switch actLower {
- case "ignore":
- keymap[key] = actIgnore
- case "beginning-of-line":
- keymap[key] = actBeginningOfLine
- case "abort":
- keymap[key] = actAbort
- case "accept":
- keymap[key] = actAccept
- case "print-query":
- keymap[key] = actPrintQuery
- case "backward-char":
- keymap[key] = actBackwardChar
- case "backward-delete-char":
- keymap[key] = actBackwardDeleteChar
- case "backward-word":
- keymap[key] = actBackwardWord
- case "clear-screen":
- keymap[key] = actClearScreen
- case "delete-char":
- keymap[key] = actDeleteChar
- case "delete-char/eof":
- keymap[key] = actDeleteCharEOF
- case "end-of-line":
- keymap[key] = actEndOfLine
- case "cancel":
- keymap[key] = actCancel
- case "forward-char":
- keymap[key] = actForwardChar
- case "forward-word":
- keymap[key] = actForwardWord
- case "jump":
- keymap[key] = actJump
- case "jump-accept":
- keymap[key] = actJumpAccept
- case "kill-line":
- keymap[key] = actKillLine
- case "kill-word":
- keymap[key] = actKillWord
- case "unix-line-discard", "line-discard":
- keymap[key] = actUnixLineDiscard
- case "unix-word-rubout", "word-rubout":
- keymap[key] = actUnixWordRubout
- case "yank":
- keymap[key] = actYank
- case "backward-kill-word":
- keymap[key] = actBackwardKillWord
- case "toggle-down":
- keymap[key] = actToggleDown
- case "toggle-up":
- keymap[key] = actToggleUp
- case "toggle-in":
- keymap[key] = actToggleIn
- case "toggle-out":
- keymap[key] = actToggleOut
- case "toggle-all":
- keymap[key] = actToggleAll
- case "select-all":
- keymap[key] = actSelectAll
- case "deselect-all":
- keymap[key] = actDeselectAll
- case "toggle":
- keymap[key] = actToggle
- case "down":
- keymap[key] = actDown
- case "up":
- keymap[key] = actUp
- case "page-up":
- keymap[key] = actPageUp
- case "page-down":
- keymap[key] = actPageDown
- case "half-page-up":
- keymap[key] = actHalfPageUp
- case "half-page-down":
- keymap[key] = actHalfPageDown
- case "previous-history":
- keymap[key] = actPreviousHistory
- case "next-history":
- keymap[key] = actNextHistory
- case "toggle-preview":
- keymap[key] = actTogglePreview
- case "toggle-sort":
- keymap[key] = actToggleSort
- case "preview-up":
- keymap[key] = actPreviewUp
- case "preview-down":
- keymap[key] = actPreviewDown
- case "preview-page-up":
- keymap[key] = actPreviewPageUp
- case "preview-page-down":
- keymap[key] = actPreviewPageDown
- default:
- if isExecuteAction(actLower) {
- var offset int
- if strings.HasPrefix(actLower, "execute-multi") {
- keymap[key] = actExecuteMulti
- offset = len("execute-multi")
- } else {
- keymap[key] = actExecute
- offset = len("execute")
- }
- if act[offset] == ':' {
- execmap[key] = act[offset+1:]
+ idx2 := len(pair[0]) + 1
+ specs := strings.Split(pair[1], "+")
+ actions := make([]action, 0, len(specs))
+ appendAction := func(types ...actionType) {
+ actions = append(actions, toActions(types...)...)
+ }
+ prevSpec := ""
+ for specIndex, maskedSpec := range specs {
+ spec := origPairStr[idx2 : idx2+len(maskedSpec)]
+ idx2 += len(maskedSpec) + 1
+ spec = prevSpec + spec
+ specLower := strings.ToLower(spec)
+ switch specLower {
+ case "ignore":
+ appendAction(actIgnore)
+ case "beginning-of-line":
+ appendAction(actBeginningOfLine)
+ case "abort":
+ appendAction(actAbort)
+ case "accept":
+ appendAction(actAccept)
+ case "print-query":
+ appendAction(actPrintQuery)
+ case "backward-char":
+ appendAction(actBackwardChar)
+ case "backward-delete-char":
+ appendAction(actBackwardDeleteChar)
+ case "backward-word":
+ appendAction(actBackwardWord)
+ case "clear-screen":
+ appendAction(actClearScreen)
+ case "delete-char":
+ appendAction(actDeleteChar)
+ case "delete-char/eof":
+ appendAction(actDeleteCharEOF)
+ case "end-of-line":
+ appendAction(actEndOfLine)
+ case "cancel":
+ appendAction(actCancel)
+ case "forward-char":
+ appendAction(actForwardChar)
+ case "forward-word":
+ appendAction(actForwardWord)
+ case "jump":
+ appendAction(actJump)
+ case "jump-accept":
+ appendAction(actJumpAccept)
+ case "kill-line":
+ appendAction(actKillLine)
+ case "kill-word":
+ appendAction(actKillWord)
+ case "unix-line-discard", "line-discard":
+ appendAction(actUnixLineDiscard)
+ case "unix-word-rubout", "word-rubout":
+ appendAction(actUnixWordRubout)
+ case "yank":
+ appendAction(actYank)
+ case "backward-kill-word":
+ appendAction(actBackwardKillWord)
+ case "toggle-down":
+ appendAction(actToggle, actDown)
+ case "toggle-up":
+ appendAction(actToggle, actUp)
+ case "toggle-in":
+ appendAction(actToggleIn)
+ case "toggle-out":
+ appendAction(actToggleOut)
+ case "toggle-all":
+ appendAction(actToggleAll)
+ case "select-all":
+ appendAction(actSelectAll)
+ case "deselect-all":
+ appendAction(actDeselectAll)
+ case "toggle":
+ appendAction(actToggle)
+ case "down":
+ appendAction(actDown)
+ case "up":
+ appendAction(actUp)
+ case "page-up":
+ appendAction(actPageUp)
+ case "page-down":
+ appendAction(actPageDown)
+ case "half-page-up":
+ appendAction(actHalfPageUp)
+ case "half-page-down":
+ appendAction(actHalfPageDown)
+ case "previous-history":
+ appendAction(actPreviousHistory)
+ case "next-history":
+ appendAction(actNextHistory)
+ case "toggle-preview":
+ appendAction(actTogglePreview)
+ case "toggle-sort":
+ appendAction(actToggleSort)
+ case "preview-up":
+ appendAction(actPreviewUp)
+ case "preview-down":
+ appendAction(actPreviewDown)
+ case "preview-page-up":
+ appendAction(actPreviewPageUp)
+ case "preview-page-down":
+ appendAction(actPreviewPageDown)
+ default:
+ t := isExecuteAction(specLower)
+ if t == actIgnore {
+ errorExit("unknown action: " + spec)
} else {
- execmap[key] = act[offset+1 : len(act)-1]
+ var offset int
+ if t == actExecuteMulti {
+ offset = len("execute-multi")
+ } else {
+ offset = len("execute")
+ }
+ if spec[offset] == ':' {
+ if specIndex == len(specs)-1 {
+ actions = append(actions, action{t: t, a: spec[offset+1:]})
+ } else {
+ prevSpec = spec + "+"
+ continue
+ }
+ } else {
+ actions = append(actions, action{t: t, a: spec[offset+1 : len(spec)-1]})
+ }
}
- } else {
- errorExit("unknown action: " + act)
}
+ prevSpec = ""
}
+ keymap[key] = actions
}
}
-func isExecuteAction(str string) bool {
- if !strings.HasPrefix(str, "execute") || len(str) < len("execute()") {
- return false
+func isExecuteAction(str string) actionType {
+ t := actExecute
+ if !strings.HasPrefix(str, "execute") || len(str) < len("execute(") {
+ return actIgnore
}
+
b := str[len("execute")]
if strings.HasPrefix(str, "execute-multi") {
- if len(str) < len("execute-multi()") {
- return false
+ if len(str) < len("execute-multi(") {
+ return actIgnore
}
+ t = actExecuteMulti
b = str[len("execute-multi")]
}
e := str[len(str)-1]
if b == ':' || b == '(' && e == ')' || b == '[' && e == ']' ||
b == e && strings.ContainsAny(string(b), "~!@#$%^&*;/|") {
- return true
+ return t
}
- return false
+ return actIgnore
}
-func parseToggleSort(keymap map[int]actionType, str string) {
+func parseToggleSort(keymap map[int][]action, str string) {
keys := parseKeyChords(str, "key name required")
if len(keys) != 1 {
errorExit("multiple keys specified")
}
- keymap[firstKey(keys)] = actToggleSort
+ keymap[firstKey(keys)] = toActions(actToggleSort)
}
func strLines(str string) []string {
@@ -919,7 +941,7 @@ func parseOptions(opts *Options, allArgs []string) {
case "--tiebreak":
opts.Criteria = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
case "--bind":
- parseKeymap(opts.Keymap, opts.Execmap, nextString(allArgs, &i, "bind expression required"))
+ parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"))
case "--color":
spec := optionalNextString(allArgs, &i)
if len(spec) == 0 {
@@ -1089,7 +1111,7 @@ func parseOptions(opts *Options, allArgs []string) {
} else if match, value := optString(arg, "--color="); match {
opts.Theme = parseTheme(opts.Theme, value)
} else if match, value := optString(arg, "--bind="); match {
- parseKeymap(opts.Keymap, opts.Execmap, value)
+ parseKeymap(opts.Keymap, value)
} else if match, value := optString(arg, "--history="); match {
setHistory(value)
} else if match, value := optString(arg, "--history-size="); match {
@@ -1145,20 +1167,22 @@ func postProcessOptions(opts *Options) {
// Default actions for CTRL-N / CTRL-P when --history is set
if opts.History != nil {
if _, prs := opts.Keymap[tui.CtrlP]; !prs {
- opts.Keymap[tui.CtrlP] = actPreviousHistory
+ opts.Keymap[tui.CtrlP] = toActions(actPreviousHistory)
}
if _, prs := opts.Keymap[tui.CtrlN]; !prs {
- opts.Keymap[tui.CtrlN] = actNextHistory
+ opts.Keymap[tui.CtrlN] = toActions(actNextHistory)
}
}
// Extend the default key map
keymap := defaultKeymap()
- for key, act := range opts.Keymap {
- if act == actToggleSort {
- opts.ToggleSort = true
+ for key, actions := range opts.Keymap {
+ for _, act := range actions {
+ if act.t == actToggleSort {
+ opts.ToggleSort = true
+ }
}
- keymap[key] = act
+ keymap[key] = actions
}
opts.Keymap = keymap
diff --git a/src/options_test.go b/src/options_test.go
index 07616fcf..29d98421 100644
--- a/src/options_test.go
+++ b/src/options_test.go
@@ -225,49 +225,51 @@ func TestParseKeysWithComma(t *testing.T) {
}
func TestBind(t *testing.T) {
- check := func(action actionType, expected actionType) {
- if action != expected {
- t.Errorf("%d != %d", action, expected)
+ keymap := defaultKeymap()
+ check := func(keyName int, arg1 string, types ...actionType) {
+ if len(keymap[keyName]) != len(types) {
+ t.Errorf("invalid number of actions (%d != %d)", len(types), len(keymap[keyName]))
+ return
}
- }
- checkString := func(action string, expected string) {
- if action != expected {
- t.Errorf("%d != %d", action, expected)
+ for idx, action := range keymap[keyName] {
+ if types[idx] != action.t {
+ t.Errorf("invalid action type (%d != %d)", types[idx], action.t)
+ }
+ }
+ if len(arg1) > 0 && keymap[keyName][0].a != arg1 {
+ t.Errorf("invalid action argument: (%s != %s)", arg1, keymap[keyName][0].a)
}
}
- keymap := defaultKeymap()
- execmap := make(map[int]string)
- check(actBeginningOfLine, keymap[tui.CtrlA])
- parseKeymap(keymap, execmap,
- "ctrl-a:kill-line,ctrl-b:toggle-sort,c:page-up,alt-z:page-down,"+
- "f1:execute(ls {}),f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+
- "alt-a:execute@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};"+
- ",,:abort,::accept,X:execute:\nfoobar,Y:execute(baz)")
- check(actKillLine, keymap[tui.CtrlA])
- check(actToggleSort, keymap[tui.CtrlB])
- check(actPageUp, keymap[tui.AltZ+'c'])
- check(actAbort, keymap[tui.AltZ+','])
- check(actAccept, keymap[tui.AltZ+':'])
- check(actPageDown, keymap[tui.AltZ])
- check(actExecute, keymap[tui.F1])
- check(actExecute, keymap[tui.F2])
- check(actExecute, keymap[tui.F3])
- check(actExecute, keymap[tui.F4])
- checkString("ls {}", execmap[tui.F1])
- checkString("echo {}, {}, {}", execmap[tui.F2])
- checkString("echo '({})'", execmap[tui.F3])
- checkString("less {}", execmap[tui.F4])
- checkString("echo (,),[,],/,:,;,%,{}", execmap[tui.AltA])
- checkString("echo (,),[,],/,:,@,%,{}", execmap[tui.AltB])
- checkString("\nfoobar,Y:execute(baz)", execmap[tui.AltZ+'X'])
+ check(tui.CtrlA, "", actBeginningOfLine)
+ parseKeymap(keymap,
+ "ctrl-a:kill-line,ctrl-b:toggle-sort+up+down,c:page-up,alt-z:page-down,"+
+ "f1:execute(ls {})+abort,f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+
+ "alt-a:execute-Multi@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};,"+
+ "x:Execute(foo+bar),X:execute/bar+baz/"+
+ ",,:abort,::accept,+:execute:++\nfoobar,Y:execute(baz)+up")
+ check(tui.CtrlA, "", actKillLine)
+ check(tui.CtrlB, "", actToggleSort, actUp, actDown)
+ check(tui.AltZ+'c', "", actPageUp)
+ check(tui.AltZ+',', "", actAbort)
+ check(tui.AltZ+':', "", actAccept)
+ check(tui.AltZ, "", actPageDown)
+ check(tui.F1, "ls {}", actExecute, actAbort)
+ check(tui.F2, "echo {}, {}, {}", actExecute)
+ check(tui.F3, "echo '({})'", actExecute)
+ check(tui.F4, "less {}", actExecute)
+ check(tui.AltZ+'x', "foo+bar", actExecute)
+ check(tui.AltZ+'X', "bar+baz", actExecute)
+ check(tui.AltA, "echo (,),[,],/,:,;,%,{}", actExecuteMulti)
+ check(tui.AltB, "echo (,),[,],/,:,@,%,{}", actExecute)
+ check(tui.AltZ+'+', "++\nfoobar,Y:execute(baz)+up", actExecute)
for idx, char := range []rune{'~', '!', '@', '#', '$', '%', '^', '&', '*', '|', ';', '/'} {
- parseKeymap(keymap, execmap, fmt.Sprintf("%d:execute%cfoobar%c", idx%10, char, char))
- checkString("foobar", execmap[tui.AltZ+int([]rune(fmt.Sprintf("%d", idx%10))[0])])
+ parseKeymap(keymap, fmt.Sprintf("%d:execute%cfoobar%c", idx%10, char, char))
+ check(tui.AltZ+int([]rune(fmt.Sprintf("%d", idx%10))[0]), "foobar", actExecute)
}
- parseKeymap(keymap, execmap, "f1:abort")
- check(actAbort, keymap[tui.F1])
+ parseKeymap(keymap, "f1:abort")
+ check(tui.F1, "", actAbort)
}
func TestColorSpec(t *testing.T) {
@@ -327,7 +329,7 @@ func TestDefaultCtrlNP(t *testing.T) {
opts := defaultOptions()
parseOptions(opts, words)
postProcessOptions(opts)
- if opts.Keymap[key] != expected {
+ if opts.Keymap[key][0].t != expected {
t.Error()
}
}
diff --git a/src/terminal.go b/src/terminal.go
index 2378984d..9e30f302 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -72,8 +72,7 @@ type Terminal struct {
toggleSort bool
delimiter Delimiter
expect map[int]string
- keymap map[int]actionType
- execmap map[int]string
+ keymap map[int][]action
pressed string
printQuery bool
history *History
@@ -148,6 +147,11 @@ const (
reqQuit
)
+type action struct {
+ t actionType
+ a string
+}
+
type actionType int
const (
@@ -203,54 +207,62 @@ const (
actExecuteMulti
)
-func defaultKeymap() map[int]actionType {
- keymap := make(map[int]actionType)
- keymap[tui.Invalid] = actInvalid
- keymap[tui.Resize] = actClearScreen
- keymap[tui.CtrlA] = actBeginningOfLine
- keymap[tui.CtrlB] = actBackwardChar
- keymap[tui.CtrlC] = actAbort
- keymap[tui.CtrlG] = actAbort
- keymap[tui.CtrlQ] = actAbort
- keymap[tui.ESC] = actAbort
- keymap[tui.CtrlD] = actDeleteCharEOF
- keymap[tui.CtrlE] = actEndOfLine
- keymap[tui.CtrlF] = actForwardChar
- keymap[tui.CtrlH] = actBackwardDeleteChar
- keymap[tui.BSpace] = actBackwardDeleteChar
- keymap[tui.Tab] = actToggleDown
- keymap[tui.BTab] = actToggleUp
- keymap[tui.CtrlJ] = actDown
- keymap[tui.CtrlK] = actUp
- keymap[tui.CtrlL] = actClearScreen
- keymap[tui.CtrlM] = actAccept
- keymap[tui.CtrlN] = actDown
- keymap[tui.CtrlP] = actUp
- keymap[tui.CtrlU] = actUnixLineDiscard
- keymap[tui.CtrlW] = actUnixWordRubout
- keymap[tui.CtrlY] = actYank
-
- keymap[tui.AltB] = actBackwardWord
- keymap[tui.SLeft] = actBackwardWord
- keymap[tui.AltF] = actForwardWord
- keymap[tui.SRight] = actForwardWord
- keymap[tui.AltD] = actKillWord
- keymap[tui.AltBS] = actBackwardKillWord
-
- keymap[tui.Up] = actUp
- keymap[tui.Down] = actDown
- keymap[tui.Left] = actBackwardChar
- keymap[tui.Right] = actForwardChar
-
- keymap[tui.Home] = actBeginningOfLine
- keymap[tui.End] = actEndOfLine
- keymap[tui.Del] = actDeleteChar
- keymap[tui.PgUp] = actPageUp
- keymap[tui.PgDn] = actPageDown
-
- keymap[tui.Rune] = actRune
- keymap[tui.Mouse] = actMouse
- keymap[tui.DoubleClick] = actAccept
+func toActions(types ...actionType) []action {
+ actions := make([]action, len(types))
+ for idx, t := range types {
+ actions[idx] = action{t: t, a: ""}
+ }
+ return actions
+}
+
+func defaultKeymap() map[int][]action {
+ keymap := make(map[int][]action)
+ keymap[tui.Invalid] = toActions(actInvalid)
+ keymap[tui.Resize] = toActions(actClearScreen)
+ keymap[tui.CtrlA] = toActions(actBeginningOfLine)
+ keymap[tui.CtrlB] = toActions(actBackwardChar)
+ keymap[tui.CtrlC] = toActions(actAbort)
+ keymap[tui.CtrlG] = toActions(actAbort)
+ keymap[tui.CtrlQ] = toActions(actAbort)
+ keymap[tui.ESC] = toActions(actAbort)
+ keymap[tui.CtrlD] = toActions(actDeleteCharEOF)
+ keymap[tui.CtrlE] = toActions(actEndOfLine)
+ keymap[tui.CtrlF] = toActions(actForwardChar)
+ keymap[tui.CtrlH] = toActions(actBackwardDeleteChar)
+ keymap[tui.BSpace] = toActions(actBackwardDeleteChar)
+ keymap[tui.Tab] = toActions(actToggleDown)
+ keymap[tui.BTab] = toActions(actToggleUp)
+ keymap[tui.CtrlJ] = toActions(actDown)
+ keymap[tui.CtrlK] = toActions(actUp)
+ keymap[tui.CtrlL] = toActions(actClearScreen)
+ keymap[tui.CtrlM] = toActions(actAccept)
+ keymap[tui.CtrlN] = toActions(actDown)
+ keymap[tui.CtrlP] = toActions(actUp)
+ keymap[tui.CtrlU] = toActions(actUnixLineDiscard)
+ keymap[tui.CtrlW] = toActions(actUnixWordRubout)
+ keymap[tui.CtrlY] = toActions(actYank)
+
+ keymap[tui.AltB] = toActions(actBackwardWord)
+ keymap[tui.SLeft] = toActions(actBackwardWord)
+ keymap[tui.AltF] = toActions(actForwardWord)
+ keymap[tui.SRight] = toActions(actForwardWord)
+ keymap[tui.AltD] = toActions(actKillWord)
+ keymap[tui.AltBS] = toActions(actBackwardKillWord)
+
+ keymap[tui.Up] = toActions(actUp)
+ keymap[tui.Down] = toActions(actDown)
+ keymap[tui.Left] = toActions(actBackwardChar)
+ keymap[tui.Right] = toActions(actForwardChar)
+
+ keymap[tui.Home] = toActions(actBeginningOfLine)
+ keymap[tui.End] = toActions(actEndOfLine)
+ keymap[tui.Del] = toActions(actDeleteChar)
+ keymap[tui.PgUp] = toActions(actPageUp)
+ keymap[tui.PgDn] = toActions(actPageDown)
+
+ keymap[tui.Rune] = toActions(actRune)
+ keymap[tui.Mouse] = toActions(actMouse)
+ keymap[tui.DoubleClick] = toActions(actAccept)
return keymap
}
@@ -323,7 +335,6 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
delimiter: opts.Delimiter,
expect: opts.Expect,
keymap: opts.Keymap,
- execmap: opts.Execmap,
pressed: "",
printQuery: opts.PrintQuery,
history: opts.History,
@@ -1314,13 +1325,21 @@ func (t *Terminal) Loop() {
}
}
- var doAction func(actionType, int) bool
- doAction = func(action actionType, mapkey int) bool {
- switch action {
+ var doAction func(action, int) bool
+ doActions := func(actions []action, mapkey int) bool {
+ for _, action := range actions {
+ if !doAction(action, mapkey) {
+ return false
+ }
+ }
+ return true
+ }
+ doAction = func(a action, mapkey int) bool {
+ switch a.t {
case actIgnore:
case actExecute:
if t.cy >= 0 && t.cy < t.merger.Length() {
- t.executeCommand(t.execmap[mapkey], []*Item{t.currentItem()})
+ t.executeCommand(a.a, []*Item{t.currentItem()})
}
case actExecuteMulti:
if len(t.selected) > 0 {
@@ -1328,9 +1347,9 @@ func (t *Terminal) Loop() {
for i, sel := range t.sortSelected() {
sels[i] = sel.item
}
- t.executeCommand(t.execmap[mapkey], sels)
+ t.executeCommand(a.a, sels)
} else {
- return doAction(actExecute, mapkey)
+ return doAction(action{t: actExecute, a: a.a}, mapkey)
}
case actInvalid:
t.mutex.Unlock()
@@ -1431,14 +1450,14 @@ func (t *Terminal) Loop() {
}
case actToggleIn:
if t.reverse {
- return doAction(actToggleUp, mapkey)
+ return doAction(action{t: actToggleUp}, mapkey)
}
- return doAction(actToggleDown, mapkey)
+ return doAction(action{t: actToggleDown}, mapkey)
case actToggleOut:
if t.reverse {
- return doAction(actToggleDown, mapkey)
+ return doAction(action{t: actToggleDown}, mapkey)
}
- return doAction(actToggleUp, mapkey)
+ return doAction(action{t: actToggleUp}, mapkey)
case actToggleDown:
if t.multi && t.merger.Length() > 0 {
toggle()
@@ -1558,7 +1577,7 @@ func (t *Terminal) Loop() {
// Double-click
if my >= min {
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
- return doAction(t.keymap[tui.DoubleClick], tui.DoubleClick)
+ return doActions(t.keymap[tui.DoubleClick], tui.DoubleClick)
}
}
} else if me.Down {
@@ -1580,14 +1599,14 @@ func (t *Terminal) Loop() {
changed := false
mapkey := event.Type
if t.jumping == jumpDisabled {
- action := t.keymap[mapkey]
+ actions := t.keymap[mapkey]
if mapkey == tui.Rune {
mapkey = int(event.Char) + int(tui.AltZ)
if act, prs := t.keymap[mapkey]; prs {
- action = act
+ actions = act
}
}
- if !doAction(action, mapkey) {
+ if !doActions(actions, mapkey) {
continue
}
// Truncate the query if it's too long