diff options
| author | Junegunn Choi <junegunn.c@gmail.com> | 2017-01-22 02:32:49 +0900 |
|---|---|---|
| committer | Junegunn Choi <junegunn.c@gmail.com> | 2017-01-22 02:32:49 +0900 |
| commit | 131aa5dd15788875e796bbd2f2bfdf644584284a (patch) | |
| tree | 2c396a98656031dbe59d12df511523fb0c294a78 /src/options.go | |
| parent | a06ccc928f46e9c095680bc67b9f65c61f323e23 (diff) | |
| download | fzf-131aa5dd15788875e796bbd2f2bfdf644584284a.tar.gz | |
Composable actions in --bind
Close #816
Diffstat (limited to 'src/options.go')
| -rw-r--r-- | src/options.go | 288 |
1 files changed, 156 insertions, 132 deletions
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 |
