2015-01-01 19:49:30 +00:00
|
|
|
package fzf
|
|
|
|
|
|
|
|
import (
|
2016-06-11 10:59:12 +00:00
|
|
|
"fmt"
|
2015-01-01 19:49:30 +00:00
|
|
|
"os"
|
|
|
|
"regexp"
|
2015-05-31 07:46:54 +00:00
|
|
|
"strconv"
|
2015-01-01 19:49:30 +00:00
|
|
|
"strings"
|
2020-02-17 01:19:03 +00:00
|
|
|
"unicode"
|
2015-03-28 17:59:32 +00:00
|
|
|
|
2016-09-07 00:58:18 +00:00
|
|
|
"github.com/junegunn/fzf/src/algo"
|
2016-10-24 00:44:56 +00:00
|
|
|
"github.com/junegunn/fzf/src/tui"
|
2023-02-11 11:21:10 +00:00
|
|
|
"github.com/junegunn/fzf/src/util"
|
2015-01-12 03:56:17 +00:00
|
|
|
|
2020-02-17 01:19:03 +00:00
|
|
|
"github.com/mattn/go-runewidth"
|
2017-06-01 23:27:17 +00:00
|
|
|
"github.com/mattn/go-shellwords"
|
2015-01-01 19:49:30 +00:00
|
|
|
)
|
|
|
|
|
2015-01-11 18:01:24 +00:00
|
|
|
const usage = `usage: fzf [options]
|
2015-01-01 19:49:30 +00:00
|
|
|
|
2015-06-08 14:28:41 +00:00
|
|
|
Search
|
2022-10-29 15:12:01 +00:00
|
|
|
-x, --extended Extended-search mode
|
|
|
|
(enabled by default; +x or --no-extended to disable)
|
|
|
|
-e, --exact Enable Exact-match
|
|
|
|
-i Case-insensitive match (default: smart-case match)
|
|
|
|
+i Case-sensitive match
|
|
|
|
--scheme=SCHEME Scoring scheme [default|path|history]
|
|
|
|
--literal Do not normalize latin script letters before matching
|
|
|
|
-n, --nth=N[,..] Comma-separated list of field index expressions
|
|
|
|
for limiting search scope. Each can be a non-zero
|
|
|
|
integer or a range expression ([BEGIN]..[END]).
|
|
|
|
--with-nth=N[,..] Transform the presentation of each line using
|
|
|
|
field index expressions
|
|
|
|
-d, --delimiter=STR Field delimiter regex (default: AWK-style)
|
|
|
|
+s, --no-sort Do not sort the result
|
2023-03-29 11:36:09 +00:00
|
|
|
--track Track the current selection when the result is updated
|
2022-10-29 15:12:01 +00:00
|
|
|
--tac Reverse the order of the input
|
|
|
|
--disabled Do not perform search
|
|
|
|
--tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
|
|
|
|
when the scores are tied [length|chunk|begin|end|index]
|
|
|
|
(default: length)
|
2015-01-01 19:49:30 +00:00
|
|
|
|
|
|
|
Interface
|
2022-10-29 15:12:01 +00:00
|
|
|
-m, --multi[=MAX] Enable multi-select with tab/shift-tab
|
|
|
|
--no-mouse Disable mouse
|
|
|
|
--bind=KEYBINDS Custom key bindings. Refer to the man page.
|
|
|
|
--cycle Enable cyclic scroll
|
|
|
|
--keep-right Keep the right end of the line visible on overflow
|
|
|
|
--scroll-off=LINES Number of screen lines to keep above or below when
|
|
|
|
scrolling to the top or to the bottom (default: 0)
|
|
|
|
--no-hscroll Disable horizontal scroll
|
|
|
|
--hscroll-off=COLS Number of screen columns to keep to the right of the
|
|
|
|
highlighted substring (default: 10)
|
|
|
|
--filepath-word Make word-wise movements respect path separators
|
|
|
|
--jump-labels=CHARS Label characters for jump and jump-accept
|
2016-06-11 10:59:12 +00:00
|
|
|
|
|
|
|
Layout
|
2022-10-29 15:12:01 +00:00
|
|
|
--height=[~]HEIGHT[%] Display fzf window below the cursor with the given
|
|
|
|
height instead of using fullscreen.
|
|
|
|
If prefixed with '~', fzf will determine the height
|
|
|
|
according to the input size.
|
|
|
|
--min-height=HEIGHT Minimum height when --height is given in percent
|
|
|
|
(default: 10)
|
|
|
|
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
|
|
|
--border[=STYLE] Draw border around the finder
|
2023-06-10 05:48:29 +00:00
|
|
|
[rounded|sharp|bold|block|thinblock|double|horizontal|vertical|
|
2022-10-29 15:12:01 +00:00
|
|
|
top|bottom|left|right|none] (default: rounded)
|
|
|
|
--border-label=LABEL Label to print on the border
|
|
|
|
--border-label-pos=COL Position of the border label
|
|
|
|
[POSITIVE_INTEGER: columns from left|
|
2022-10-30 15:22:41 +00:00
|
|
|
NEGATIVE_INTEGER: columns from right][:bottom]
|
|
|
|
(default: 0 or center)
|
2022-10-29 15:12:01 +00:00
|
|
|
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
|
|
|
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
2023-06-10 15:04:24 +00:00
|
|
|
--info=STYLE Finder info style
|
|
|
|
[default|right|hidden|inline[:SEPARATOR]|inline-right]
|
2022-11-10 07:23:33 +00:00
|
|
|
--separator=STR String to form horizontal separator on info line
|
|
|
|
--no-separator Hide info line separator
|
2023-05-16 14:53:10 +00:00
|
|
|
--scrollbar[=C1[C2]] Scrollbar character(s) (each for main and preview window)
|
2023-01-01 05:48:14 +00:00
|
|
|
--no-scrollbar Hide scrollbar
|
2022-10-29 15:12:01 +00:00
|
|
|
--prompt=STR Input prompt (default: '> ')
|
|
|
|
--pointer=STR Pointer to the current line (default: '>')
|
|
|
|
--marker=STR Multi-select marker (default: '>')
|
|
|
|
--header=STR String to print as header
|
|
|
|
--header-lines=N The first N lines of the input are treated as header
|
|
|
|
--header-first Print header before the prompt line
|
|
|
|
--ellipsis=STR Ellipsis to show when line is truncated (default: '..')
|
2015-01-01 19:49:30 +00:00
|
|
|
|
2016-06-11 10:59:12 +00:00
|
|
|
Display
|
2022-10-29 15:12:01 +00:00
|
|
|
--ansi Enable processing of ANSI color codes
|
|
|
|
--tabstop=SPACES Number of spaces for a tab character (default: 8)
|
|
|
|
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
|
|
|
|
--no-bold Do not use bold text
|
2016-06-11 10:59:12 +00:00
|
|
|
|
|
|
|
History
|
2022-10-29 15:12:01 +00:00
|
|
|
--history=FILE History file
|
|
|
|
--history-size=N Maximum number of history entries (default: 1000)
|
2016-06-11 10:59:12 +00:00
|
|
|
|
|
|
|
Preview
|
2022-10-29 15:12:01 +00:00
|
|
|
--preview=COMMAND Command to preview highlighted line ({})
|
|
|
|
--preview-window=OPT Preview window layout (default: right:50%)
|
|
|
|
[up|down|left|right][,SIZE[%]]
|
|
|
|
[,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
|
|
|
|
[,border-BORDER_OPT]
|
|
|
|
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
|
|
|
|
[,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
|
2022-10-30 11:44:46 +00:00
|
|
|
--preview-label=LABEL
|
2022-10-30 15:22:41 +00:00
|
|
|
--preview-label-pos=N Same as --border-label and --border-label-pos,
|
|
|
|
but for preview window
|
2016-06-11 10:59:12 +00:00
|
|
|
|
2015-01-01 19:49:30 +00:00
|
|
|
Scripting
|
2022-10-29 15:12:01 +00:00
|
|
|
-q, --query=STR Start the finder with the given query
|
|
|
|
-1, --select-1 Automatically select the only match
|
|
|
|
-0, --exit-0 Exit immediately when there's no match
|
|
|
|
-f, --filter=STR Filter mode. Do not start interactive finder.
|
|
|
|
--print-query Print query as the first line
|
|
|
|
--expect=KEYS Comma-separated list of keys to complete fzf
|
|
|
|
--read0 Read input delimited by ASCII NUL characters
|
|
|
|
--print0 Print output delimited by ASCII NUL characters
|
|
|
|
--sync Synchronous search for multi-staged filtering
|
2023-03-19 06:42:47 +00:00
|
|
|
--listen[=HTTP_PORT] Start HTTP server to receive actions (POST /)
|
2022-10-29 15:12:01 +00:00
|
|
|
--version Display version information and exit
|
2015-01-01 19:49:30 +00:00
|
|
|
|
|
|
|
Environment variables
|
2022-10-29 15:12:01 +00:00
|
|
|
FZF_DEFAULT_COMMAND Default command to use when input is tty
|
|
|
|
FZF_DEFAULT_OPTS Default options
|
|
|
|
(e.g. '--layout=reverse --inline-info')
|
2023-07-20 14:42:09 +00:00
|
|
|
FZF_API_KEY X-API-Key header for HTTP server (--listen)
|
2015-01-01 19:49:30 +00:00
|
|
|
|
|
|
|
`
|
|
|
|
|
2023-01-24 08:40:08 +00:00
|
|
|
const defaultInfoSep = " < "
|
|
|
|
|
2015-01-11 18:01:24 +00:00
|
|
|
// Case denotes case-sensitivity of search
|
2015-01-01 19:49:30 +00:00
|
|
|
type Case int
|
|
|
|
|
2015-01-11 18:01:24 +00:00
|
|
|
// Case-sensitivities
|
2015-01-01 19:49:30 +00:00
|
|
|
const (
|
2015-01-11 18:01:24 +00:00
|
|
|
CaseSmart Case = iota
|
|
|
|
CaseIgnore
|
|
|
|
CaseRespect
|
2015-01-01 19:49:30 +00:00
|
|
|
)
|
|
|
|
|
2015-04-16 05:19:28 +00:00
|
|
|
// Sort criteria
|
2016-01-12 18:07:42 +00:00
|
|
|
type criterion int
|
2015-04-16 05:19:28 +00:00
|
|
|
|
|
|
|
const (
|
2016-09-07 00:58:18 +00:00
|
|
|
byScore criterion = iota
|
2022-08-02 04:44:55 +00:00
|
|
|
byChunk
|
2016-01-12 18:07:42 +00:00
|
|
|
byLength
|
2015-04-16 05:19:28 +00:00
|
|
|
byBegin
|
|
|
|
byEnd
|
|
|
|
)
|
|
|
|
|
2022-09-07 16:01:22 +00:00
|
|
|
type heightSpec struct {
|
|
|
|
size float64
|
|
|
|
percent bool
|
|
|
|
auto bool
|
|
|
|
}
|
|
|
|
|
2016-06-11 10:59:12 +00:00
|
|
|
type sizeSpec struct {
|
|
|
|
size float64
|
|
|
|
percent bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func defaultMargin() [4]sizeSpec {
|
|
|
|
return [4]sizeSpec{}
|
|
|
|
}
|
|
|
|
|
2023-04-22 14:39:35 +00:00
|
|
|
type trackOption int
|
|
|
|
|
|
|
|
const (
|
|
|
|
trackDisabled trackOption = iota
|
|
|
|
trackEnabled
|
|
|
|
trackCurrent
|
|
|
|
)
|
|
|
|
|
2016-06-11 10:59:12 +00:00
|
|
|
type windowPosition int
|
|
|
|
|
|
|
|
const (
|
|
|
|
posUp windowPosition = iota
|
|
|
|
posDown
|
|
|
|
posLeft
|
|
|
|
posRight
|
|
|
|
)
|
|
|
|
|
2018-06-09 16:41:50 +00:00
|
|
|
type layoutType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
layoutDefault layoutType = iota
|
|
|
|
layoutReverse
|
|
|
|
layoutReverseList
|
|
|
|
)
|
|
|
|
|
2022-11-10 07:23:33 +00:00
|
|
|
type infoStyle int
|
2019-11-14 15:39:29 +00:00
|
|
|
|
|
|
|
const (
|
2022-11-10 07:23:33 +00:00
|
|
|
infoDefault infoStyle = iota
|
2023-06-10 15:04:24 +00:00
|
|
|
infoRight
|
2019-11-14 15:39:29 +00:00
|
|
|
infoInline
|
2023-06-10 14:11:05 +00:00
|
|
|
infoInlineRight
|
2019-11-14 15:39:29 +00:00
|
|
|
infoHidden
|
|
|
|
)
|
|
|
|
|
2023-06-10 15:04:24 +00:00
|
|
|
func (s infoStyle) noExtraLine() bool {
|
|
|
|
return s == infoInline || s == infoInlineRight || s == infoHidden
|
|
|
|
}
|
|
|
|
|
2022-10-30 15:22:41 +00:00
|
|
|
type labelOpts struct {
|
|
|
|
label string
|
|
|
|
column int
|
|
|
|
bottom bool
|
|
|
|
}
|
|
|
|
|
2016-06-11 10:59:12 +00:00
|
|
|
type previewOpts struct {
|
2021-03-12 10:51:28 +00:00
|
|
|
command string
|
|
|
|
position windowPosition
|
|
|
|
size sizeSpec
|
|
|
|
scroll string
|
|
|
|
hidden bool
|
|
|
|
wrap bool
|
2023-10-22 16:01:47 +00:00
|
|
|
clear bool
|
2021-03-12 10:51:28 +00:00
|
|
|
cycle bool
|
|
|
|
follow bool
|
|
|
|
border tui.BorderShape
|
|
|
|
headerLines int
|
2022-07-20 03:08:54 +00:00
|
|
|
threshold int
|
|
|
|
alternative *previewOpts
|
2015-07-26 14:02:04 +00:00
|
|
|
}
|
|
|
|
|
2023-01-07 02:21:52 +00:00
|
|
|
func (o *previewOpts) Visible() bool {
|
|
|
|
return o.size.size > 0 || o.alternative != nil && o.alternative.size.size > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *previewOpts) Toggle() {
|
|
|
|
o.hidden = !o.hidden
|
|
|
|
}
|
|
|
|
|
2022-10-30 15:22:41 +00:00
|
|
|
func parseLabelPosition(opts *labelOpts, arg string) {
|
|
|
|
opts.column = 0
|
|
|
|
opts.bottom = false
|
|
|
|
for _, token := range splitRegexp.Split(strings.ToLower(arg), -1) {
|
|
|
|
switch token {
|
|
|
|
case "center":
|
|
|
|
opts.column = 0
|
|
|
|
case "bottom":
|
|
|
|
opts.bottom = true
|
|
|
|
case "top":
|
|
|
|
opts.bottom = false
|
|
|
|
default:
|
|
|
|
opts.column = atoi(token)
|
|
|
|
}
|
2022-10-29 15:12:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-07 16:01:22 +00:00
|
|
|
func (a previewOpts) aboveOrBelow() bool {
|
|
|
|
return a.size.size > 0 && (a.position == posUp || a.position == posDown)
|
|
|
|
}
|
|
|
|
|
2021-11-30 14:37:48 +00:00
|
|
|
func (a previewOpts) sameLayout(b previewOpts) bool {
|
2022-07-20 03:08:54 +00:00
|
|
|
return a.size == b.size && a.position == b.position && a.border == b.border && a.hidden == b.hidden && a.threshold == b.threshold &&
|
|
|
|
(a.alternative != nil && b.alternative != nil && a.alternative.sameLayout(*b.alternative) ||
|
|
|
|
a.alternative == nil && b.alternative == nil)
|
2021-11-30 14:37:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a previewOpts) sameContentLayout(b previewOpts) bool {
|
|
|
|
return a.wrap == b.wrap && a.headerLines == b.headerLines
|
|
|
|
}
|
|
|
|
|
2023-01-24 10:33:14 +00:00
|
|
|
func firstLine(s string) string {
|
|
|
|
return strings.SplitN(s, "\n", 2)[0]
|
|
|
|
}
|
|
|
|
|
2015-01-11 18:01:24 +00:00
|
|
|
// Options stores the values of command-line options
|
2015-01-01 19:49:30 +00:00
|
|
|
type Options struct {
|
2022-10-30 15:22:41 +00:00
|
|
|
Fuzzy bool
|
|
|
|
FuzzyAlgo algo.Algo
|
|
|
|
Scheme string
|
|
|
|
Extended bool
|
|
|
|
Phony bool
|
|
|
|
Case Case
|
|
|
|
Normalize bool
|
|
|
|
Nth []Range
|
|
|
|
WithNth []Range
|
|
|
|
Delimiter Delimiter
|
|
|
|
Sort int
|
2023-04-22 14:39:35 +00:00
|
|
|
Track trackOption
|
2022-10-30 15:22:41 +00:00
|
|
|
Tac bool
|
|
|
|
Criteria []criterion
|
|
|
|
Multi int
|
|
|
|
Ansi bool
|
|
|
|
Mouse bool
|
|
|
|
Theme *tui.ColorTheme
|
|
|
|
Black bool
|
|
|
|
Bold bool
|
|
|
|
Height heightSpec
|
|
|
|
MinHeight int
|
|
|
|
Layout layoutType
|
|
|
|
Cycle bool
|
|
|
|
KeepRight bool
|
|
|
|
Hscroll bool
|
|
|
|
HscrollOff int
|
|
|
|
ScrollOff int
|
|
|
|
FileWord bool
|
|
|
|
InfoStyle infoStyle
|
2023-01-24 08:40:08 +00:00
|
|
|
InfoSep string
|
2022-11-10 07:23:33 +00:00
|
|
|
Separator *string
|
2022-10-30 15:22:41 +00:00
|
|
|
JumpLabels string
|
|
|
|
Prompt string
|
|
|
|
Pointer string
|
|
|
|
Marker string
|
|
|
|
Query string
|
|
|
|
Select1 bool
|
|
|
|
Exit0 bool
|
|
|
|
Filter *string
|
|
|
|
ToggleSort bool
|
|
|
|
Expect map[tui.Event]string
|
|
|
|
Keymap map[tui.Event][]*action
|
|
|
|
Preview previewOpts
|
|
|
|
PrintQuery bool
|
|
|
|
ReadZero bool
|
|
|
|
Printer func(string)
|
|
|
|
PrintSep string
|
|
|
|
Sync bool
|
|
|
|
History *History
|
|
|
|
Header []string
|
|
|
|
HeaderLines int
|
|
|
|
HeaderFirst bool
|
|
|
|
Ellipsis string
|
2023-01-01 05:48:14 +00:00
|
|
|
Scrollbar *string
|
2022-10-30 15:22:41 +00:00
|
|
|
Margin [4]sizeSpec
|
|
|
|
Padding [4]sizeSpec
|
|
|
|
BorderShape tui.BorderShape
|
|
|
|
BorderLabel labelOpts
|
|
|
|
PreviewLabel labelOpts
|
|
|
|
Unicode bool
|
|
|
|
Tabstop int
|
2023-03-19 06:42:47 +00:00
|
|
|
ListenPort *int
|
2022-10-30 15:22:41 +00:00
|
|
|
ClearOnExit bool
|
|
|
|
Version bool
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
2020-10-10 16:51:39 +00:00
|
|
|
func defaultPreviewOpts(command string) previewOpts {
|
2023-10-22 16:01:47 +00:00
|
|
|
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, false, tui.DefaultBorderShape, 0, 0, nil}
|
2020-10-10 16:51:39 +00:00
|
|
|
}
|
|
|
|
|
2015-05-31 07:46:54 +00:00
|
|
|
func defaultOptions() *Options {
|
2015-01-01 19:49:30 +00:00
|
|
|
return &Options{
|
2022-10-30 15:22:41 +00:00
|
|
|
Fuzzy: true,
|
|
|
|
FuzzyAlgo: algo.FuzzyMatchV2,
|
|
|
|
Scheme: "default",
|
|
|
|
Extended: true,
|
|
|
|
Phony: false,
|
|
|
|
Case: CaseSmart,
|
|
|
|
Normalize: true,
|
|
|
|
Nth: make([]Range, 0),
|
|
|
|
WithNth: make([]Range, 0),
|
|
|
|
Delimiter: Delimiter{},
|
|
|
|
Sort: 1000,
|
2023-04-22 14:39:35 +00:00
|
|
|
Track: trackDisabled,
|
2022-10-30 15:22:41 +00:00
|
|
|
Tac: false,
|
|
|
|
Criteria: []criterion{byScore, byLength},
|
|
|
|
Multi: 0,
|
|
|
|
Ansi: false,
|
|
|
|
Mouse: true,
|
|
|
|
Theme: tui.EmptyTheme(),
|
|
|
|
Black: false,
|
|
|
|
Bold: true,
|
|
|
|
MinHeight: 10,
|
|
|
|
Layout: layoutDefault,
|
|
|
|
Cycle: false,
|
|
|
|
KeepRight: false,
|
|
|
|
Hscroll: true,
|
|
|
|
HscrollOff: 10,
|
|
|
|
ScrollOff: 0,
|
|
|
|
FileWord: false,
|
2022-11-10 07:23:33 +00:00
|
|
|
InfoStyle: infoDefault,
|
|
|
|
Separator: nil,
|
2022-10-30 15:22:41 +00:00
|
|
|
JumpLabels: defaultJumpLabels,
|
|
|
|
Prompt: "> ",
|
|
|
|
Pointer: ">",
|
|
|
|
Marker: ">",
|
|
|
|
Query: "",
|
|
|
|
Select1: false,
|
|
|
|
Exit0: false,
|
|
|
|
Filter: nil,
|
|
|
|
ToggleSort: false,
|
|
|
|
Expect: make(map[tui.Event]string),
|
|
|
|
Keymap: make(map[tui.Event][]*action),
|
|
|
|
Preview: defaultPreviewOpts(""),
|
|
|
|
PrintQuery: false,
|
|
|
|
ReadZero: false,
|
|
|
|
Printer: func(str string) { fmt.Println(str) },
|
|
|
|
PrintSep: "\n",
|
|
|
|
Sync: false,
|
|
|
|
History: nil,
|
|
|
|
Header: make([]string, 0),
|
|
|
|
HeaderLines: 0,
|
|
|
|
HeaderFirst: false,
|
|
|
|
Ellipsis: "..",
|
2023-01-01 05:48:14 +00:00
|
|
|
Scrollbar: nil,
|
2022-10-30 15:22:41 +00:00
|
|
|
Margin: defaultMargin(),
|
|
|
|
Padding: defaultMargin(),
|
|
|
|
Unicode: true,
|
|
|
|
Tabstop: 8,
|
|
|
|
BorderLabel: labelOpts{},
|
|
|
|
PreviewLabel: labelOpts{},
|
|
|
|
ClearOnExit: true,
|
|
|
|
Version: false}
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
2015-09-15 04:21:51 +00:00
|
|
|
func help(code int) {
|
2019-04-21 09:02:34 +00:00
|
|
|
os.Stdout.WriteString(usage)
|
2015-09-15 04:21:51 +00:00
|
|
|
os.Exit(code)
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func errorExit(msg string) {
|
|
|
|
os.Stderr.WriteString(msg + "\n")
|
2015-09-15 04:21:51 +00:00
|
|
|
os.Exit(exitError)
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
2015-06-14 02:23:07 +00:00
|
|
|
func optString(arg string, prefixes ...string) (bool, string) {
|
|
|
|
for _, prefix := range prefixes {
|
|
|
|
if strings.HasPrefix(arg, prefix) {
|
|
|
|
return true, arg[len(prefix):]
|
|
|
|
}
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
2015-01-11 18:01:24 +00:00
|
|
|
return false, ""
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func nextString(args []string, i *int, message string) string {
|
|
|
|
if len(args) > *i+1 {
|
|
|
|
*i++
|
|
|
|
} else {
|
|
|
|
errorExit(message)
|
|
|
|
}
|
|
|
|
return args[*i]
|
|
|
|
}
|
|
|
|
|
2020-03-05 11:15:15 +00:00
|
|
|
func optionalNextString(args []string, i *int) (bool, string) {
|
|
|
|
if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") && !strings.HasPrefix(args[*i+1], "+") {
|
2015-05-31 07:46:54 +00:00
|
|
|
*i++
|
2020-03-05 11:15:15 +00:00
|
|
|
return true, args[*i]
|
2015-05-31 07:46:54 +00:00
|
|
|
}
|
2020-03-05 11:15:15 +00:00
|
|
|
return false, ""
|
2015-05-31 07:46:54 +00:00
|
|
|
}
|
|
|
|
|
2015-06-13 15:43:44 +00:00
|
|
|
func atoi(str string) int {
|
|
|
|
num, err := strconv.Atoi(str)
|
|
|
|
if err != nil {
|
|
|
|
errorExit("not a valid integer: " + str)
|
|
|
|
}
|
|
|
|
return num
|
|
|
|
}
|
|
|
|
|
2015-07-26 14:02:04 +00:00
|
|
|
func atof(str string) float64 {
|
|
|
|
num, err := strconv.ParseFloat(str, 64)
|
|
|
|
if err != nil {
|
|
|
|
errorExit("not a valid number: " + str)
|
|
|
|
}
|
|
|
|
return num
|
|
|
|
}
|
|
|
|
|
2015-06-13 15:43:44 +00:00
|
|
|
func nextInt(args []string, i *int, message string) int {
|
|
|
|
if len(args) > *i+1 {
|
|
|
|
*i++
|
|
|
|
} else {
|
|
|
|
errorExit(message)
|
|
|
|
}
|
|
|
|
return atoi(args[*i])
|
|
|
|
}
|
|
|
|
|
2019-11-02 03:55:26 +00:00
|
|
|
func optionalNumeric(args []string, i *int, defaultValue int) int {
|
2015-01-01 19:49:30 +00:00
|
|
|
if len(args) > *i+1 {
|
|
|
|
if strings.IndexAny(args[*i+1], "0123456789") == 0 {
|
|
|
|
*i++
|
2019-11-02 03:55:26 +00:00
|
|
|
return atoi(args[*i])
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-02 03:55:26 +00:00
|
|
|
return defaultValue
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func splitNth(str string) []Range {
|
|
|
|
if match, _ := regexp.MatchString("^[0-9,-.]+$", str); !match {
|
|
|
|
errorExit("invalid format: " + str)
|
|
|
|
}
|
|
|
|
|
|
|
|
tokens := strings.Split(str, ",")
|
|
|
|
ranges := make([]Range, len(tokens))
|
|
|
|
for idx, s := range tokens {
|
|
|
|
r, ok := ParseRange(&s)
|
|
|
|
if !ok {
|
|
|
|
errorExit("invalid format: " + str)
|
|
|
|
}
|
|
|
|
ranges[idx] = r
|
|
|
|
}
|
|
|
|
return ranges
|
|
|
|
}
|
|
|
|
|
2015-08-10 09:34:20 +00:00
|
|
|
func delimiterRegexp(str string) Delimiter {
|
|
|
|
// Special handling of \t
|
|
|
|
str = strings.Replace(str, "\\t", "\t", -1)
|
|
|
|
|
|
|
|
// 1. Pattern does not contain any special character
|
|
|
|
if regexp.QuoteMeta(str) == str {
|
|
|
|
return Delimiter{str: &str}
|
|
|
|
}
|
|
|
|
|
2015-01-01 19:49:30 +00:00
|
|
|
rx, e := regexp.Compile(str)
|
2015-08-10 09:34:20 +00:00
|
|
|
// 2. Pattern is not a valid regular expression
|
2015-01-01 19:49:30 +00:00
|
|
|
if e != nil {
|
2015-08-10 09:34:20 +00:00
|
|
|
return Delimiter{str: &str}
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
2015-08-10 09:34:20 +00:00
|
|
|
// 3. Pattern as regular expression. Slow.
|
|
|
|
return Delimiter{regex: rx}
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 17:59:32 +00:00
|
|
|
func isAlphabet(char uint8) bool {
|
|
|
|
return char >= 'a' && char <= 'z'
|
|
|
|
}
|
|
|
|
|
2016-11-19 13:40:28 +00:00
|
|
|
func isNumeric(char uint8) bool {
|
|
|
|
return char >= '0' && char <= '9'
|
|
|
|
}
|
|
|
|
|
2016-09-07 00:58:18 +00:00
|
|
|
func parseAlgo(str string) algo.Algo {
|
|
|
|
switch str {
|
|
|
|
case "v1":
|
|
|
|
return algo.FuzzyMatchV1
|
|
|
|
case "v2":
|
|
|
|
return algo.FuzzyMatchV2
|
|
|
|
default:
|
|
|
|
errorExit("invalid algorithm (expected: v1 or v2)")
|
|
|
|
}
|
|
|
|
return algo.FuzzyMatchV2
|
|
|
|
}
|
|
|
|
|
2022-08-28 13:16:57 +00:00
|
|
|
func processScheme(opts *Options) {
|
|
|
|
if !algo.Init(opts.Scheme) {
|
|
|
|
errorExit("invalid scoring scheme (expected: default|path|history)")
|
|
|
|
}
|
|
|
|
if opts.Scheme == "history" {
|
|
|
|
opts.Criteria = []criterion{byScore}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 11:15:15 +00:00
|
|
|
func parseBorder(str string, optional bool) tui.BorderShape {
|
|
|
|
switch str {
|
|
|
|
case "rounded":
|
|
|
|
return tui.BorderRounded
|
|
|
|
case "sharp":
|
|
|
|
return tui.BorderSharp
|
2022-11-06 05:38:31 +00:00
|
|
|
case "bold":
|
|
|
|
return tui.BorderBold
|
2023-05-16 14:45:31 +00:00
|
|
|
case "block":
|
|
|
|
return tui.BorderBlock
|
2023-06-10 05:48:29 +00:00
|
|
|
case "thinblock":
|
|
|
|
return tui.BorderThinBlock
|
2022-11-06 05:38:31 +00:00
|
|
|
case "double":
|
|
|
|
return tui.BorderDouble
|
2020-03-05 11:15:15 +00:00
|
|
|
case "horizontal":
|
|
|
|
return tui.BorderHorizontal
|
2020-10-26 13:33:41 +00:00
|
|
|
case "vertical":
|
|
|
|
return tui.BorderVertical
|
|
|
|
case "top":
|
|
|
|
return tui.BorderTop
|
|
|
|
case "bottom":
|
|
|
|
return tui.BorderBottom
|
|
|
|
case "left":
|
|
|
|
return tui.BorderLeft
|
|
|
|
case "right":
|
|
|
|
return tui.BorderRight
|
2021-04-06 08:37:11 +00:00
|
|
|
case "none":
|
|
|
|
return tui.BorderNone
|
2020-03-05 11:15:15 +00:00
|
|
|
default:
|
|
|
|
if optional && str == "" {
|
2023-01-15 16:22:02 +00:00
|
|
|
return tui.DefaultBorderShape
|
2020-03-05 11:15:15 +00:00
|
|
|
}
|
2023-06-10 05:48:29 +00:00
|
|
|
errorExit("invalid border style (expected: rounded|sharp|bold|block|thinblock|double|horizontal|vertical|top|bottom|left|right|none)")
|
2020-03-05 11:15:15 +00:00
|
|
|
}
|
|
|
|
return tui.BorderNone
|
|
|
|
}
|
|
|
|
|
2020-12-29 16:59:18 +00:00
|
|
|
func parseKeyChords(str string, message string) map[tui.Event]string {
|
2023-01-07 06:35:29 +00:00
|
|
|
return parseKeyChordsImpl(str, message, errorExit)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.Event]string {
|
2015-03-31 13:05:02 +00:00
|
|
|
if len(str) == 0 {
|
2023-01-07 06:35:29 +00:00
|
|
|
exit(message)
|
|
|
|
return nil
|
2015-03-31 13:05:02 +00:00
|
|
|
}
|
|
|
|
|
2021-01-03 04:25:43 +00:00
|
|
|
str = regexp.MustCompile("(?i)(alt-),").ReplaceAllString(str, "$1"+string([]rune{escapedComma}))
|
2015-03-31 13:05:02 +00:00
|
|
|
tokens := strings.Split(str, ",")
|
Improvements to code quality and readability (#1737)
* Remove 1 unused field and 3 unused functions
unused elements fount by running
golangci-lint run --disable-all --enable unused
src/result.go:19:2: field `index` is unused (unused)
index int32
^
src/tui/light.go:716:23: func `(*LightWindow).stderr` is unused (unused)
func (w *LightWindow) stderr(str string) {
^
src/terminal.go:1015:6: func `numLinesMax` is unused (unused)
func numLinesMax(str string, max int) int {
^
src/tui/tui.go:167:20: func `ColorPair.is24` is unused (unused)
func (p ColorPair) is24() bool {
^
* Address warnings from "gosimple" linter
src/options.go:389:83: S1003: should use strings.Contains(str, ",,,") instead (gosimple)
if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Index(str, ",,,") >= 0 {
^
src/options.go:630:18: S1007: should use raw string (`...`) with regexp.MustCompile to avoid having to escape twice (gosimple)
executeRegexp = regexp.MustCompile(
^
src/terminal.go:29:16: S1007: should use raw string (`...`) with regexp.MustCompile to avoid having to escape twice (gosimple)
placeholder = regexp.MustCompile("\\\\?(?:{[+sf]*[0-9,-.]*}|{q}|{\\+?f?nf?})")
^
src/terminal_test.go:92:10: S1007: should use raw string (`...`) with regexp.MustCompile to avoid having to escape twice (gosimple)
regex = regexp.MustCompile("\\w+")
^
* Address warnings from "staticcheck" linter
src/algo/algo.go:374:2: SA4006: this value of `offset32` is never used (staticcheck)
offset32, T := alloc32(offset32, slab, N)
^
src/algo/algo.go:456:2: SA4006: this value of `offset16` is never used (staticcheck)
offset16, C := alloc16(offset16, slab, width*M)
^
src/tui/tui.go:119:2: SA9004: only the first constant in this group has an explicit type (staticcheck)
colUndefined Color = -2
^
2019-11-05 00:46:51 +00:00
|
|
|
if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Contains(str, ",,,") {
|
2015-03-31 13:05:02 +00:00
|
|
|
tokens = append(tokens, ",")
|
|
|
|
}
|
|
|
|
|
2020-12-29 16:59:18 +00:00
|
|
|
chords := make(map[tui.Event]string)
|
2015-03-31 13:05:02 +00:00
|
|
|
for _, key := range tokens {
|
|
|
|
if len(key) == 0 {
|
|
|
|
continue // ignore
|
|
|
|
}
|
2021-01-03 04:25:43 +00:00
|
|
|
key = strings.ReplaceAll(key, string([]rune{escapedComma}), ",")
|
2015-03-28 17:59:32 +00:00
|
|
|
lkey := strings.ToLower(key)
|
2020-12-29 16:59:18 +00:00
|
|
|
add := func(e tui.EventType) {
|
|
|
|
chords[e.AsEvent()] = key
|
|
|
|
}
|
2015-06-18 15:31:48 +00:00
|
|
|
switch lkey {
|
|
|
|
case "up":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Up)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "down":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Down)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "left":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Left)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "right":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Right)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "enter", "return":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.CtrlM)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "space":
|
2020-12-29 16:59:18 +00:00
|
|
|
chords[tui.Key(' ')] = key
|
2015-06-18 15:31:48 +00:00
|
|
|
case "bspace", "bs":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.BSpace)
|
2017-01-27 17:54:47 +00:00
|
|
|
case "ctrl-space":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.CtrlSpace)
|
2023-05-21 09:40:05 +00:00
|
|
|
case "ctrl-delete":
|
|
|
|
add(tui.CtrlDelete)
|
2019-11-14 13:39:25 +00:00
|
|
|
case "ctrl-^", "ctrl-6":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.CtrlCaret)
|
2019-11-14 13:39:25 +00:00
|
|
|
case "ctrl-/", "ctrl-_":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.CtrlSlash)
|
2019-11-14 13:39:25 +00:00
|
|
|
case "ctrl-\\":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.CtrlBackSlash)
|
2019-11-14 13:39:25 +00:00
|
|
|
case "ctrl-]":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.CtrlRightBracket)
|
2017-05-22 08:07:05 +00:00
|
|
|
case "change":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Change)
|
2020-06-07 14:07:03 +00:00
|
|
|
case "backward-eof":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.BackwardEOF)
|
2022-10-26 15:33:05 +00:00
|
|
|
case "start":
|
|
|
|
add(tui.Start)
|
2022-12-29 11:01:50 +00:00
|
|
|
case "load":
|
|
|
|
add(tui.Load)
|
2023-01-23 07:22:25 +00:00
|
|
|
case "focus":
|
|
|
|
add(tui.Focus)
|
2023-04-01 08:16:02 +00:00
|
|
|
case "one":
|
|
|
|
add(tui.One)
|
2023-04-26 06:13:08 +00:00
|
|
|
case "zero":
|
|
|
|
add(tui.Zero)
|
2016-05-12 15:36:13 +00:00
|
|
|
case "alt-enter", "alt-return":
|
2020-12-29 16:59:18 +00:00
|
|
|
chords[tui.CtrlAltKey('m')] = key
|
2016-05-12 15:36:13 +00:00
|
|
|
case "alt-space":
|
2020-12-29 16:59:18 +00:00
|
|
|
chords[tui.AltKey(' ')] = key
|
2015-06-18 15:31:48 +00:00
|
|
|
case "alt-bs", "alt-bspace":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltBS)
|
2018-04-12 08:39:28 +00:00
|
|
|
case "alt-up":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltUp)
|
2018-04-12 08:39:28 +00:00
|
|
|
case "alt-down":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltDown)
|
2018-04-12 08:39:28 +00:00
|
|
|
case "alt-left":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltLeft)
|
2018-04-12 08:39:28 +00:00
|
|
|
case "alt-right":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltRight)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "tab":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Tab)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "btab", "shift-tab":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.BTab)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "esc":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.ESC)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "del":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Del)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "home":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Home)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "end":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.End)
|
2020-02-23 16:43:19 +00:00
|
|
|
case "insert":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.Insert)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "pgup", "page-up":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.PgUp)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "pgdn", "page-down":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.PgDn)
|
2020-11-24 10:36:58 +00:00
|
|
|
case "alt-shift-up", "shift-alt-up":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltSUp)
|
2020-11-24 10:36:58 +00:00
|
|
|
case "alt-shift-down", "shift-alt-down":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltSDown)
|
2020-11-24 10:36:58 +00:00
|
|
|
case "alt-shift-left", "shift-alt-left":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltSLeft)
|
2020-11-24 10:36:58 +00:00
|
|
|
case "alt-shift-right", "shift-alt-right":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.AltSRight)
|
2018-02-15 10:56:11 +00:00
|
|
|
case "shift-up":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.SUp)
|
2018-02-15 10:56:11 +00:00
|
|
|
case "shift-down":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.SDown)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "shift-left":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.SLeft)
|
2015-06-18 15:31:48 +00:00
|
|
|
case "shift-right":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.SRight)
|
2023-05-21 09:40:05 +00:00
|
|
|
case "shift-delete":
|
|
|
|
add(tui.SDelete)
|
2017-11-30 17:11:20 +00:00
|
|
|
case "left-click":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.LeftClick)
|
2017-11-30 17:11:20 +00:00
|
|
|
case "right-click":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.RightClick)
|
2023-10-10 09:58:22 +00:00
|
|
|
case "shift-left-click":
|
|
|
|
add(tui.SLeftClick)
|
|
|
|
case "shift-right-click":
|
|
|
|
add(tui.SRightClick)
|
2015-10-12 17:24:38 +00:00
|
|
|
case "double-click":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.DoubleClick)
|
2023-10-10 09:58:22 +00:00
|
|
|
case "scroll-up":
|
|
|
|
add(tui.ScrollUp)
|
|
|
|
case "scroll-down":
|
|
|
|
add(tui.ScrollDown)
|
|
|
|
case "shift-scroll-up":
|
|
|
|
add(tui.SScrollUp)
|
|
|
|
case "shift-scroll-down":
|
|
|
|
add(tui.SScrollDown)
|
|
|
|
case "preview-scroll-up":
|
|
|
|
add(tui.PreviewScrollUp)
|
|
|
|
case "preview-scroll-down":
|
|
|
|
add(tui.PreviewScrollDown)
|
2016-05-18 13:25:09 +00:00
|
|
|
case "f10":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.F10)
|
2016-11-19 13:40:28 +00:00
|
|
|
case "f11":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.F11)
|
2016-11-19 13:40:28 +00:00
|
|
|
case "f12":
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.F12)
|
2015-06-18 15:31:48 +00:00
|
|
|
default:
|
2020-12-29 16:59:18 +00:00
|
|
|
runes := []rune(key)
|
2017-04-27 17:36:36 +00:00
|
|
|
if len(key) == 10 && strings.HasPrefix(lkey, "ctrl-alt-") && isAlphabet(lkey[9]) {
|
2020-12-29 16:59:18 +00:00
|
|
|
chords[tui.CtrlAltKey(rune(key[9]))] = key
|
2017-04-27 17:36:36 +00:00
|
|
|
} else if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.EventType(tui.CtrlA.Int() + int(lkey[5]) - 'a'))
|
|
|
|
} else if len(runes) == 5 && strings.HasPrefix(lkey, "alt-") {
|
|
|
|
r := runes[4]
|
|
|
|
switch r {
|
|
|
|
case escapedColon:
|
|
|
|
r = ':'
|
|
|
|
case escapedComma:
|
|
|
|
r = ','
|
|
|
|
case escapedPlus:
|
|
|
|
r = '+'
|
|
|
|
}
|
|
|
|
chords[tui.AltKey(r)] = key
|
2016-05-18 13:25:09 +00:00
|
|
|
} else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' {
|
2020-12-29 16:59:18 +00:00
|
|
|
add(tui.EventType(tui.F1.Int() + int(key[1]) - '1'))
|
|
|
|
} else if len(runes) == 1 {
|
|
|
|
chords[tui.Key(runes[0])] = key
|
2015-06-14 16:26:18 +00:00
|
|
|
} else {
|
2023-01-07 06:35:29 +00:00
|
|
|
exit("unsupported key: " + key)
|
|
|
|
return nil
|
2015-06-14 16:26:18 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-28 17:59:32 +00:00
|
|
|
}
|
|
|
|
return chords
|
|
|
|
}
|
|
|
|
|
2016-01-12 18:07:42 +00:00
|
|
|
func parseTiebreak(str string) []criterion {
|
2016-09-07 00:58:18 +00:00
|
|
|
criteria := []criterion{byScore}
|
2016-01-12 18:07:42 +00:00
|
|
|
hasIndex := false
|
2022-08-02 04:44:55 +00:00
|
|
|
hasChunk := false
|
2016-01-12 18:07:42 +00:00
|
|
|
hasLength := false
|
|
|
|
hasBegin := false
|
|
|
|
hasEnd := false
|
|
|
|
check := func(notExpected *bool, name string) {
|
|
|
|
if *notExpected {
|
|
|
|
errorExit("duplicate sort criteria: " + name)
|
|
|
|
}
|
|
|
|
if hasIndex {
|
|
|
|
errorExit("index should be the last criterion")
|
|
|
|
}
|
|
|
|
*notExpected = true
|
|
|
|
}
|
|
|
|
for _, str := range strings.Split(strings.ToLower(str), ",") {
|
|
|
|
switch str {
|
|
|
|
case "index":
|
|
|
|
check(&hasIndex, "index")
|
2022-08-02 04:44:55 +00:00
|
|
|
case "chunk":
|
|
|
|
check(&hasChunk, "chunk")
|
|
|
|
criteria = append(criteria, byChunk)
|
2016-01-12 18:07:42 +00:00
|
|
|
case "length":
|
|
|
|
check(&hasLength, "length")
|
|
|
|
criteria = append(criteria, byLength)
|
|
|
|
case "begin":
|
|
|
|
check(&hasBegin, "begin")
|
|
|
|
criteria = append(criteria, byBegin)
|
|
|
|
case "end":
|
|
|
|
check(&hasEnd, "end")
|
|
|
|
criteria = append(criteria, byEnd)
|
|
|
|
default:
|
|
|
|
errorExit("invalid sort criterion: " + str)
|
|
|
|
}
|
|
|
|
}
|
2022-08-02 04:44:55 +00:00
|
|
|
if len(criteria) > 4 {
|
|
|
|
errorExit("at most 3 tiebreaks are allowed: " + str)
|
|
|
|
}
|
2016-01-12 18:07:42 +00:00
|
|
|
return criteria
|
2015-04-16 05:19:28 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 00:44:56 +00:00
|
|
|
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
2020-10-25 10:29:37 +00:00
|
|
|
dupe := *theme
|
|
|
|
return &dupe
|
2015-05-31 07:46:54 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 00:44:56 +00:00
|
|
|
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
2015-05-31 07:46:54 +00:00
|
|
|
theme := dupeTheme(defaultTheme)
|
2017-01-09 17:16:12 +00:00
|
|
|
rrggbb := regexp.MustCompile("^#[0-9a-fA-F]{6}$")
|
2015-05-31 07:46:54 +00:00
|
|
|
for _, str := range strings.Split(strings.ToLower(str), ",") {
|
|
|
|
switch str {
|
|
|
|
case "dark":
|
2016-10-24 00:44:56 +00:00
|
|
|
theme = dupeTheme(tui.Dark256)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "light":
|
2016-10-24 00:44:56 +00:00
|
|
|
theme = dupeTheme(tui.Light256)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "16":
|
2016-10-24 00:44:56 +00:00
|
|
|
theme = dupeTheme(tui.Default16)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "bw", "no":
|
2020-10-28 16:27:08 +00:00
|
|
|
theme = tui.NoColorTheme()
|
2015-05-31 07:46:54 +00:00
|
|
|
default:
|
|
|
|
fail := func() {
|
|
|
|
errorExit("invalid color specification: " + str)
|
|
|
|
}
|
|
|
|
// Color is disabled
|
|
|
|
if theme == nil {
|
2015-10-09 03:16:47 +00:00
|
|
|
continue
|
2015-05-31 07:46:54 +00:00
|
|
|
}
|
|
|
|
|
2020-10-25 10:29:37 +00:00
|
|
|
components := strings.Split(str, ":")
|
|
|
|
if len(components) < 2 {
|
2015-05-31 07:46:54 +00:00
|
|
|
fail()
|
|
|
|
}
|
2017-01-09 17:16:12 +00:00
|
|
|
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr := func(cattr *tui.ColorAttr) {
|
|
|
|
for _, component := range components[1:] {
|
|
|
|
switch component {
|
|
|
|
case "regular":
|
|
|
|
cattr.Attr = tui.AttrRegular
|
|
|
|
case "bold", "strong":
|
|
|
|
cattr.Attr |= tui.Bold
|
|
|
|
case "dim":
|
|
|
|
cattr.Attr |= tui.Dim
|
|
|
|
case "italic":
|
|
|
|
cattr.Attr |= tui.Italic
|
|
|
|
case "underline":
|
|
|
|
cattr.Attr |= tui.Underline
|
|
|
|
case "blink":
|
|
|
|
cattr.Attr |= tui.Blink
|
|
|
|
case "reverse":
|
|
|
|
cattr.Attr |= tui.Reverse
|
2022-08-20 21:23:03 +00:00
|
|
|
case "strikethrough":
|
|
|
|
cattr.Attr |= tui.StrikeThrough
|
2021-05-26 10:32:20 +00:00
|
|
|
case "black":
|
|
|
|
cattr.Color = tui.Color(0)
|
|
|
|
case "red":
|
|
|
|
cattr.Color = tui.Color(1)
|
|
|
|
case "green":
|
|
|
|
cattr.Color = tui.Color(2)
|
|
|
|
case "yellow":
|
|
|
|
cattr.Color = tui.Color(3)
|
|
|
|
case "blue":
|
|
|
|
cattr.Color = tui.Color(4)
|
|
|
|
case "magenta":
|
|
|
|
cattr.Color = tui.Color(5)
|
|
|
|
case "cyan":
|
|
|
|
cattr.Color = tui.Color(6)
|
|
|
|
case "white":
|
|
|
|
cattr.Color = tui.Color(7)
|
|
|
|
case "bright-black", "gray", "grey":
|
|
|
|
cattr.Color = tui.Color(8)
|
|
|
|
case "bright-red":
|
|
|
|
cattr.Color = tui.Color(9)
|
|
|
|
case "bright-green":
|
|
|
|
cattr.Color = tui.Color(10)
|
|
|
|
case "bright-yellow":
|
|
|
|
cattr.Color = tui.Color(11)
|
|
|
|
case "bright-blue":
|
|
|
|
cattr.Color = tui.Color(12)
|
|
|
|
case "bright-magenta":
|
|
|
|
cattr.Color = tui.Color(13)
|
|
|
|
case "bright-cyan":
|
|
|
|
cattr.Color = tui.Color(14)
|
|
|
|
case "bright-white":
|
|
|
|
cattr.Color = tui.Color(15)
|
2020-12-07 10:07:17 +00:00
|
|
|
case "":
|
|
|
|
default:
|
|
|
|
if rrggbb.MatchString(component) {
|
|
|
|
cattr.Color = tui.HexToColor(component)
|
|
|
|
} else {
|
|
|
|
ansi32, err := strconv.Atoi(component)
|
|
|
|
if err != nil || ansi32 < -1 || ansi32 > 255 {
|
|
|
|
fail()
|
|
|
|
}
|
|
|
|
cattr.Color = tui.Color(ansi32)
|
2020-10-25 10:29:37 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-09 17:16:12 +00:00
|
|
|
}
|
2015-05-31 07:46:54 +00:00
|
|
|
}
|
2020-10-25 10:29:37 +00:00
|
|
|
switch components[0] {
|
2021-01-02 15:00:40 +00:00
|
|
|
case "query", "input":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Input)
|
2021-01-02 15:00:40 +00:00
|
|
|
case "disabled":
|
|
|
|
mergeAttr(&theme.Disabled)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "fg":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Fg)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "bg":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Bg)
|
2019-12-12 14:03:17 +00:00
|
|
|
case "preview-fg":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.PreviewFg)
|
2019-12-12 14:03:17 +00:00
|
|
|
case "preview-bg":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.PreviewBg)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "fg+":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Current)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "bg+":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.DarkBg)
|
2019-03-17 06:37:23 +00:00
|
|
|
case "gutter":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Gutter)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "hl":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Match)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "hl+":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.CurrentMatch)
|
2016-06-11 10:59:12 +00:00
|
|
|
case "border":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Border)
|
2023-05-14 09:28:46 +00:00
|
|
|
case "preview-border":
|
|
|
|
mergeAttr(&theme.PreviewBorder)
|
2022-10-30 07:28:58 +00:00
|
|
|
case "separator":
|
|
|
|
mergeAttr(&theme.Separator)
|
2023-01-01 05:48:14 +00:00
|
|
|
case "scrollbar":
|
|
|
|
mergeAttr(&theme.Scrollbar)
|
2023-05-14 09:28:46 +00:00
|
|
|
case "preview-scrollbar":
|
|
|
|
mergeAttr(&theme.PreviewScrollbar)
|
2022-10-29 15:12:01 +00:00
|
|
|
case "label":
|
|
|
|
mergeAttr(&theme.BorderLabel)
|
2022-12-09 03:05:27 +00:00
|
|
|
case "preview-label":
|
|
|
|
mergeAttr(&theme.PreviewLabel)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "prompt":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Prompt)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "spinner":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Spinner)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "info":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Info)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "pointer":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Cursor)
|
2015-05-31 07:46:54 +00:00
|
|
|
case "marker":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Selected)
|
2015-07-21 15:19:37 +00:00
|
|
|
case "header":
|
2020-12-07 10:07:17 +00:00
|
|
|
mergeAttr(&theme.Header)
|
2015-05-31 07:46:54 +00:00
|
|
|
default:
|
|
|
|
fail()
|
|
|
|
}
|
|
|
|
}
|
2015-04-17 17:52:30 +00:00
|
|
|
}
|
2015-05-31 07:46:54 +00:00
|
|
|
return theme
|
2015-04-17 17:52:30 +00:00
|
|
|
}
|
|
|
|
|
2022-10-30 15:22:41 +00:00
|
|
|
var (
|
2022-12-17 15:22:15 +00:00
|
|
|
executeRegexp *regexp.Regexp
|
|
|
|
splitRegexp *regexp.Regexp
|
|
|
|
actionNameRegexp *regexp.Regexp
|
2022-10-30 15:22:41 +00:00
|
|
|
)
|
2015-06-14 14:36:49 +00:00
|
|
|
|
2020-12-29 16:59:18 +00:00
|
|
|
func firstKey(keymap map[tui.Event]string) tui.Event {
|
2015-06-18 15:31:48 +00:00
|
|
|
for k := range keymap {
|
|
|
|
return k
|
|
|
|
}
|
2020-12-29 16:59:18 +00:00
|
|
|
return tui.EventType(0).AsEvent()
|
2015-06-18 15:31:48 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 10:18:23 +00:00
|
|
|
const (
|
|
|
|
escapedColon = 0
|
|
|
|
escapedComma = 1
|
2017-01-21 17:32:49 +00:00
|
|
|
escapedPlus = 2
|
2015-07-16 10:18:23 +00:00
|
|
|
)
|
|
|
|
|
2017-01-27 08:46:56 +00:00
|
|
|
func init() {
|
|
|
|
executeRegexp = regexp.MustCompile(
|
2023-04-22 13:01:00 +00:00
|
|
|
`(?si)[:+](become|execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:header|query|prompt|border-label|preview-label)|change-preview-window|change-preview|(?:re|un)bind|pos|put)`)
|
2022-10-30 15:22:41 +00:00
|
|
|
splitRegexp = regexp.MustCompile("[,:]+")
|
2022-12-17 15:22:15 +00:00
|
|
|
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
|
2017-01-27 08:46:56 +00:00
|
|
|
}
|
|
|
|
|
2022-12-17 15:22:15 +00:00
|
|
|
func maskActionContents(action string) string {
|
2022-12-22 18:28:16 +00:00
|
|
|
masked := ""
|
|
|
|
Loop:
|
|
|
|
for len(action) > 0 {
|
|
|
|
loc := executeRegexp.FindStringIndex(action)
|
|
|
|
if loc == nil {
|
|
|
|
masked += action
|
|
|
|
break
|
|
|
|
}
|
|
|
|
masked += action[:loc[1]]
|
|
|
|
action = action[loc[1]:]
|
|
|
|
if len(action) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
cs := string(action[0])
|
|
|
|
ce := ")"
|
|
|
|
switch action[0] {
|
|
|
|
case ':':
|
|
|
|
masked += strings.Repeat(" ", len(action))
|
|
|
|
break Loop
|
|
|
|
case '(':
|
|
|
|
ce = ")"
|
|
|
|
case '{':
|
|
|
|
ce = "}"
|
|
|
|
case '[':
|
|
|
|
ce = "]"
|
|
|
|
case '<':
|
|
|
|
ce = ">"
|
|
|
|
case '~', '!', '@', '#', '$', '%', '^', '&', '*', ';', '/', '|':
|
|
|
|
ce = string(cs)
|
|
|
|
default:
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
cs = regexp.QuoteMeta(cs)
|
|
|
|
ce = regexp.QuoteMeta(ce)
|
|
|
|
|
|
|
|
// @$ or @+
|
2023-01-21 13:20:26 +00:00
|
|
|
loc = regexp.MustCompile(fmt.Sprintf(`(?s)^%s.*?(%s[+,]|%s$)`, cs, ce, ce)).FindStringIndex(action)
|
2022-12-22 18:28:16 +00:00
|
|
|
if loc == nil {
|
|
|
|
masked += action
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Keep + or , at the end
|
2022-12-23 06:37:39 +00:00
|
|
|
lastChar := action[loc[1]-1]
|
|
|
|
if lastChar == '+' || lastChar == ',' {
|
|
|
|
loc[1]--
|
|
|
|
}
|
|
|
|
masked += strings.Repeat(" ", loc[1])
|
2022-12-22 18:28:16 +00:00
|
|
|
action = action[loc[1]:]
|
|
|
|
}
|
2015-07-16 10:18:23 +00:00
|
|
|
masked = strings.Replace(masked, "::", string([]rune{escapedColon, ':'}), -1)
|
|
|
|
masked = strings.Replace(masked, ",:", string([]rune{escapedComma, ':'}), -1)
|
2017-01-21 17:32:49 +00:00
|
|
|
masked = strings.Replace(masked, "+:", string([]rune{escapedPlus, ':'}), -1)
|
2022-12-17 15:22:15 +00:00
|
|
|
return masked
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseSingleActionList(str string, exit func(string)) []*action {
|
|
|
|
// We prepend a colon to satisfy executeRegexp and remove it later
|
|
|
|
masked := maskActionContents(":" + str)[1:]
|
|
|
|
return parseActionList(masked, str, []*action{}, false, exit)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseActionList(masked string, original string, prevActions []*action, putAllowed bool, exit func(string)) []*action {
|
|
|
|
maskedStrings := strings.Split(masked, "+")
|
|
|
|
originalStrings := make([]string, len(maskedStrings))
|
|
|
|
idx := 0
|
|
|
|
for i, maskedString := range maskedStrings {
|
|
|
|
originalStrings[i] = original[idx : idx+len(maskedString)]
|
|
|
|
idx += len(maskedString) + 1
|
|
|
|
}
|
|
|
|
actions := make([]*action, 0, len(maskedStrings))
|
|
|
|
appendAction := func(types ...actionType) {
|
|
|
|
actions = append(actions, toActions(types...)...)
|
|
|
|
}
|
|
|
|
prevSpec := ""
|
|
|
|
for specIndex, spec := range originalStrings {
|
|
|
|
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 "accept-non-empty":
|
|
|
|
appendAction(actAcceptNonEmpty)
|
|
|
|
case "print-query":
|
|
|
|
appendAction(actPrintQuery)
|
|
|
|
case "refresh-preview":
|
|
|
|
appendAction(actRefreshPreview)
|
|
|
|
case "replace-query":
|
|
|
|
appendAction(actReplaceQuery)
|
|
|
|
case "backward-char":
|
|
|
|
appendAction(actBackwardChar)
|
|
|
|
case "backward-delete-char":
|
|
|
|
appendAction(actBackwardDeleteChar)
|
|
|
|
case "backward-delete-char/eof":
|
|
|
|
appendAction(actBackwardDeleteCharEOF)
|
|
|
|
case "backward-word":
|
|
|
|
appendAction(actBackwardWord)
|
|
|
|
case "clear-screen":
|
|
|
|
appendAction(actClearScreen)
|
|
|
|
case "delete-char":
|
|
|
|
appendAction(actDeleteChar)
|
|
|
|
case "delete-char/eof":
|
|
|
|
appendAction(actDeleteCharEOF)
|
|
|
|
case "deselect":
|
|
|
|
appendAction(actDeselect)
|
|
|
|
case "end-of-line":
|
|
|
|
appendAction(actEndOfLine)
|
|
|
|
case "cancel":
|
|
|
|
appendAction(actCancel)
|
|
|
|
case "clear-query":
|
|
|
|
appendAction(actClearQuery)
|
|
|
|
case "clear-selection":
|
|
|
|
appendAction(actClearSelection)
|
|
|
|
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 "toggle-search":
|
|
|
|
appendAction(actToggleSearch)
|
2023-04-22 06:06:22 +00:00
|
|
|
case "toggle-track":
|
|
|
|
appendAction(actToggleTrack)
|
2023-07-25 13:11:15 +00:00
|
|
|
case "toggle-header":
|
|
|
|
appendAction(actToggleHeader)
|
2023-04-22 14:39:35 +00:00
|
|
|
case "track":
|
|
|
|
appendAction(actTrack)
|
2022-12-17 15:22:15 +00:00
|
|
|
case "select":
|
|
|
|
appendAction(actSelect)
|
|
|
|
case "select-all":
|
|
|
|
appendAction(actSelectAll)
|
|
|
|
case "deselect-all":
|
|
|
|
appendAction(actDeselectAll)
|
|
|
|
case "close":
|
|
|
|
appendAction(actClose)
|
|
|
|
case "toggle":
|
|
|
|
appendAction(actToggle)
|
|
|
|
case "down":
|
|
|
|
appendAction(actDown)
|
|
|
|
case "up":
|
|
|
|
appendAction(actUp)
|
|
|
|
case "first", "top":
|
|
|
|
appendAction(actFirst)
|
|
|
|
case "last":
|
|
|
|
appendAction(actLast)
|
|
|
|
case "page-up":
|
|
|
|
appendAction(actPageUp)
|
|
|
|
case "page-down":
|
|
|
|
appendAction(actPageDown)
|
|
|
|
case "half-page-up":
|
|
|
|
appendAction(actHalfPageUp)
|
|
|
|
case "half-page-down":
|
|
|
|
appendAction(actHalfPageDown)
|
|
|
|
case "prev-history", "previous-history":
|
|
|
|
appendAction(actPrevHistory)
|
|
|
|
case "next-history":
|
|
|
|
appendAction(actNextHistory)
|
|
|
|
case "prev-selected":
|
|
|
|
appendAction(actPrevSelected)
|
|
|
|
case "next-selected":
|
|
|
|
appendAction(actNextSelected)
|
2023-01-31 08:33:53 +00:00
|
|
|
case "show-preview":
|
|
|
|
appendAction(actShowPreview)
|
|
|
|
case "hide-preview":
|
|
|
|
appendAction(actHidePreview)
|
2022-12-17 15:22:15 +00:00
|
|
|
case "toggle-preview":
|
|
|
|
appendAction(actTogglePreview)
|
|
|
|
case "toggle-preview-wrap":
|
|
|
|
appendAction(actTogglePreviewWrap)
|
|
|
|
case "toggle-sort":
|
|
|
|
appendAction(actToggleSort)
|
2023-10-11 03:51:05 +00:00
|
|
|
case "offset-up":
|
|
|
|
appendAction(actOffsetUp)
|
|
|
|
case "offset-down":
|
|
|
|
appendAction(actOffsetDown)
|
2022-12-17 15:22:15 +00:00
|
|
|
case "preview-top":
|
|
|
|
appendAction(actPreviewTop)
|
|
|
|
case "preview-bottom":
|
|
|
|
appendAction(actPreviewBottom)
|
|
|
|
case "preview-up":
|
|
|
|
appendAction(actPreviewUp)
|
|
|
|
case "preview-down":
|
|
|
|
appendAction(actPreviewDown)
|
|
|
|
case "preview-page-up":
|
|
|
|
appendAction(actPreviewPageUp)
|
|
|
|
case "preview-page-down":
|
|
|
|
appendAction(actPreviewPageDown)
|
|
|
|
case "preview-half-page-up":
|
|
|
|
appendAction(actPreviewHalfPageUp)
|
|
|
|
case "preview-half-page-down":
|
|
|
|
appendAction(actPreviewHalfPageDown)
|
|
|
|
case "enable-search":
|
|
|
|
appendAction(actEnableSearch)
|
|
|
|
case "disable-search":
|
|
|
|
appendAction(actDisableSearch)
|
|
|
|
case "put":
|
|
|
|
if putAllowed {
|
|
|
|
appendAction(actRune)
|
|
|
|
} else {
|
|
|
|
exit("unable to put non-printable character")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
t := isExecuteAction(specLower)
|
|
|
|
if t == actIgnore {
|
|
|
|
if specIndex == 0 && specLower == "" {
|
|
|
|
actions = append(prevActions, actions...)
|
|
|
|
} else {
|
|
|
|
exit("unknown action: " + spec)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
offset := len(actionNameRegexp.FindString(spec))
|
|
|
|
var actionArg string
|
|
|
|
if spec[offset] == ':' {
|
|
|
|
if specIndex == len(originalStrings)-1 {
|
|
|
|
actionArg = spec[offset+1:]
|
|
|
|
actions = append(actions, &action{t: t, a: actionArg})
|
|
|
|
} else {
|
|
|
|
prevSpec = spec + "+"
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actionArg = spec[offset+1 : len(spec)-1]
|
|
|
|
actions = append(actions, &action{t: t, a: actionArg})
|
|
|
|
}
|
2023-01-07 06:35:29 +00:00
|
|
|
switch t {
|
2023-02-11 11:21:10 +00:00
|
|
|
case actBecome:
|
|
|
|
if util.IsWindows() {
|
|
|
|
exit("become action is not supported on Windows")
|
|
|
|
}
|
2023-01-07 06:35:29 +00:00
|
|
|
case actUnbind, actRebind:
|
|
|
|
parseKeyChordsImpl(actionArg, spec[0:offset]+" target required", exit)
|
|
|
|
case actChangePreviewWindow:
|
|
|
|
opts := previewOpts{}
|
|
|
|
for _, arg := range strings.Split(actionArg, "|") {
|
|
|
|
// Make sure that each expression is valid
|
|
|
|
parsePreviewWindowImpl(&opts, arg, exit)
|
|
|
|
}
|
2022-12-17 15:22:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prevSpec = ""
|
|
|
|
}
|
|
|
|
return actions
|
|
|
|
}
|
2015-06-14 03:25:08 +00:00
|
|
|
|
2022-12-17 15:22:15 +00:00
|
|
|
func parseKeymap(keymap map[tui.Event][]*action, str string, exit func(string)) {
|
|
|
|
masked := maskActionContents(str)
|
2015-06-14 03:25:08 +00:00
|
|
|
idx := 0
|
|
|
|
for _, pairStr := range strings.Split(masked, ",") {
|
2015-07-16 10:18:23 +00:00
|
|
|
origPairStr := str[idx : idx+len(pairStr)]
|
2015-06-14 03:25:08 +00:00
|
|
|
idx += len(pairStr) + 1
|
|
|
|
|
|
|
|
pair := strings.SplitN(pairStr, ":", 2)
|
2015-07-17 17:31:35 +00:00
|
|
|
if len(pair) < 2 {
|
2022-12-17 15:22:15 +00:00
|
|
|
exit("bind action not specified: " + origPairStr)
|
2015-05-20 12:25:15 +00:00
|
|
|
}
|
2020-12-29 16:59:18 +00:00
|
|
|
var key tui.Event
|
2015-07-16 10:18:23 +00:00
|
|
|
if len(pair[0]) == 1 && pair[0][0] == escapedColon {
|
2020-12-29 16:59:18 +00:00
|
|
|
key = tui.Key(':')
|
2015-07-16 10:18:23 +00:00
|
|
|
} else if len(pair[0]) == 1 && pair[0][0] == escapedComma {
|
2020-12-29 16:59:18 +00:00
|
|
|
key = tui.Key(',')
|
2017-01-21 17:32:49 +00:00
|
|
|
} else if len(pair[0]) == 1 && pair[0][0] == escapedPlus {
|
2020-12-29 16:59:18 +00:00
|
|
|
key = tui.Key('+')
|
2015-07-16 10:18:23 +00:00
|
|
|
} else {
|
2023-01-07 06:35:29 +00:00
|
|
|
keys := parseKeyChordsImpl(pair[0], "key name required", exit)
|
2015-07-16 10:18:23 +00:00
|
|
|
key = firstKey(keys)
|
2015-05-20 12:25:15 +00:00
|
|
|
}
|
2022-12-17 15:22:15 +00:00
|
|
|
putAllowed := key.Type == tui.Rune && unicode.IsGraphic(key.Char)
|
|
|
|
keymap[key] = parseActionList(pair[1], origPairStr[len(pair[0])+1:], keymap[key], putAllowed, exit)
|
2015-05-20 12:25:15 +00:00
|
|
|
}
|
2015-06-14 03:25:08 +00:00
|
|
|
}
|
|
|
|
|
2017-01-21 17:32:49 +00:00
|
|
|
func isExecuteAction(str string) actionType {
|
2022-12-22 18:28:16 +00:00
|
|
|
masked := maskActionContents(":" + str)[1:]
|
|
|
|
if masked == str {
|
|
|
|
// Not masked
|
2017-01-21 17:32:49 +00:00
|
|
|
return actIgnore
|
2015-06-14 03:25:08 +00:00
|
|
|
}
|
2022-12-22 18:28:16 +00:00
|
|
|
|
|
|
|
prefix := actionNameRegexp.FindString(str)
|
2017-01-27 08:46:56 +00:00
|
|
|
switch prefix {
|
2023-02-11 11:21:10 +00:00
|
|
|
case "become":
|
|
|
|
return actBecome
|
2019-11-10 02:36:22 +00:00
|
|
|
case "reload":
|
|
|
|
return actReload
|
2022-12-29 11:03:51 +00:00
|
|
|
case "reload-sync":
|
|
|
|
return actReloadSync
|
2021-05-22 04:13:55 +00:00
|
|
|
case "unbind":
|
|
|
|
return actUnbind
|
2022-04-04 12:54:22 +00:00
|
|
|
case "rebind":
|
|
|
|
return actRebind
|
2020-06-21 13:41:33 +00:00
|
|
|
case "preview":
|
|
|
|
return actPreview
|
2023-01-21 16:56:29 +00:00
|
|
|
case "change-border-label":
|
|
|
|
return actChangeBorderLabel
|
2023-04-22 13:01:00 +00:00
|
|
|
case "change-header":
|
|
|
|
return actChangeHeader
|
2023-01-21 16:56:29 +00:00
|
|
|
case "change-preview-label":
|
|
|
|
return actChangePreviewLabel
|
2021-11-30 14:37:48 +00:00
|
|
|
case "change-preview-window":
|
|
|
|
return actChangePreviewWindow
|
|
|
|
case "change-preview":
|
|
|
|
return actChangePreview
|
2020-12-04 11:34:41 +00:00
|
|
|
case "change-prompt":
|
|
|
|
return actChangePrompt
|
2022-12-17 09:59:16 +00:00
|
|
|
case "change-query":
|
|
|
|
return actChangeQuery
|
2022-12-26 16:01:06 +00:00
|
|
|
case "pos":
|
|
|
|
return actPosition
|
2017-01-27 08:46:56 +00:00
|
|
|
case "execute":
|
|
|
|
return actExecute
|
|
|
|
case "execute-silent":
|
|
|
|
return actExecuteSilent
|
|
|
|
case "execute-multi":
|
|
|
|
return actExecuteMulti
|
2022-12-27 10:54:46 +00:00
|
|
|
case "put":
|
|
|
|
return actPut
|
2023-01-21 16:56:29 +00:00
|
|
|
case "transform-border-label":
|
|
|
|
return actTransformBorderLabel
|
|
|
|
case "transform-preview-label":
|
|
|
|
return actTransformPreviewLabel
|
2023-04-22 13:01:00 +00:00
|
|
|
case "transform-header":
|
|
|
|
return actTransformHeader
|
2022-12-31 00:27:11 +00:00
|
|
|
case "transform-prompt":
|
|
|
|
return actTransformPrompt
|
2022-12-27 15:05:31 +00:00
|
|
|
case "transform-query":
|
|
|
|
return actTransformQuery
|
2015-06-14 03:25:08 +00:00
|
|
|
}
|
2017-01-21 17:32:49 +00:00
|
|
|
return actIgnore
|
2015-05-20 12:25:15 +00:00
|
|
|
}
|
|
|
|
|
2021-12-04 02:46:15 +00:00
|
|
|
func parseToggleSort(keymap map[tui.Event][]*action, str string) {
|
2015-06-18 15:31:48 +00:00
|
|
|
keys := parseKeyChords(str, "key name required")
|
2015-03-31 13:05:02 +00:00
|
|
|
if len(keys) != 1 {
|
|
|
|
errorExit("multiple keys specified")
|
|
|
|
}
|
2017-01-21 17:32:49 +00:00
|
|
|
keymap[firstKey(keys)] = toActions(actToggleSort)
|
2015-03-31 13:05:02 +00:00
|
|
|
}
|
|
|
|
|
2015-09-15 10:04:53 +00:00
|
|
|
func strLines(str string) []string {
|
|
|
|
return strings.Split(strings.TrimSuffix(str, "\n"), "\n")
|
2015-07-21 15:19:37 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 10:59:12 +00:00
|
|
|
func parseSize(str string, maxPercent float64, label string) sizeSpec {
|
|
|
|
var val float64
|
|
|
|
percent := strings.HasSuffix(str, "%")
|
|
|
|
if percent {
|
|
|
|
val = atof(str[:len(str)-1])
|
|
|
|
if val < 0 {
|
|
|
|
errorExit(label + " must be non-negative")
|
2015-07-26 14:02:04 +00:00
|
|
|
}
|
2016-06-11 10:59:12 +00:00
|
|
|
if val > maxPercent {
|
|
|
|
errorExit(fmt.Sprintf("%s too large (max: %d%%)", label, int(maxPercent)))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if strings.Contains(str, ".") {
|
|
|
|
errorExit(label + " (without %) must be a non-negative integer")
|
|
|
|
}
|
|
|
|
|
|
|
|
val = float64(atoi(str))
|
|
|
|
if val < 0 {
|
|
|
|
errorExit(label + " must be non-negative")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sizeSpec{val, percent}
|
|
|
|
}
|
|
|
|
|
2022-09-07 16:01:22 +00:00
|
|
|
func parseHeight(str string) heightSpec {
|
|
|
|
heightSpec := heightSpec{}
|
|
|
|
if strings.HasPrefix(str, "~") {
|
|
|
|
heightSpec.auto = true
|
|
|
|
str = str[1:]
|
|
|
|
}
|
|
|
|
|
2017-01-07 16:30:31 +00:00
|
|
|
size := parseSize(str, 100, "height")
|
2022-09-07 16:01:22 +00:00
|
|
|
heightSpec.size = size.size
|
|
|
|
heightSpec.percent = size.percent
|
|
|
|
return heightSpec
|
2017-01-07 16:30:31 +00:00
|
|
|
}
|
|
|
|
|
2018-06-09 16:41:50 +00:00
|
|
|
func parseLayout(str string) layoutType {
|
|
|
|
switch str {
|
|
|
|
case "default":
|
|
|
|
return layoutDefault
|
|
|
|
case "reverse":
|
|
|
|
return layoutReverse
|
|
|
|
case "reverse-list":
|
|
|
|
return layoutReverseList
|
|
|
|
default:
|
|
|
|
errorExit("invalid layout (expected: default / reverse / reverse-list)")
|
|
|
|
}
|
|
|
|
return layoutDefault
|
|
|
|
}
|
|
|
|
|
2023-01-24 08:40:08 +00:00
|
|
|
func parseInfoStyle(str string) (infoStyle, string) {
|
2022-11-10 07:23:33 +00:00
|
|
|
switch str {
|
|
|
|
case "default":
|
2023-01-24 08:40:08 +00:00
|
|
|
return infoDefault, ""
|
2023-06-10 15:04:24 +00:00
|
|
|
case "right":
|
|
|
|
return infoRight, ""
|
2022-11-10 07:23:33 +00:00
|
|
|
case "inline":
|
2023-01-24 08:40:08 +00:00
|
|
|
return infoInline, defaultInfoSep
|
2023-06-10 14:11:05 +00:00
|
|
|
case "inline-right":
|
|
|
|
return infoInlineRight, ""
|
2022-11-10 07:23:33 +00:00
|
|
|
case "hidden":
|
2023-01-24 08:40:08 +00:00
|
|
|
return infoHidden, ""
|
2022-11-10 07:23:33 +00:00
|
|
|
default:
|
2023-01-24 08:40:08 +00:00
|
|
|
prefix := "inline:"
|
|
|
|
if strings.HasPrefix(str, prefix) {
|
|
|
|
return infoInline, strings.ReplaceAll(str[len(prefix):], "\n", " ")
|
|
|
|
}
|
2023-06-10 15:04:24 +00:00
|
|
|
errorExit("invalid info style (expected: default|right|hidden|inline[:SEPARATOR]|inline-right)")
|
2019-11-14 15:39:29 +00:00
|
|
|
}
|
2023-01-24 08:40:08 +00:00
|
|
|
return infoDefault, ""
|
2019-11-14 15:39:29 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 10:59:12 +00:00
|
|
|
func parsePreviewWindow(opts *previewOpts, input string) {
|
2023-01-07 06:35:29 +00:00
|
|
|
parsePreviewWindowImpl(opts, input, errorExit)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parsePreviewWindowImpl(opts *previewOpts, input string, exit func(string)) {
|
2022-07-20 03:08:54 +00:00
|
|
|
tokenRegex := regexp.MustCompile(`[:,]*(<([1-9][0-9]*)\(([^)<]+)\)|[^,:]+)`)
|
2020-08-23 08:12:37 +00:00
|
|
|
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
|
2021-03-12 17:24:37 +00:00
|
|
|
offsetRegex := regexp.MustCompile(`^(\+{-?[0-9]+})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`)
|
2021-03-12 10:51:28 +00:00
|
|
|
headerRegex := regexp.MustCompile("^~(0|[1-9][0-9]*)$")
|
2022-07-20 03:08:54 +00:00
|
|
|
tokens := tokenRegex.FindAllStringSubmatch(input, -1)
|
|
|
|
var alternative string
|
|
|
|
for _, match := range tokens {
|
|
|
|
if len(match[2]) > 0 {
|
|
|
|
opts.threshold = atoi(match[2])
|
|
|
|
alternative = match[3]
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
token := match[1]
|
2016-12-04 17:13:47 +00:00
|
|
|
switch token {
|
2019-11-15 02:37:52 +00:00
|
|
|
case "":
|
2020-10-10 16:51:39 +00:00
|
|
|
case "default":
|
|
|
|
*opts = defaultPreviewOpts(opts.command)
|
2016-12-04 17:13:47 +00:00
|
|
|
case "hidden":
|
|
|
|
opts.hidden = true
|
2020-10-09 12:56:16 +00:00
|
|
|
case "nohidden":
|
|
|
|
opts.hidden = false
|
2016-12-04 17:13:47 +00:00
|
|
|
case "wrap":
|
|
|
|
opts.wrap = true
|
2020-10-09 12:56:16 +00:00
|
|
|
case "nowrap":
|
|
|
|
opts.wrap = false
|
2023-10-22 16:01:47 +00:00
|
|
|
case "clear":
|
|
|
|
opts.clear = true
|
|
|
|
case "noclear":
|
|
|
|
opts.clear = false
|
2020-10-06 01:05:57 +00:00
|
|
|
case "cycle":
|
|
|
|
opts.cycle = true
|
2020-10-09 12:56:16 +00:00
|
|
|
case "nocycle":
|
|
|
|
opts.cycle = false
|
2016-12-04 17:13:47 +00:00
|
|
|
case "up", "top":
|
|
|
|
opts.position = posUp
|
|
|
|
case "down", "bottom":
|
|
|
|
opts.position = posDown
|
|
|
|
case "left":
|
|
|
|
opts.position = posLeft
|
|
|
|
case "right":
|
|
|
|
opts.position = posRight
|
2021-04-06 08:37:11 +00:00
|
|
|
case "rounded", "border", "border-rounded":
|
2020-08-23 08:05:45 +00:00
|
|
|
opts.border = tui.BorderRounded
|
2021-04-06 08:37:11 +00:00
|
|
|
case "sharp", "border-sharp":
|
2020-08-23 08:05:45 +00:00
|
|
|
opts.border = tui.BorderSharp
|
2022-11-06 05:38:31 +00:00
|
|
|
case "border-bold":
|
|
|
|
opts.border = tui.BorderBold
|
2023-05-16 14:45:31 +00:00
|
|
|
case "border-block":
|
|
|
|
opts.border = tui.BorderBlock
|
2023-06-10 05:48:29 +00:00
|
|
|
case "border-thinblock":
|
|
|
|
opts.border = tui.BorderThinBlock
|
2022-11-06 05:38:31 +00:00
|
|
|
case "border-double":
|
|
|
|
opts.border = tui.BorderDouble
|
2021-04-06 08:37:11 +00:00
|
|
|
case "noborder", "border-none":
|
2020-08-23 08:05:45 +00:00
|
|
|
opts.border = tui.BorderNone
|
2021-04-06 08:37:11 +00:00
|
|
|
case "border-horizontal":
|
|
|
|
opts.border = tui.BorderHorizontal
|
|
|
|
case "border-vertical":
|
|
|
|
opts.border = tui.BorderVertical
|
2022-08-18 00:19:02 +00:00
|
|
|
case "border-up", "border-top":
|
2021-04-06 08:37:11 +00:00
|
|
|
opts.border = tui.BorderTop
|
2022-08-18 00:19:02 +00:00
|
|
|
case "border-down", "border-bottom":
|
2021-04-06 08:37:11 +00:00
|
|
|
opts.border = tui.BorderBottom
|
|
|
|
case "border-left":
|
|
|
|
opts.border = tui.BorderLeft
|
|
|
|
case "border-right":
|
|
|
|
opts.border = tui.BorderRight
|
2020-12-05 12:16:35 +00:00
|
|
|
case "follow":
|
|
|
|
opts.follow = true
|
|
|
|
case "nofollow":
|
|
|
|
opts.follow = false
|
2016-12-04 17:13:47 +00:00
|
|
|
default:
|
2021-03-12 10:51:28 +00:00
|
|
|
if headerRegex.MatchString(token) {
|
|
|
|
opts.headerLines = atoi(token[1:])
|
|
|
|
} else if sizeRegex.MatchString(token) {
|
2016-12-04 17:13:47 +00:00
|
|
|
opts.size = parseSize(token, 99, "window size")
|
2020-07-26 15:15:25 +00:00
|
|
|
} else if offsetRegex.MatchString(token) {
|
2021-03-12 17:24:37 +00:00
|
|
|
opts.scroll = token
|
2016-12-04 17:13:47 +00:00
|
|
|
} else {
|
2023-01-07 06:35:29 +00:00
|
|
|
exit("invalid preview window option: " + token)
|
|
|
|
return
|
2016-12-04 17:13:47 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-11 10:59:12 +00:00
|
|
|
}
|
2022-07-20 03:08:54 +00:00
|
|
|
if len(alternative) > 0 {
|
|
|
|
alternativeOpts := *opts
|
|
|
|
opts.alternative = &alternativeOpts
|
2022-07-20 03:29:45 +00:00
|
|
|
opts.alternative.hidden = false
|
2022-07-20 03:08:54 +00:00
|
|
|
opts.alternative.alternative = nil
|
2023-01-07 06:35:29 +00:00
|
|
|
parsePreviewWindowImpl(opts.alternative, alternative, exit)
|
2022-07-20 03:08:54 +00:00
|
|
|
}
|
2016-06-11 10:59:12 +00:00
|
|
|
}
|
|
|
|
|
2020-11-09 11:34:08 +00:00
|
|
|
func parseMargin(opt string, margin string) [4]sizeSpec {
|
2016-06-11 10:59:12 +00:00
|
|
|
margins := strings.Split(margin, ",")
|
|
|
|
checked := func(str string) sizeSpec {
|
2020-11-09 11:34:08 +00:00
|
|
|
return parseSize(str, 49, opt)
|
2015-07-26 14:02:04 +00:00
|
|
|
}
|
|
|
|
switch len(margins) {
|
|
|
|
case 1:
|
|
|
|
m := checked(margins[0])
|
2016-06-11 10:59:12 +00:00
|
|
|
return [4]sizeSpec{m, m, m, m}
|
2015-07-26 14:02:04 +00:00
|
|
|
case 2:
|
|
|
|
tb := checked(margins[0])
|
|
|
|
rl := checked(margins[1])
|
2016-06-11 10:59:12 +00:00
|
|
|
return [4]sizeSpec{tb, rl, tb, rl}
|
2015-07-26 14:02:04 +00:00
|
|
|
case 3:
|
|
|
|
t := checked(margins[0])
|
|
|
|
rl := checked(margins[1])
|
|
|
|
b := checked(margins[2])
|
2016-06-11 10:59:12 +00:00
|
|
|
return [4]sizeSpec{t, rl, b, rl}
|
2015-07-26 14:02:04 +00:00
|
|
|
case 4:
|
2016-06-11 10:59:12 +00:00
|
|
|
return [4]sizeSpec{
|
2015-07-26 14:02:04 +00:00
|
|
|
checked(margins[0]), checked(margins[1]),
|
|
|
|
checked(margins[2]), checked(margins[3])}
|
|
|
|
default:
|
2020-11-09 11:34:08 +00:00
|
|
|
errorExit("invalid " + opt + ": " + margin)
|
2015-07-26 14:02:04 +00:00
|
|
|
}
|
|
|
|
return defaultMargin()
|
|
|
|
}
|
|
|
|
|
2015-01-01 19:49:30 +00:00
|
|
|
func parseOptions(opts *Options, allArgs []string) {
|
2015-06-13 15:43:44 +00:00
|
|
|
var historyMax int
|
|
|
|
if opts.History == nil {
|
|
|
|
historyMax = defaultHistoryMax
|
|
|
|
} else {
|
|
|
|
historyMax = opts.History.maxSize
|
|
|
|
}
|
|
|
|
setHistory := func(path string) {
|
|
|
|
h, e := NewHistory(path, historyMax)
|
|
|
|
if e != nil {
|
|
|
|
errorExit(e.Error())
|
|
|
|
}
|
|
|
|
opts.History = h
|
|
|
|
}
|
|
|
|
setHistoryMax := func(max int) {
|
|
|
|
historyMax = max
|
|
|
|
if historyMax < 1 {
|
|
|
|
errorExit("history max must be a positive integer")
|
|
|
|
}
|
|
|
|
if opts.History != nil {
|
|
|
|
opts.History.maxSize = historyMax
|
|
|
|
}
|
|
|
|
}
|
2016-05-18 16:46:22 +00:00
|
|
|
validateJumpLabels := false
|
2020-02-17 01:19:03 +00:00
|
|
|
validatePointer := false
|
|
|
|
validateMarker := false
|
2015-01-01 19:49:30 +00:00
|
|
|
for i := 0; i < len(allArgs); i++ {
|
|
|
|
arg := allArgs[i]
|
|
|
|
switch arg {
|
|
|
|
case "-h", "--help":
|
2015-09-15 04:21:51 +00:00
|
|
|
help(exitOk)
|
2015-01-01 19:49:30 +00:00
|
|
|
case "-x", "--extended":
|
2015-11-03 13:49:32 +00:00
|
|
|
opts.Extended = true
|
|
|
|
case "-e", "--exact":
|
|
|
|
opts.Fuzzy = false
|
|
|
|
case "--extended-exact":
|
|
|
|
// Note that we now don't have --no-extended-exact
|
|
|
|
opts.Fuzzy = false
|
|
|
|
opts.Extended = true
|
|
|
|
case "+x", "--no-extended":
|
|
|
|
opts.Extended = false
|
|
|
|
case "+e", "--no-exact":
|
|
|
|
opts.Fuzzy = true
|
2015-01-01 19:49:30 +00:00
|
|
|
case "-q", "--query":
|
|
|
|
opts.Query = nextString(allArgs, &i, "query string required")
|
|
|
|
case "-f", "--filter":
|
|
|
|
filter := nextString(allArgs, &i, "query string required")
|
|
|
|
opts.Filter = &filter
|
2017-01-15 04:22:09 +00:00
|
|
|
case "--literal":
|
2017-01-08 18:12:23 +00:00
|
|
|
opts.Normalize = false
|
2017-01-15 04:22:09 +00:00
|
|
|
case "--no-literal":
|
|
|
|
opts.Normalize = true
|
2016-09-07 00:58:18 +00:00
|
|
|
case "--algo":
|
|
|
|
opts.FuzzyAlgo = parseAlgo(nextString(allArgs, &i, "algorithm required (v1|v2)"))
|
2022-08-28 13:16:57 +00:00
|
|
|
case "--scheme":
|
|
|
|
opts.Scheme = strings.ToLower(nextString(allArgs, &i, "scoring scheme required (default|path|history)"))
|
2015-03-28 17:59:32 +00:00
|
|
|
case "--expect":
|
2017-08-26 17:19:39 +00:00
|
|
|
for k, v := range parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required") {
|
|
|
|
opts.Expect[k] = v
|
|
|
|
}
|
2017-07-04 14:02:08 +00:00
|
|
|
case "--no-expect":
|
2020-12-29 16:59:18 +00:00
|
|
|
opts.Expect = make(map[tui.Event]string)
|
2021-01-02 15:00:40 +00:00
|
|
|
case "--enabled", "--no-phony":
|
2019-11-09 13:54:48 +00:00
|
|
|
opts.Phony = false
|
2021-01-02 15:00:40 +00:00
|
|
|
case "--disabled", "--phony":
|
2019-11-09 13:54:48 +00:00
|
|
|
opts.Phony = true
|
2015-04-16 05:19:28 +00:00
|
|
|
case "--tiebreak":
|
2016-01-12 18:07:42 +00:00
|
|
|
opts.Criteria = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
|
2015-05-20 12:25:15 +00:00
|
|
|
case "--bind":
|
2022-12-17 15:22:15 +00:00
|
|
|
parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"), errorExit)
|
2015-04-17 17:52:30 +00:00
|
|
|
case "--color":
|
2020-03-05 11:15:15 +00:00
|
|
|
_, spec := optionalNextString(allArgs, &i)
|
2015-05-31 07:46:54 +00:00
|
|
|
if len(spec) == 0 {
|
2016-10-24 00:44:56 +00:00
|
|
|
opts.Theme = tui.EmptyTheme()
|
2015-05-31 07:46:54 +00:00
|
|
|
} else {
|
|
|
|
opts.Theme = parseTheme(opts.Theme, spec)
|
|
|
|
}
|
2015-03-31 13:05:02 +00:00
|
|
|
case "--toggle-sort":
|
2016-02-17 16:46:18 +00:00
|
|
|
parseToggleSort(opts.Keymap, nextString(allArgs, &i, "key name required"))
|
2015-01-01 19:49:30 +00:00
|
|
|
case "-d", "--delimiter":
|
|
|
|
opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required"))
|
|
|
|
case "-n", "--nth":
|
|
|
|
opts.Nth = splitNth(nextString(allArgs, &i, "nth expression required"))
|
|
|
|
case "--with-nth":
|
|
|
|
opts.WithNth = splitNth(nextString(allArgs, &i, "nth expression required"))
|
|
|
|
case "-s", "--sort":
|
2019-11-02 03:55:26 +00:00
|
|
|
opts.Sort = optionalNumeric(allArgs, &i, 1)
|
2015-01-01 19:49:30 +00:00
|
|
|
case "+s", "--no-sort":
|
|
|
|
opts.Sort = 0
|
2023-03-29 11:36:09 +00:00
|
|
|
case "--track":
|
2023-04-22 14:39:35 +00:00
|
|
|
opts.Track = trackEnabled
|
2023-03-29 11:36:09 +00:00
|
|
|
case "--no-track":
|
2023-04-22 14:39:35 +00:00
|
|
|
opts.Track = trackDisabled
|
2015-02-25 16:42:15 +00:00
|
|
|
case "--tac":
|
|
|
|
opts.Tac = true
|
|
|
|
case "--no-tac":
|
|
|
|
opts.Tac = false
|
2015-01-01 19:49:30 +00:00
|
|
|
case "-i":
|
2015-01-11 18:01:24 +00:00
|
|
|
opts.Case = CaseIgnore
|
2015-01-01 19:49:30 +00:00
|
|
|
case "+i":
|
2015-01-11 18:01:24 +00:00
|
|
|
opts.Case = CaseRespect
|
2015-01-01 19:49:30 +00:00
|
|
|
case "-m", "--multi":
|
2019-11-02 03:55:26 +00:00
|
|
|
opts.Multi = optionalNumeric(allArgs, &i, maxMulti)
|
2015-01-01 19:49:30 +00:00
|
|
|
case "+m", "--no-multi":
|
2019-11-02 03:55:26 +00:00
|
|
|
opts.Multi = 0
|
2015-03-18 16:59:14 +00:00
|
|
|
case "--ansi":
|
|
|
|
opts.Ansi = true
|
|
|
|
case "--no-ansi":
|
|
|
|
opts.Ansi = false
|
2015-01-01 19:49:30 +00:00
|
|
|
case "--no-mouse":
|
|
|
|
opts.Mouse = false
|
|
|
|
case "+c", "--no-color":
|
2020-10-25 10:29:37 +00:00
|
|
|
opts.Theme = tui.NoColorTheme()
|
2015-01-01 19:49:30 +00:00
|
|
|
case "+2", "--no-256":
|
2016-10-24 00:44:56 +00:00
|
|
|
opts.Theme = tui.Default16
|
2015-01-01 19:49:30 +00:00
|
|
|
case "--black":
|
|
|
|
opts.Black = true
|
|
|
|
case "--no-black":
|
|
|
|
opts.Black = false
|
2016-11-15 14:57:32 +00:00
|
|
|
case "--bold":
|
|
|
|
opts.Bold = true
|
|
|
|
case "--no-bold":
|
|
|
|
opts.Bold = false
|
2018-06-09 16:41:50 +00:00
|
|
|
case "--layout":
|
|
|
|
opts.Layout = parseLayout(
|
|
|
|
nextString(allArgs, &i, "layout required (default / reverse / reverse-list)"))
|
2015-01-01 19:49:30 +00:00
|
|
|
case "--reverse":
|
2018-06-09 16:41:50 +00:00
|
|
|
opts.Layout = layoutReverse
|
2015-01-01 19:49:30 +00:00
|
|
|
case "--no-reverse":
|
2018-06-09 16:41:50 +00:00
|
|
|
opts.Layout = layoutDefault
|
2015-06-16 14:14:57 +00:00
|
|
|
case "--cycle":
|
|
|
|
opts.Cycle = true
|
|
|
|
case "--no-cycle":
|
|
|
|
opts.Cycle = false
|
2020-03-11 13:35:24 +00:00
|
|
|
case "--keep-right":
|
|
|
|
opts.KeepRight = true
|
|
|
|
case "--no-keep-right":
|
|
|
|
opts.KeepRight = false
|
2015-04-16 03:56:01 +00:00
|
|
|
case "--hscroll":
|
|
|
|
opts.Hscroll = true
|
|
|
|
case "--no-hscroll":
|
|
|
|
opts.Hscroll = false
|
2016-03-01 18:06:21 +00:00
|
|
|
case "--hscroll-off":
|
|
|
|
opts.HscrollOff = nextInt(allArgs, &i, "hscroll offset required")
|
2021-11-02 12:18:29 +00:00
|
|
|
case "--scroll-off":
|
|
|
|
opts.ScrollOff = nextInt(allArgs, &i, "scroll offset required")
|
2017-01-15 10:42:28 +00:00
|
|
|
case "--filepath-word":
|
|
|
|
opts.FileWord = true
|
|
|
|
case "--no-filepath-word":
|
|
|
|
opts.FileWord = false
|
2019-11-14 15:39:29 +00:00
|
|
|
case "--info":
|
2023-01-24 08:40:08 +00:00
|
|
|
opts.InfoStyle, opts.InfoSep = parseInfoStyle(
|
2019-11-14 15:39:29 +00:00
|
|
|
nextString(allArgs, &i, "info style required"))
|
|
|
|
case "--no-info":
|
2022-11-10 07:23:33 +00:00
|
|
|
opts.InfoStyle = infoHidden
|
2015-04-21 14:50:53 +00:00
|
|
|
case "--inline-info":
|
2022-11-10 07:23:33 +00:00
|
|
|
opts.InfoStyle = infoInline
|
2023-01-24 08:40:08 +00:00
|
|
|
opts.InfoSep = defaultInfoSep
|
2015-04-21 14:50:53 +00:00
|
|
|
case "--no-inline-info":
|
2022-11-10 07:23:33 +00:00
|
|
|
opts.InfoStyle = infoDefault
|
|
|
|
case "--separator":
|
|
|
|
separator := nextString(allArgs, &i, "separator character required")
|
|
|
|
opts.Separator = &separator
|
|
|
|
case "--no-separator":
|
|
|
|
nosep := ""
|
|
|
|
opts.Separator = &nosep
|
2023-01-01 05:48:14 +00:00
|
|
|
case "--scrollbar":
|
|
|
|
given, bar := optionalNextString(allArgs, &i)
|
|
|
|
if given {
|
|
|
|
opts.Scrollbar = &bar
|
|
|
|
} else {
|
|
|
|
opts.Scrollbar = nil
|
|
|
|
}
|
|
|
|
case "--no-scrollbar":
|
|
|
|
noBar := ""
|
|
|
|
opts.Scrollbar = &noBar
|
2016-05-17 17:06:52 +00:00
|
|
|
case "--jump-labels":
|
|
|
|
opts.JumpLabels = nextString(allArgs, &i, "label characters required")
|
2016-05-18 16:46:22 +00:00
|
|
|
validateJumpLabels = true
|
2015-01-01 19:49:30 +00:00
|
|
|
case "-1", "--select-1":
|
|
|
|
opts.Select1 = true
|
|
|
|
case "+1", "--no-select-1":
|
|
|
|
opts.Select1 = false
|
|
|
|
case "-0", "--exit-0":
|
|
|
|
opts.Exit0 = true
|
|
|
|
case "+0", "--no-exit-0":
|
|
|
|
opts.Exit0 = false
|
2015-06-21 08:29:58 +00:00
|
|
|
case "--read0":
|
2015-06-08 06:36:21 +00:00
|
|
|
opts.ReadZero = true
|
2015-06-21 08:29:58 +00:00
|
|
|
case "--no-read0":
|
|
|
|
opts.ReadZero = false
|
2016-09-17 19:52:47 +00:00
|
|
|
case "--print0":
|
|
|
|
opts.Printer = func(str string) { fmt.Print(str, "\x00") }
|
2019-10-27 14:50:12 +00:00
|
|
|
opts.PrintSep = "\x00"
|
2016-09-17 19:52:47 +00:00
|
|
|
case "--no-print0":
|
|
|
|
opts.Printer = func(str string) { fmt.Println(str) }
|
2019-10-27 14:50:12 +00:00
|
|
|
opts.PrintSep = "\n"
|
2015-01-01 19:49:30 +00:00
|
|
|
case "--print-query":
|
|
|
|
opts.PrintQuery = true
|
|
|
|
case "--no-print-query":
|
|
|
|
opts.PrintQuery = false
|
|
|
|
case "--prompt":
|
|
|
|
opts.Prompt = nextString(allArgs, &i, "prompt string required")
|
2020-02-17 01:19:03 +00:00
|
|
|
case "--pointer":
|
2023-01-24 10:33:14 +00:00
|
|
|
opts.Pointer = firstLine(nextString(allArgs, &i, "pointer sign string required"))
|
2020-02-17 01:19:03 +00:00
|
|
|
validatePointer = true
|
|
|
|
case "--marker":
|
2023-01-24 10:33:14 +00:00
|
|
|
opts.Marker = firstLine(nextString(allArgs, &i, "selected sign string required"))
|
2020-02-17 01:19:03 +00:00
|
|
|
validateMarker = true
|
2015-02-13 03:25:19 +00:00
|
|
|
case "--sync":
|
|
|
|
opts.Sync = true
|
|
|
|
case "--no-sync":
|
|
|
|
opts.Sync = false
|
|
|
|
case "--async":
|
|
|
|
opts.Sync = false
|
2015-06-13 15:43:44 +00:00
|
|
|
case "--no-history":
|
|
|
|
opts.History = nil
|
|
|
|
case "--history":
|
|
|
|
setHistory(nextString(allArgs, &i, "history file path required"))
|
2015-06-18 16:03:25 +00:00
|
|
|
case "--history-size":
|
2015-06-13 15:43:44 +00:00
|
|
|
setHistoryMax(nextInt(allArgs, &i, "history max size required"))
|
2015-09-15 10:04:53 +00:00
|
|
|
case "--no-header":
|
2015-07-21 18:21:20 +00:00
|
|
|
opts.Header = []string{}
|
|
|
|
case "--no-header-lines":
|
|
|
|
opts.HeaderLines = 0
|
2015-09-15 10:04:53 +00:00
|
|
|
case "--header":
|
|
|
|
opts.Header = strLines(nextString(allArgs, &i, "header string required"))
|
2015-07-21 18:21:20 +00:00
|
|
|
case "--header-lines":
|
|
|
|
opts.HeaderLines = atoi(
|
|
|
|
nextString(allArgs, &i, "number of header lines required"))
|
2021-11-03 12:19:22 +00:00
|
|
|
case "--header-first":
|
|
|
|
opts.HeaderFirst = true
|
|
|
|
case "--no-header-first":
|
|
|
|
opts.HeaderFirst = false
|
2022-03-29 12:35:36 +00:00
|
|
|
case "--ellipsis":
|
|
|
|
opts.Ellipsis = nextString(allArgs, &i, "ellipsis string required")
|
2016-06-11 10:59:12 +00:00
|
|
|
case "--preview":
|
|
|
|
opts.Preview.command = nextString(allArgs, &i, "preview command required")
|
|
|
|
case "--no-preview":
|
|
|
|
opts.Preview.command = ""
|
|
|
|
case "--preview-window":
|
|
|
|
parsePreviewWindow(&opts.Preview,
|
2023-10-22 16:01:47 +00:00
|
|
|
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][,SIZE[%]][,border-BORDER_OPT][,wrap][,clear][,cycle][,hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]"))
|
2017-01-07 16:30:31 +00:00
|
|
|
case "--height":
|
2022-09-07 16:01:22 +00:00
|
|
|
opts.Height = parseHeight(nextString(allArgs, &i, "height required: [~]HEIGHT[%]"))
|
2017-01-09 16:04:36 +00:00
|
|
|
case "--min-height":
|
|
|
|
opts.MinHeight = nextInt(allArgs, &i, "height required: HEIGHT")
|
2017-01-07 16:30:31 +00:00
|
|
|
case "--no-height":
|
2022-09-07 16:01:22 +00:00
|
|
|
opts.Height = heightSpec{}
|
2015-07-26 14:02:04 +00:00
|
|
|
case "--no-margin":
|
|
|
|
opts.Margin = defaultMargin()
|
2020-11-09 11:34:08 +00:00
|
|
|
case "--no-padding":
|
|
|
|
opts.Padding = defaultMargin()
|
2017-02-04 12:51:22 +00:00
|
|
|
case "--no-border":
|
2020-03-05 11:15:15 +00:00
|
|
|
opts.BorderShape = tui.BorderNone
|
2017-02-04 12:51:22 +00:00
|
|
|
case "--border":
|
2020-03-05 11:15:15 +00:00
|
|
|
hasArg, arg := optionalNextString(allArgs, &i)
|
|
|
|
opts.BorderShape = parseBorder(arg, !hasArg)
|
2022-10-30 15:22:41 +00:00
|
|
|
case "--no-border-label":
|
|
|
|
opts.BorderLabel.label = ""
|
2022-10-29 15:12:01 +00:00
|
|
|
case "--border-label":
|
2022-10-30 15:22:41 +00:00
|
|
|
opts.BorderLabel.label = nextString(allArgs, &i, "label required")
|
2022-10-29 15:12:01 +00:00
|
|
|
case "--border-label-pos":
|
|
|
|
pos := nextString(allArgs, &i, "label position required (positive or negative integer or 'center')")
|
2022-10-30 15:22:41 +00:00
|
|
|
parseLabelPosition(&opts.BorderLabel, pos)
|
|
|
|
case "--no-preview-label":
|
|
|
|
opts.PreviewLabel.label = ""
|
2022-10-30 11:44:46 +00:00
|
|
|
case "--preview-label":
|
2022-10-30 15:22:41 +00:00
|
|
|
opts.PreviewLabel.label = nextString(allArgs, &i, "preview label required")
|
2022-10-30 11:44:46 +00:00
|
|
|
case "--preview-label-pos":
|
|
|
|
pos := nextString(allArgs, &i, "preview label position required (positive or negative integer or 'center')")
|
2022-10-30 15:22:41 +00:00
|
|
|
parseLabelPosition(&opts.PreviewLabel, pos)
|
2019-03-28 17:11:03 +00:00
|
|
|
case "--no-unicode":
|
|
|
|
opts.Unicode = false
|
|
|
|
case "--unicode":
|
|
|
|
opts.Unicode = true
|
2015-07-26 14:02:04 +00:00
|
|
|
case "--margin":
|
|
|
|
opts.Margin = parseMargin(
|
2020-11-09 11:34:08 +00:00
|
|
|
"margin",
|
2015-07-26 14:02:04 +00:00
|
|
|
nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
|
2020-11-09 11:34:08 +00:00
|
|
|
case "--padding":
|
|
|
|
opts.Padding = parseMargin(
|
|
|
|
"padding",
|
|
|
|
nextString(allArgs, &i, "padding required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
|
2015-11-30 08:35:03 +00:00
|
|
|
case "--tabstop":
|
|
|
|
opts.Tabstop = nextInt(allArgs, &i, "tab stop required")
|
2022-12-17 15:22:15 +00:00
|
|
|
case "--listen":
|
2023-03-19 06:42:47 +00:00
|
|
|
port := optionalNumeric(allArgs, &i, 0)
|
|
|
|
opts.ListenPort = &port
|
2022-12-17 15:22:15 +00:00
|
|
|
case "--no-listen":
|
2023-03-19 06:42:47 +00:00
|
|
|
opts.ListenPort = nil
|
2017-03-04 02:29:31 +00:00
|
|
|
case "--clear":
|
|
|
|
opts.ClearOnExit = true
|
|
|
|
case "--no-clear":
|
|
|
|
opts.ClearOnExit = false
|
2015-01-01 19:49:30 +00:00
|
|
|
case "--version":
|
|
|
|
opts.Version = true
|
2022-08-08 04:39:37 +00:00
|
|
|
case "--":
|
|
|
|
// Ignored
|
2015-01-01 19:49:30 +00:00
|
|
|
default:
|
2016-09-07 00:58:18 +00:00
|
|
|
if match, value := optString(arg, "--algo="); match {
|
|
|
|
opts.FuzzyAlgo = parseAlgo(value)
|
2022-08-28 13:16:57 +00:00
|
|
|
} else if match, value := optString(arg, "--scheme="); match {
|
|
|
|
opts.Scheme = strings.ToLower(value)
|
2016-09-07 00:58:18 +00:00
|
|
|
} else if match, value := optString(arg, "-q", "--query="); match {
|
2015-01-01 19:49:30 +00:00
|
|
|
opts.Query = value
|
2015-06-14 02:23:07 +00:00
|
|
|
} else if match, value := optString(arg, "-f", "--filter="); match {
|
2015-01-01 19:49:30 +00:00
|
|
|
opts.Filter = &value
|
2015-06-14 02:23:07 +00:00
|
|
|
} else if match, value := optString(arg, "-d", "--delimiter="); match {
|
2015-01-01 19:49:30 +00:00
|
|
|
opts.Delimiter = delimiterRegexp(value)
|
2020-03-05 11:15:15 +00:00
|
|
|
} else if match, value := optString(arg, "--border="); match {
|
|
|
|
opts.BorderShape = parseBorder(value, false)
|
2022-10-29 15:12:01 +00:00
|
|
|
} else if match, value := optString(arg, "--border-label="); match {
|
2022-10-30 15:22:41 +00:00
|
|
|
opts.BorderLabel.label = value
|
2022-10-29 15:12:01 +00:00
|
|
|
} else if match, value := optString(arg, "--border-label-pos="); match {
|
2022-10-30 15:22:41 +00:00
|
|
|
parseLabelPosition(&opts.BorderLabel, value)
|
2022-10-30 11:44:46 +00:00
|
|
|
} else if match, value := optString(arg, "--preview-label="); match {
|
2022-10-30 15:22:41 +00:00
|
|
|
opts.PreviewLabel.label = value
|
2022-10-30 11:44:46 +00:00
|
|
|
} else if match, value := optString(arg, "--preview-label-pos="); match {
|
2022-10-30 15:22:41 +00:00
|
|
|
parseLabelPosition(&opts.PreviewLabel, value)
|
2015-01-01 19:49:30 +00:00
|
|
|
} else if match, value := optString(arg, "--prompt="); match {
|
|
|
|
opts.Prompt = value
|
2020-02-17 01:19:03 +00:00
|
|
|
} else if match, value := optString(arg, "--pointer="); match {
|
2023-01-24 10:33:14 +00:00
|
|
|
opts.Pointer = firstLine(value)
|
2020-02-17 01:19:03 +00:00
|
|
|
validatePointer = true
|
|
|
|
} else if match, value := optString(arg, "--marker="); match {
|
2023-01-24 10:33:14 +00:00
|
|
|
opts.Marker = firstLine(value)
|
2020-02-17 01:19:03 +00:00
|
|
|
validateMarker = true
|
2015-06-14 02:23:07 +00:00
|
|
|
} else if match, value := optString(arg, "-n", "--nth="); match {
|
2015-01-01 19:49:30 +00:00
|
|
|
opts.Nth = splitNth(value)
|
|
|
|
} else if match, value := optString(arg, "--with-nth="); match {
|
|
|
|
opts.WithNth = splitNth(value)
|
2015-06-14 02:23:07 +00:00
|
|
|
} else if match, _ := optString(arg, "-s", "--sort="); match {
|
2015-01-01 19:49:30 +00:00
|
|
|
opts.Sort = 1 // Don't care
|
2019-11-02 11:44:21 +00:00
|
|
|
} else if match, value := optString(arg, "-m", "--multi="); match {
|
2019-11-02 03:55:26 +00:00
|
|
|
opts.Multi = atoi(value)
|
2017-01-07 16:30:31 +00:00
|
|
|
} else if match, value := optString(arg, "--height="); match {
|
|
|
|
opts.Height = parseHeight(value)
|
2017-01-09 16:04:36 +00:00
|
|
|
} else if match, value := optString(arg, "--min-height="); match {
|
|
|
|
opts.MinHeight = atoi(value)
|
2018-06-09 16:41:50 +00:00
|
|
|
} else if match, value := optString(arg, "--layout="); match {
|
|
|
|
opts.Layout = parseLayout(value)
|
2019-11-14 15:39:29 +00:00
|
|
|
} else if match, value := optString(arg, "--info="); match {
|
2023-01-24 08:40:08 +00:00
|
|
|
opts.InfoStyle, opts.InfoSep = parseInfoStyle(value)
|
2022-11-10 07:23:33 +00:00
|
|
|
} else if match, value := optString(arg, "--separator="); match {
|
|
|
|
opts.Separator = &value
|
2023-01-01 05:48:14 +00:00
|
|
|
} else if match, value := optString(arg, "--scrollbar="); match {
|
|
|
|
opts.Scrollbar = &value
|
2015-03-31 13:05:02 +00:00
|
|
|
} else if match, value := optString(arg, "--toggle-sort="); match {
|
2016-02-17 16:46:18 +00:00
|
|
|
parseToggleSort(opts.Keymap, value)
|
2015-03-28 17:59:32 +00:00
|
|
|
} else if match, value := optString(arg, "--expect="); match {
|
2017-08-26 17:19:39 +00:00
|
|
|
for k, v := range parseKeyChords(value, "key names required") {
|
|
|
|
opts.Expect[k] = v
|
|
|
|
}
|
2015-04-16 05:19:28 +00:00
|
|
|
} else if match, value := optString(arg, "--tiebreak="); match {
|
2016-01-12 18:07:42 +00:00
|
|
|
opts.Criteria = parseTiebreak(value)
|
2015-04-17 17:52:30 +00:00
|
|
|
} else if match, value := optString(arg, "--color="); match {
|
2015-05-31 07:46:54 +00:00
|
|
|
opts.Theme = parseTheme(opts.Theme, value)
|
2015-05-20 12:25:15 +00:00
|
|
|
} else if match, value := optString(arg, "--bind="); match {
|
2022-12-17 15:22:15 +00:00
|
|
|
parseKeymap(opts.Keymap, value, errorExit)
|
2015-06-13 15:43:44 +00:00
|
|
|
} else if match, value := optString(arg, "--history="); match {
|
|
|
|
setHistory(value)
|
2015-06-18 16:03:25 +00:00
|
|
|
} else if match, value := optString(arg, "--history-size="); match {
|
2015-06-13 15:43:44 +00:00
|
|
|
setHistoryMax(atoi(value))
|
2015-09-15 10:04:53 +00:00
|
|
|
} else if match, value := optString(arg, "--header="); match {
|
|
|
|
opts.Header = strLines(value)
|
2015-07-21 18:21:20 +00:00
|
|
|
} else if match, value := optString(arg, "--header-lines="); match {
|
|
|
|
opts.HeaderLines = atoi(value)
|
2022-03-29 12:35:36 +00:00
|
|
|
} else if match, value := optString(arg, "--ellipsis="); match {
|
|
|
|
opts.Ellipsis = value
|
2016-06-11 10:59:12 +00:00
|
|
|
} else if match, value := optString(arg, "--preview="); match {
|
|
|
|
opts.Preview.command = value
|
|
|
|
} else if match, value := optString(arg, "--preview-window="); match {
|
|
|
|
parsePreviewWindow(&opts.Preview, value)
|
2015-07-26 14:02:04 +00:00
|
|
|
} else if match, value := optString(arg, "--margin="); match {
|
2020-11-09 11:34:08 +00:00
|
|
|
opts.Margin = parseMargin("margin", value)
|
|
|
|
} else if match, value := optString(arg, "--padding="); match {
|
|
|
|
opts.Padding = parseMargin("padding", value)
|
2015-11-30 08:35:03 +00:00
|
|
|
} else if match, value := optString(arg, "--tabstop="); match {
|
|
|
|
opts.Tabstop = atoi(value)
|
2022-12-17 15:22:15 +00:00
|
|
|
} else if match, value := optString(arg, "--listen="); match {
|
2023-03-19 06:42:47 +00:00
|
|
|
port := atoi(value)
|
|
|
|
opts.ListenPort = &port
|
2016-03-01 18:06:21 +00:00
|
|
|
} else if match, value := optString(arg, "--hscroll-off="); match {
|
|
|
|
opts.HscrollOff = atoi(value)
|
2021-11-02 12:18:29 +00:00
|
|
|
} else if match, value := optString(arg, "--scroll-off="); match {
|
|
|
|
opts.ScrollOff = atoi(value)
|
2016-05-17 17:06:52 +00:00
|
|
|
} else if match, value := optString(arg, "--jump-labels="); match {
|
|
|
|
opts.JumpLabels = value
|
2020-02-16 06:45:59 +00:00
|
|
|
validateJumpLabels = true
|
2015-01-01 19:49:30 +00:00
|
|
|
} else {
|
|
|
|
errorExit("unknown option: " + arg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-22 21:26:00 +00:00
|
|
|
|
2015-07-22 04:45:38 +00:00
|
|
|
if opts.HeaderLines < 0 {
|
|
|
|
errorExit("header lines must be a non-negative integer")
|
|
|
|
}
|
|
|
|
|
2016-03-01 18:06:21 +00:00
|
|
|
if opts.HscrollOff < 0 {
|
|
|
|
errorExit("hscroll offset must be a non-negative integer")
|
|
|
|
}
|
|
|
|
|
2021-11-02 12:18:29 +00:00
|
|
|
if opts.ScrollOff < 0 {
|
|
|
|
errorExit("scroll offset must be a non-negative integer")
|
|
|
|
}
|
|
|
|
|
2015-11-30 08:35:03 +00:00
|
|
|
if opts.Tabstop < 1 {
|
|
|
|
errorExit("tab stop must be a positive integer")
|
|
|
|
}
|
2016-05-17 17:06:52 +00:00
|
|
|
|
2023-03-19 06:42:47 +00:00
|
|
|
if opts.ListenPort != nil && (*opts.ListenPort < 0 || *opts.ListenPort > 65535) {
|
2022-12-17 15:22:15 +00:00
|
|
|
errorExit("invalid listen port")
|
|
|
|
}
|
|
|
|
|
2016-05-17 17:06:52 +00:00
|
|
|
if len(opts.JumpLabels) == 0 {
|
|
|
|
errorExit("empty jump labels")
|
|
|
|
}
|
2016-05-18 16:46:22 +00:00
|
|
|
|
|
|
|
if validateJumpLabels {
|
|
|
|
for _, r := range opts.JumpLabels {
|
|
|
|
if r < 32 || r > 126 {
|
|
|
|
errorExit("non-ascii jump labels are not allowed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-17 01:19:03 +00:00
|
|
|
|
|
|
|
if validatePointer {
|
|
|
|
if err := validateSign(opts.Pointer, "pointer"); err != nil {
|
|
|
|
errorExit(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if validateMarker {
|
|
|
|
if err := validateSign(opts.Marker, "marker"); err != nil {
|
|
|
|
errorExit(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func validateSign(sign string, signOptName string) error {
|
|
|
|
if sign == "" {
|
|
|
|
return fmt.Errorf("%v cannot be empty", signOptName)
|
|
|
|
}
|
2021-05-14 02:43:32 +00:00
|
|
|
if runewidth.StringWidth(sign) > 2 {
|
|
|
|
return fmt.Errorf("%v display width should be up to 2", signOptName)
|
2020-02-17 01:19:03 +00:00
|
|
|
}
|
|
|
|
return nil
|
2016-02-17 16:46:18 +00:00
|
|
|
}
|
2015-11-30 08:35:03 +00:00
|
|
|
|
2016-02-17 16:46:18 +00:00
|
|
|
func postProcessOptions(opts *Options) {
|
2021-09-16 00:54:56 +00:00
|
|
|
if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
|
2019-02-05 06:51:39 +00:00
|
|
|
errorExit("--height option is currently not supported on this platform")
|
2017-10-15 10:02:05 +00:00
|
|
|
}
|
2023-01-01 05:48:14 +00:00
|
|
|
|
2023-05-16 14:53:10 +00:00
|
|
|
if opts.Scrollbar != nil {
|
|
|
|
runes := []rune(*opts.Scrollbar)
|
|
|
|
if len(runes) > 2 {
|
|
|
|
errorExit("--scrollbar should be given one or two characters")
|
|
|
|
}
|
|
|
|
for _, r := range runes {
|
|
|
|
if runewidth.RuneWidth(r) != 1 {
|
|
|
|
errorExit("scrollbar display width should be 1")
|
|
|
|
}
|
|
|
|
}
|
2023-01-01 05:48:14 +00:00
|
|
|
}
|
|
|
|
|
2016-02-17 16:46:18 +00:00
|
|
|
// Default actions for CTRL-N / CTRL-P when --history is set
|
2015-06-13 15:43:44 +00:00
|
|
|
if opts.History != nil {
|
2020-12-29 16:59:18 +00:00
|
|
|
if _, prs := opts.Keymap[tui.CtrlP.AsEvent()]; !prs {
|
2022-12-10 13:42:56 +00:00
|
|
|
opts.Keymap[tui.CtrlP.AsEvent()] = toActions(actPrevHistory)
|
2015-06-13 15:43:44 +00:00
|
|
|
}
|
2020-12-29 16:59:18 +00:00
|
|
|
if _, prs := opts.Keymap[tui.CtrlN.AsEvent()]; !prs {
|
|
|
|
opts.Keymap[tui.CtrlN.AsEvent()] = toActions(actNextHistory)
|
2015-06-13 15:43:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-17 16:46:18 +00:00
|
|
|
// Extend the default key map
|
|
|
|
keymap := defaultKeymap()
|
2017-01-21 17:32:49 +00:00
|
|
|
for key, actions := range opts.Keymap {
|
2023-05-05 06:08:08 +00:00
|
|
|
reordered := []*action{}
|
2017-01-21 17:32:49 +00:00
|
|
|
for _, act := range actions {
|
2021-11-30 14:37:48 +00:00
|
|
|
switch act.t {
|
|
|
|
case actToggleSort:
|
|
|
|
// To display "+S"/"-S" on info line
|
2017-01-21 17:32:49 +00:00
|
|
|
opts.ToggleSort = true
|
2023-05-05 06:08:08 +00:00
|
|
|
case actTogglePreview, actShowPreview, actHidePreview, actChangePreviewWindow:
|
|
|
|
reordered = append(reordered, act)
|
2021-11-30 14:37:48 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-07 16:01:22 +00:00
|
|
|
|
2023-05-05 06:08:08 +00:00
|
|
|
// Re-organize actions so that we put actions that change the preview window first in the list.
|
2021-11-30 14:37:48 +00:00
|
|
|
// * change-preview-window(up,+10)+preview(sleep 3; cat {})+change-preview-window(up,+20)
|
2023-05-05 06:08:08 +00:00
|
|
|
// -> change-preview-window(up,+10)+change-preview-window(up,+20)+preview(sleep 3; cat {})
|
|
|
|
if len(reordered) > 0 {
|
2021-11-30 14:37:48 +00:00
|
|
|
for _, act := range actions {
|
2023-05-05 06:08:08 +00:00
|
|
|
switch act.t {
|
|
|
|
case actTogglePreview, actShowPreview, actHidePreview, actChangePreviewWindow:
|
|
|
|
default:
|
2021-11-30 14:37:48 +00:00
|
|
|
reordered = append(reordered, act)
|
|
|
|
}
|
2017-01-21 17:32:49 +00:00
|
|
|
}
|
2021-11-30 14:37:48 +00:00
|
|
|
actions = reordered
|
2016-02-17 16:46:18 +00:00
|
|
|
}
|
2017-01-21 17:32:49 +00:00
|
|
|
keymap[key] = actions
|
2015-06-13 15:43:44 +00:00
|
|
|
}
|
2016-02-17 16:46:18 +00:00
|
|
|
opts.Keymap = keymap
|
2015-06-13 15:43:44 +00:00
|
|
|
|
2022-11-29 11:24:18 +00:00
|
|
|
// If 'double-click' is left unbound, bind it to the action bound to 'enter'
|
|
|
|
if _, prs := opts.Keymap[tui.DoubleClick.AsEvent()]; !prs {
|
|
|
|
opts.Keymap[tui.DoubleClick.AsEvent()] = opts.Keymap[tui.CtrlM.AsEvent()]
|
|
|
|
}
|
|
|
|
|
2022-09-07 16:01:22 +00:00
|
|
|
if opts.Height.auto {
|
|
|
|
for _, s := range []sizeSpec{opts.Margin[0], opts.Margin[2]} {
|
|
|
|
if s.percent {
|
|
|
|
errorExit("adaptive height is not compatible with top/bottom percent margin")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, s := range []sizeSpec{opts.Padding[0], opts.Padding[2]} {
|
|
|
|
if s.percent {
|
|
|
|
errorExit("adaptive height is not compatible with top/bottom percent padding")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 21:26:00 +00:00
|
|
|
// If we're not using extended search mode, --nth option becomes irrelevant
|
|
|
|
// if it contains the whole range
|
2015-11-03 13:49:32 +00:00
|
|
|
if !opts.Extended || len(opts.Nth) == 1 {
|
2015-01-22 21:26:00 +00:00
|
|
|
for _, r := range opts.Nth {
|
|
|
|
if r.begin == rangeEllipsis && r.end == rangeEllipsis {
|
|
|
|
opts.Nth = make([]Range, 0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-25 10:29:37 +00:00
|
|
|
|
|
|
|
if opts.Bold {
|
|
|
|
theme := opts.Theme
|
|
|
|
boldify := func(c tui.ColorAttr) tui.ColorAttr {
|
|
|
|
dup := c
|
2023-03-26 14:39:05 +00:00
|
|
|
if (c.Attr & tui.AttrRegular) == 0 {
|
2020-10-25 10:29:37 +00:00
|
|
|
dup.Attr |= tui.Bold
|
|
|
|
}
|
|
|
|
return dup
|
|
|
|
}
|
|
|
|
theme.Current = boldify(theme.Current)
|
|
|
|
theme.CurrentMatch = boldify(theme.CurrentMatch)
|
|
|
|
theme.Prompt = boldify(theme.Prompt)
|
|
|
|
theme.Input = boldify(theme.Input)
|
|
|
|
theme.Cursor = boldify(theme.Cursor)
|
|
|
|
theme.Spinner = boldify(theme.Spinner)
|
|
|
|
}
|
2022-08-28 13:16:57 +00:00
|
|
|
|
|
|
|
if opts.Scheme != "default" {
|
|
|
|
processScheme(opts)
|
|
|
|
}
|
2015-01-01 19:49:30 +00:00
|
|
|
}
|
|
|
|
|
2022-08-01 01:42:16 +00:00
|
|
|
func expectsArbitraryString(opt string) bool {
|
|
|
|
switch opt {
|
|
|
|
case "-q", "--query", "-f", "--filter", "--header", "--prompt":
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2015-01-11 18:01:24 +00:00
|
|
|
// ParseOptions parses command-line options
|
2015-01-01 19:49:30 +00:00
|
|
|
func ParseOptions() *Options {
|
2015-01-11 18:01:24 +00:00
|
|
|
opts := defaultOptions()
|
2015-01-01 19:49:30 +00:00
|
|
|
|
2022-08-01 01:42:16 +00:00
|
|
|
for idx, arg := range os.Args[1:] {
|
|
|
|
if arg == "--version" && (idx == 0 || idx > 0 && !expectsArbitraryString(os.Args[idx])) {
|
2022-07-21 13:36:24 +00:00
|
|
|
opts.Version = true
|
|
|
|
return opts
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-01 19:49:30 +00:00
|
|
|
// Options from Env var
|
|
|
|
words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS"))
|
2016-02-17 16:46:18 +00:00
|
|
|
if len(words) > 0 {
|
|
|
|
parseOptions(opts, words)
|
|
|
|
}
|
2015-01-01 19:49:30 +00:00
|
|
|
|
|
|
|
// Options from command-line arguments
|
|
|
|
parseOptions(opts, os.Args[1:])
|
2016-02-17 16:46:18 +00:00
|
|
|
|
|
|
|
postProcessOptions(opts)
|
2015-01-01 19:49:30 +00:00
|
|
|
return opts
|
|
|
|
}
|