|
|
|
@ -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
|
|
|
|
|
|
|
|
|
|