doing 2.0.19 → 2.0.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile.lock +15 -5
- data/README.md +1 -1
- data/bin/doing +2 -18
- data/doing.gemspec +5 -4
- data/doing.rdoc +2 -2
- data/generate_completions.sh +3 -3
- data/lib/doing/cli_status.rb +6 -2
- data/lib/doing/completion/bash_completion.rb +185 -0
- data/lib/doing/completion/fish_completion.rb +175 -0
- data/lib/doing/completion/string.rb +17 -0
- data/lib/doing/completion/zsh_completion.rb +140 -0
- data/lib/doing/completion.rb +39 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +19 -5
- data/lib/doing.rb +1 -1
- data/lib/helpers/fzf/.goreleaser.yml +119 -0
- data/lib/helpers/fzf/.rubocop.yml +28 -0
- data/lib/helpers/fzf/ADVANCED.md +565 -0
- data/lib/helpers/fzf/BUILD.md +49 -0
- data/lib/helpers/fzf/CHANGELOG.md +1193 -0
- data/lib/helpers/fzf/Dockerfile +11 -0
- data/lib/helpers/fzf/LICENSE +21 -0
- data/lib/helpers/fzf/Makefile +166 -0
- data/lib/helpers/fzf/README-VIM.md +486 -0
- data/lib/helpers/fzf/README.md +712 -0
- data/lib/helpers/fzf/bin/fzf-tmux +233 -0
- data/lib/helpers/fzf/doc/fzf.txt +512 -0
- data/lib/helpers/fzf/go.mod +17 -0
- data/lib/helpers/fzf/go.sum +31 -0
- data/lib/helpers/fzf/install +382 -0
- data/lib/helpers/fzf/install.ps1 +65 -0
- data/lib/helpers/fzf/main.go +14 -0
- data/lib/helpers/fzf/man/man1/fzf-tmux.1 +68 -0
- data/lib/helpers/fzf/man/man1/fzf.1 +1001 -0
- data/lib/helpers/fzf/plugin/fzf.vim +1048 -0
- data/lib/helpers/fzf/shell/completion.bash +381 -0
- data/lib/helpers/fzf/shell/completion.zsh +329 -0
- data/lib/helpers/fzf/shell/key-bindings.bash +96 -0
- data/lib/helpers/fzf/shell/key-bindings.fish +172 -0
- data/lib/helpers/fzf/shell/key-bindings.zsh +114 -0
- data/lib/helpers/fzf/src/LICENSE +21 -0
- data/lib/helpers/fzf/src/algo/algo.go +884 -0
- data/lib/helpers/fzf/src/algo/algo_test.go +197 -0
- data/lib/helpers/fzf/src/algo/normalize.go +492 -0
- data/lib/helpers/fzf/src/ansi.go +409 -0
- data/lib/helpers/fzf/src/ansi_test.go +427 -0
- data/lib/helpers/fzf/src/cache.go +81 -0
- data/lib/helpers/fzf/src/cache_test.go +39 -0
- data/lib/helpers/fzf/src/chunklist.go +89 -0
- data/lib/helpers/fzf/src/chunklist_test.go +80 -0
- data/lib/helpers/fzf/src/constants.go +85 -0
- data/lib/helpers/fzf/src/core.go +351 -0
- data/lib/helpers/fzf/src/history.go +96 -0
- data/lib/helpers/fzf/src/history_test.go +68 -0
- data/lib/helpers/fzf/src/item.go +44 -0
- data/lib/helpers/fzf/src/item_test.go +23 -0
- data/lib/helpers/fzf/src/matcher.go +235 -0
- data/lib/helpers/fzf/src/merger.go +120 -0
- data/lib/helpers/fzf/src/merger_test.go +88 -0
- data/lib/helpers/fzf/src/options.go +1691 -0
- data/lib/helpers/fzf/src/options_test.go +457 -0
- data/lib/helpers/fzf/src/pattern.go +425 -0
- data/lib/helpers/fzf/src/pattern_test.go +209 -0
- data/lib/helpers/fzf/src/protector/protector.go +8 -0
- data/lib/helpers/fzf/src/protector/protector_openbsd.go +10 -0
- data/lib/helpers/fzf/src/reader.go +201 -0
- data/lib/helpers/fzf/src/reader_test.go +63 -0
- data/lib/helpers/fzf/src/result.go +243 -0
- data/lib/helpers/fzf/src/result_others.go +16 -0
- data/lib/helpers/fzf/src/result_test.go +159 -0
- data/lib/helpers/fzf/src/result_x86.go +16 -0
- data/lib/helpers/fzf/src/terminal.go +2832 -0
- data/lib/helpers/fzf/src/terminal_test.go +638 -0
- data/lib/helpers/fzf/src/terminal_unix.go +26 -0
- data/lib/helpers/fzf/src/terminal_windows.go +45 -0
- data/lib/helpers/fzf/src/tokenizer.go +253 -0
- data/lib/helpers/fzf/src/tokenizer_test.go +112 -0
- data/lib/helpers/fzf/src/tui/dummy.go +46 -0
- data/lib/helpers/fzf/src/tui/light.go +987 -0
- data/lib/helpers/fzf/src/tui/light_unix.go +110 -0
- data/lib/helpers/fzf/src/tui/light_windows.go +145 -0
- data/lib/helpers/fzf/src/tui/tcell.go +721 -0
- data/lib/helpers/fzf/src/tui/tcell_test.go +392 -0
- data/lib/helpers/fzf/src/tui/ttyname_unix.go +47 -0
- data/lib/helpers/fzf/src/tui/ttyname_windows.go +14 -0
- data/lib/helpers/fzf/src/tui/tui.go +625 -0
- data/lib/helpers/fzf/src/tui/tui_test.go +20 -0
- data/lib/helpers/fzf/src/util/atomicbool.go +34 -0
- data/lib/helpers/fzf/src/util/atomicbool_test.go +17 -0
- data/lib/helpers/fzf/src/util/chars.go +198 -0
- data/lib/helpers/fzf/src/util/chars_test.go +46 -0
- data/lib/helpers/fzf/src/util/eventbox.go +96 -0
- data/lib/helpers/fzf/src/util/eventbox_test.go +61 -0
- data/lib/helpers/fzf/src/util/slab.go +12 -0
- data/lib/helpers/fzf/src/util/util.go +138 -0
- data/lib/helpers/fzf/src/util/util_test.go +40 -0
- data/lib/helpers/fzf/src/util/util_unix.go +47 -0
- data/lib/helpers/fzf/src/util/util_windows.go +83 -0
- data/lib/helpers/fzf/test/fzf.vader +175 -0
- data/lib/helpers/fzf/test/test_go.rb +2626 -0
- data/lib/helpers/fzf/uninstall +117 -0
- data/scripts/generate_bash_completions.rb +6 -12
- data/scripts/generate_fish_completions.rb +7 -16
- data/scripts/generate_zsh_completions.rb +6 -15
- metadata +144 -9
@@ -0,0 +1,1691 @@
|
|
1
|
+
package fzf
|
2
|
+
|
3
|
+
import (
|
4
|
+
"fmt"
|
5
|
+
"os"
|
6
|
+
"regexp"
|
7
|
+
"strconv"
|
8
|
+
"strings"
|
9
|
+
"unicode"
|
10
|
+
|
11
|
+
"github.com/junegunn/fzf/src/algo"
|
12
|
+
"github.com/junegunn/fzf/src/tui"
|
13
|
+
|
14
|
+
"github.com/mattn/go-runewidth"
|
15
|
+
"github.com/mattn/go-shellwords"
|
16
|
+
)
|
17
|
+
|
18
|
+
const usage = `usage: fzf [options]
|
19
|
+
|
20
|
+
Search
|
21
|
+
-x, --extended Extended-search mode
|
22
|
+
(enabled by default; +x or --no-extended to disable)
|
23
|
+
-e, --exact Enable Exact-match
|
24
|
+
--algo=TYPE Fuzzy matching algorithm: [v1|v2] (default: v2)
|
25
|
+
-i Case-insensitive match (default: smart-case match)
|
26
|
+
+i Case-sensitive match
|
27
|
+
--literal Do not normalize latin script letters before matching
|
28
|
+
-n, --nth=N[,..] Comma-separated list of field index expressions
|
29
|
+
for limiting search scope. Each can be a non-zero
|
30
|
+
integer or a range expression ([BEGIN]..[END]).
|
31
|
+
--with-nth=N[,..] Transform the presentation of each line using
|
32
|
+
field index expressions
|
33
|
+
-d, --delimiter=STR Field delimiter regex (default: AWK-style)
|
34
|
+
+s, --no-sort Do not sort the result
|
35
|
+
--tac Reverse the order of the input
|
36
|
+
--disabled Do not perform search
|
37
|
+
--tiebreak=CRI[,..] Comma-separated list of sort criteria to apply
|
38
|
+
when the scores are tied [length|begin|end|index]
|
39
|
+
(default: length)
|
40
|
+
|
41
|
+
Interface
|
42
|
+
-m, --multi[=MAX] Enable multi-select with tab/shift-tab
|
43
|
+
--no-mouse Disable mouse
|
44
|
+
--bind=KEYBINDS Custom key bindings. Refer to the man page.
|
45
|
+
--cycle Enable cyclic scroll
|
46
|
+
--keep-right Keep the right end of the line visible on overflow
|
47
|
+
--scroll-off=LINES Number of screen lines to keep above or below when
|
48
|
+
scrolling to the top or to the bottom (default: 0)
|
49
|
+
--no-hscroll Disable horizontal scroll
|
50
|
+
--hscroll-off=COLS Number of screen columns to keep to the right of the
|
51
|
+
highlighted substring (default: 10)
|
52
|
+
--filepath-word Make word-wise movements respect path separators
|
53
|
+
--jump-labels=CHARS Label characters for jump and jump-accept
|
54
|
+
|
55
|
+
Layout
|
56
|
+
--height=HEIGHT[%] Display fzf window below the cursor with the given
|
57
|
+
height instead of using fullscreen
|
58
|
+
--min-height=HEIGHT Minimum height when --height is given in percent
|
59
|
+
(default: 10)
|
60
|
+
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
61
|
+
--border[=STYLE] Draw border around the finder
|
62
|
+
[rounded|sharp|horizontal|vertical|
|
63
|
+
top|bottom|left|right|none] (default: rounded)
|
64
|
+
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
65
|
+
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
|
66
|
+
--info=STYLE Finder info style [default|inline|hidden]
|
67
|
+
--prompt=STR Input prompt (default: '> ')
|
68
|
+
--pointer=STR Pointer to the current line (default: '>')
|
69
|
+
--marker=STR Multi-select marker (default: '>')
|
70
|
+
--header=STR String to print as header
|
71
|
+
--header-lines=N The first N lines of the input are treated as header
|
72
|
+
--header-first Print header before the prompt line
|
73
|
+
|
74
|
+
Display
|
75
|
+
--ansi Enable processing of ANSI color codes
|
76
|
+
--tabstop=SPACES Number of spaces for a tab character (default: 8)
|
77
|
+
--color=COLSPEC Base scheme (dark|light|16|bw) and/or custom colors
|
78
|
+
--no-bold Do not use bold text
|
79
|
+
|
80
|
+
History
|
81
|
+
--history=FILE History file
|
82
|
+
--history-size=N Maximum number of history entries (default: 1000)
|
83
|
+
|
84
|
+
Preview
|
85
|
+
--preview=COMMAND Command to preview highlighted line ({})
|
86
|
+
--preview-window=OPT Preview window layout (default: right:50%)
|
87
|
+
[up|down|left|right][,SIZE[%]]
|
88
|
+
[,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
|
89
|
+
[,border-BORDER_OPT]
|
90
|
+
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
|
91
|
+
[,default]
|
92
|
+
|
93
|
+
Scripting
|
94
|
+
-q, --query=STR Start the finder with the given query
|
95
|
+
-1, --select-1 Automatically select the only match
|
96
|
+
-0, --exit-0 Exit immediately when there's no match
|
97
|
+
-f, --filter=STR Filter mode. Do not start interactive finder.
|
98
|
+
--print-query Print query as the first line
|
99
|
+
--expect=KEYS Comma-separated list of keys to complete fzf
|
100
|
+
--read0 Read input delimited by ASCII NUL characters
|
101
|
+
--print0 Print output delimited by ASCII NUL characters
|
102
|
+
--sync Synchronous search for multi-staged filtering
|
103
|
+
--version Display version information and exit
|
104
|
+
|
105
|
+
Environment variables
|
106
|
+
FZF_DEFAULT_COMMAND Default command to use when input is tty
|
107
|
+
FZF_DEFAULT_OPTS Default options
|
108
|
+
(e.g. '--layout=reverse --inline-info')
|
109
|
+
|
110
|
+
`
|
111
|
+
|
112
|
+
// Case denotes case-sensitivity of search
|
113
|
+
type Case int
|
114
|
+
|
115
|
+
// Case-sensitivities
|
116
|
+
const (
|
117
|
+
CaseSmart Case = iota
|
118
|
+
CaseIgnore
|
119
|
+
CaseRespect
|
120
|
+
)
|
121
|
+
|
122
|
+
// Sort criteria
|
123
|
+
type criterion int
|
124
|
+
|
125
|
+
const (
|
126
|
+
byScore criterion = iota
|
127
|
+
byLength
|
128
|
+
byBegin
|
129
|
+
byEnd
|
130
|
+
)
|
131
|
+
|
132
|
+
type sizeSpec struct {
|
133
|
+
size float64
|
134
|
+
percent bool
|
135
|
+
}
|
136
|
+
|
137
|
+
func defaultMargin() [4]sizeSpec {
|
138
|
+
return [4]sizeSpec{}
|
139
|
+
}
|
140
|
+
|
141
|
+
type windowPosition int
|
142
|
+
|
143
|
+
const (
|
144
|
+
posUp windowPosition = iota
|
145
|
+
posDown
|
146
|
+
posLeft
|
147
|
+
posRight
|
148
|
+
)
|
149
|
+
|
150
|
+
type layoutType int
|
151
|
+
|
152
|
+
const (
|
153
|
+
layoutDefault layoutType = iota
|
154
|
+
layoutReverse
|
155
|
+
layoutReverseList
|
156
|
+
)
|
157
|
+
|
158
|
+
type infoStyle int
|
159
|
+
|
160
|
+
const (
|
161
|
+
infoDefault infoStyle = iota
|
162
|
+
infoInline
|
163
|
+
infoHidden
|
164
|
+
)
|
165
|
+
|
166
|
+
type previewOpts struct {
|
167
|
+
command string
|
168
|
+
position windowPosition
|
169
|
+
size sizeSpec
|
170
|
+
scroll string
|
171
|
+
hidden bool
|
172
|
+
wrap bool
|
173
|
+
cycle bool
|
174
|
+
follow bool
|
175
|
+
border tui.BorderShape
|
176
|
+
headerLines int
|
177
|
+
}
|
178
|
+
|
179
|
+
// Options stores the values of command-line options
|
180
|
+
type Options struct {
|
181
|
+
Fuzzy bool
|
182
|
+
FuzzyAlgo algo.Algo
|
183
|
+
Extended bool
|
184
|
+
Phony bool
|
185
|
+
Case Case
|
186
|
+
Normalize bool
|
187
|
+
Nth []Range
|
188
|
+
WithNth []Range
|
189
|
+
Delimiter Delimiter
|
190
|
+
Sort int
|
191
|
+
Tac bool
|
192
|
+
Criteria []criterion
|
193
|
+
Multi int
|
194
|
+
Ansi bool
|
195
|
+
Mouse bool
|
196
|
+
Theme *tui.ColorTheme
|
197
|
+
Black bool
|
198
|
+
Bold bool
|
199
|
+
Height sizeSpec
|
200
|
+
MinHeight int
|
201
|
+
Layout layoutType
|
202
|
+
Cycle bool
|
203
|
+
KeepRight bool
|
204
|
+
Hscroll bool
|
205
|
+
HscrollOff int
|
206
|
+
ScrollOff int
|
207
|
+
FileWord bool
|
208
|
+
InfoStyle infoStyle
|
209
|
+
JumpLabels string
|
210
|
+
Prompt string
|
211
|
+
Pointer string
|
212
|
+
Marker string
|
213
|
+
Query string
|
214
|
+
Select1 bool
|
215
|
+
Exit0 bool
|
216
|
+
Filter *string
|
217
|
+
ToggleSort bool
|
218
|
+
Expect map[tui.Event]string
|
219
|
+
Keymap map[tui.Event][]action
|
220
|
+
Preview previewOpts
|
221
|
+
PrintQuery bool
|
222
|
+
ReadZero bool
|
223
|
+
Printer func(string)
|
224
|
+
PrintSep string
|
225
|
+
Sync bool
|
226
|
+
History *History
|
227
|
+
Header []string
|
228
|
+
HeaderLines int
|
229
|
+
HeaderFirst bool
|
230
|
+
Margin [4]sizeSpec
|
231
|
+
Padding [4]sizeSpec
|
232
|
+
BorderShape tui.BorderShape
|
233
|
+
Unicode bool
|
234
|
+
Tabstop int
|
235
|
+
ClearOnExit bool
|
236
|
+
Version bool
|
237
|
+
}
|
238
|
+
|
239
|
+
func defaultPreviewOpts(command string) previewOpts {
|
240
|
+
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.BorderRounded, 0}
|
241
|
+
}
|
242
|
+
|
243
|
+
func defaultOptions() *Options {
|
244
|
+
return &Options{
|
245
|
+
Fuzzy: true,
|
246
|
+
FuzzyAlgo: algo.FuzzyMatchV2,
|
247
|
+
Extended: true,
|
248
|
+
Phony: false,
|
249
|
+
Case: CaseSmart,
|
250
|
+
Normalize: true,
|
251
|
+
Nth: make([]Range, 0),
|
252
|
+
WithNth: make([]Range, 0),
|
253
|
+
Delimiter: Delimiter{},
|
254
|
+
Sort: 1000,
|
255
|
+
Tac: false,
|
256
|
+
Criteria: []criterion{byScore, byLength},
|
257
|
+
Multi: 0,
|
258
|
+
Ansi: false,
|
259
|
+
Mouse: true,
|
260
|
+
Theme: tui.EmptyTheme(),
|
261
|
+
Black: false,
|
262
|
+
Bold: true,
|
263
|
+
MinHeight: 10,
|
264
|
+
Layout: layoutDefault,
|
265
|
+
Cycle: false,
|
266
|
+
KeepRight: false,
|
267
|
+
Hscroll: true,
|
268
|
+
HscrollOff: 10,
|
269
|
+
ScrollOff: 0,
|
270
|
+
FileWord: false,
|
271
|
+
InfoStyle: infoDefault,
|
272
|
+
JumpLabels: defaultJumpLabels,
|
273
|
+
Prompt: "> ",
|
274
|
+
Pointer: ">",
|
275
|
+
Marker: ">",
|
276
|
+
Query: "",
|
277
|
+
Select1: false,
|
278
|
+
Exit0: false,
|
279
|
+
Filter: nil,
|
280
|
+
ToggleSort: false,
|
281
|
+
Expect: make(map[tui.Event]string),
|
282
|
+
Keymap: make(map[tui.Event][]action),
|
283
|
+
Preview: defaultPreviewOpts(""),
|
284
|
+
PrintQuery: false,
|
285
|
+
ReadZero: false,
|
286
|
+
Printer: func(str string) { fmt.Println(str) },
|
287
|
+
PrintSep: "\n",
|
288
|
+
Sync: false,
|
289
|
+
History: nil,
|
290
|
+
Header: make([]string, 0),
|
291
|
+
HeaderLines: 0,
|
292
|
+
HeaderFirst: false,
|
293
|
+
Margin: defaultMargin(),
|
294
|
+
Padding: defaultMargin(),
|
295
|
+
Unicode: true,
|
296
|
+
Tabstop: 8,
|
297
|
+
ClearOnExit: true,
|
298
|
+
Version: false}
|
299
|
+
}
|
300
|
+
|
301
|
+
func help(code int) {
|
302
|
+
os.Stdout.WriteString(usage)
|
303
|
+
os.Exit(code)
|
304
|
+
}
|
305
|
+
|
306
|
+
func errorExit(msg string) {
|
307
|
+
os.Stderr.WriteString(msg + "\n")
|
308
|
+
os.Exit(exitError)
|
309
|
+
}
|
310
|
+
|
311
|
+
func optString(arg string, prefixes ...string) (bool, string) {
|
312
|
+
for _, prefix := range prefixes {
|
313
|
+
if strings.HasPrefix(arg, prefix) {
|
314
|
+
return true, arg[len(prefix):]
|
315
|
+
}
|
316
|
+
}
|
317
|
+
return false, ""
|
318
|
+
}
|
319
|
+
|
320
|
+
func nextString(args []string, i *int, message string) string {
|
321
|
+
if len(args) > *i+1 {
|
322
|
+
*i++
|
323
|
+
} else {
|
324
|
+
errorExit(message)
|
325
|
+
}
|
326
|
+
return args[*i]
|
327
|
+
}
|
328
|
+
|
329
|
+
func optionalNextString(args []string, i *int) (bool, string) {
|
330
|
+
if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") && !strings.HasPrefix(args[*i+1], "+") {
|
331
|
+
*i++
|
332
|
+
return true, args[*i]
|
333
|
+
}
|
334
|
+
return false, ""
|
335
|
+
}
|
336
|
+
|
337
|
+
func atoi(str string) int {
|
338
|
+
num, err := strconv.Atoi(str)
|
339
|
+
if err != nil {
|
340
|
+
errorExit("not a valid integer: " + str)
|
341
|
+
}
|
342
|
+
return num
|
343
|
+
}
|
344
|
+
|
345
|
+
func atof(str string) float64 {
|
346
|
+
num, err := strconv.ParseFloat(str, 64)
|
347
|
+
if err != nil {
|
348
|
+
errorExit("not a valid number: " + str)
|
349
|
+
}
|
350
|
+
return num
|
351
|
+
}
|
352
|
+
|
353
|
+
func nextInt(args []string, i *int, message string) int {
|
354
|
+
if len(args) > *i+1 {
|
355
|
+
*i++
|
356
|
+
} else {
|
357
|
+
errorExit(message)
|
358
|
+
}
|
359
|
+
return atoi(args[*i])
|
360
|
+
}
|
361
|
+
|
362
|
+
func optionalNumeric(args []string, i *int, defaultValue int) int {
|
363
|
+
if len(args) > *i+1 {
|
364
|
+
if strings.IndexAny(args[*i+1], "0123456789") == 0 {
|
365
|
+
*i++
|
366
|
+
return atoi(args[*i])
|
367
|
+
}
|
368
|
+
}
|
369
|
+
return defaultValue
|
370
|
+
}
|
371
|
+
|
372
|
+
func splitNth(str string) []Range {
|
373
|
+
if match, _ := regexp.MatchString("^[0-9,-.]+$", str); !match {
|
374
|
+
errorExit("invalid format: " + str)
|
375
|
+
}
|
376
|
+
|
377
|
+
tokens := strings.Split(str, ",")
|
378
|
+
ranges := make([]Range, len(tokens))
|
379
|
+
for idx, s := range tokens {
|
380
|
+
r, ok := ParseRange(&s)
|
381
|
+
if !ok {
|
382
|
+
errorExit("invalid format: " + str)
|
383
|
+
}
|
384
|
+
ranges[idx] = r
|
385
|
+
}
|
386
|
+
return ranges
|
387
|
+
}
|
388
|
+
|
389
|
+
func delimiterRegexp(str string) Delimiter {
|
390
|
+
// Special handling of \t
|
391
|
+
str = strings.Replace(str, "\\t", "\t", -1)
|
392
|
+
|
393
|
+
// 1. Pattern does not contain any special character
|
394
|
+
if regexp.QuoteMeta(str) == str {
|
395
|
+
return Delimiter{str: &str}
|
396
|
+
}
|
397
|
+
|
398
|
+
rx, e := regexp.Compile(str)
|
399
|
+
// 2. Pattern is not a valid regular expression
|
400
|
+
if e != nil {
|
401
|
+
return Delimiter{str: &str}
|
402
|
+
}
|
403
|
+
|
404
|
+
// 3. Pattern as regular expression. Slow.
|
405
|
+
return Delimiter{regex: rx}
|
406
|
+
}
|
407
|
+
|
408
|
+
func isAlphabet(char uint8) bool {
|
409
|
+
return char >= 'a' && char <= 'z'
|
410
|
+
}
|
411
|
+
|
412
|
+
func isNumeric(char uint8) bool {
|
413
|
+
return char >= '0' && char <= '9'
|
414
|
+
}
|
415
|
+
|
416
|
+
func parseAlgo(str string) algo.Algo {
|
417
|
+
switch str {
|
418
|
+
case "v1":
|
419
|
+
return algo.FuzzyMatchV1
|
420
|
+
case "v2":
|
421
|
+
return algo.FuzzyMatchV2
|
422
|
+
default:
|
423
|
+
errorExit("invalid algorithm (expected: v1 or v2)")
|
424
|
+
}
|
425
|
+
return algo.FuzzyMatchV2
|
426
|
+
}
|
427
|
+
|
428
|
+
func parseBorder(str string, optional bool) tui.BorderShape {
|
429
|
+
switch str {
|
430
|
+
case "rounded":
|
431
|
+
return tui.BorderRounded
|
432
|
+
case "sharp":
|
433
|
+
return tui.BorderSharp
|
434
|
+
case "horizontal":
|
435
|
+
return tui.BorderHorizontal
|
436
|
+
case "vertical":
|
437
|
+
return tui.BorderVertical
|
438
|
+
case "top":
|
439
|
+
return tui.BorderTop
|
440
|
+
case "bottom":
|
441
|
+
return tui.BorderBottom
|
442
|
+
case "left":
|
443
|
+
return tui.BorderLeft
|
444
|
+
case "right":
|
445
|
+
return tui.BorderRight
|
446
|
+
case "none":
|
447
|
+
return tui.BorderNone
|
448
|
+
default:
|
449
|
+
if optional && str == "" {
|
450
|
+
return tui.BorderRounded
|
451
|
+
}
|
452
|
+
errorExit("invalid border style (expected: rounded|sharp|horizontal|vertical|top|bottom|left|right|none)")
|
453
|
+
}
|
454
|
+
return tui.BorderNone
|
455
|
+
}
|
456
|
+
|
457
|
+
func parseKeyChords(str string, message string) map[tui.Event]string {
|
458
|
+
if len(str) == 0 {
|
459
|
+
errorExit(message)
|
460
|
+
}
|
461
|
+
|
462
|
+
str = regexp.MustCompile("(?i)(alt-),").ReplaceAllString(str, "$1"+string([]rune{escapedComma}))
|
463
|
+
tokens := strings.Split(str, ",")
|
464
|
+
if str == "," || strings.HasPrefix(str, ",,") || strings.HasSuffix(str, ",,") || strings.Contains(str, ",,,") {
|
465
|
+
tokens = append(tokens, ",")
|
466
|
+
}
|
467
|
+
|
468
|
+
chords := make(map[tui.Event]string)
|
469
|
+
for _, key := range tokens {
|
470
|
+
if len(key) == 0 {
|
471
|
+
continue // ignore
|
472
|
+
}
|
473
|
+
key = strings.ReplaceAll(key, string([]rune{escapedComma}), ",")
|
474
|
+
lkey := strings.ToLower(key)
|
475
|
+
add := func(e tui.EventType) {
|
476
|
+
chords[e.AsEvent()] = key
|
477
|
+
}
|
478
|
+
switch lkey {
|
479
|
+
case "up":
|
480
|
+
add(tui.Up)
|
481
|
+
case "down":
|
482
|
+
add(tui.Down)
|
483
|
+
case "left":
|
484
|
+
add(tui.Left)
|
485
|
+
case "right":
|
486
|
+
add(tui.Right)
|
487
|
+
case "enter", "return":
|
488
|
+
add(tui.CtrlM)
|
489
|
+
case "space":
|
490
|
+
chords[tui.Key(' ')] = key
|
491
|
+
case "bspace", "bs":
|
492
|
+
add(tui.BSpace)
|
493
|
+
case "ctrl-space":
|
494
|
+
add(tui.CtrlSpace)
|
495
|
+
case "ctrl-^", "ctrl-6":
|
496
|
+
add(tui.CtrlCaret)
|
497
|
+
case "ctrl-/", "ctrl-_":
|
498
|
+
add(tui.CtrlSlash)
|
499
|
+
case "ctrl-\\":
|
500
|
+
add(tui.CtrlBackSlash)
|
501
|
+
case "ctrl-]":
|
502
|
+
add(tui.CtrlRightBracket)
|
503
|
+
case "change":
|
504
|
+
add(tui.Change)
|
505
|
+
case "backward-eof":
|
506
|
+
add(tui.BackwardEOF)
|
507
|
+
case "alt-enter", "alt-return":
|
508
|
+
chords[tui.CtrlAltKey('m')] = key
|
509
|
+
case "alt-space":
|
510
|
+
chords[tui.AltKey(' ')] = key
|
511
|
+
case "alt-bs", "alt-bspace":
|
512
|
+
add(tui.AltBS)
|
513
|
+
case "alt-up":
|
514
|
+
add(tui.AltUp)
|
515
|
+
case "alt-down":
|
516
|
+
add(tui.AltDown)
|
517
|
+
case "alt-left":
|
518
|
+
add(tui.AltLeft)
|
519
|
+
case "alt-right":
|
520
|
+
add(tui.AltRight)
|
521
|
+
case "tab":
|
522
|
+
add(tui.Tab)
|
523
|
+
case "btab", "shift-tab":
|
524
|
+
add(tui.BTab)
|
525
|
+
case "esc":
|
526
|
+
add(tui.ESC)
|
527
|
+
case "del":
|
528
|
+
add(tui.Del)
|
529
|
+
case "home":
|
530
|
+
add(tui.Home)
|
531
|
+
case "end":
|
532
|
+
add(tui.End)
|
533
|
+
case "insert":
|
534
|
+
add(tui.Insert)
|
535
|
+
case "pgup", "page-up":
|
536
|
+
add(tui.PgUp)
|
537
|
+
case "pgdn", "page-down":
|
538
|
+
add(tui.PgDn)
|
539
|
+
case "alt-shift-up", "shift-alt-up":
|
540
|
+
add(tui.AltSUp)
|
541
|
+
case "alt-shift-down", "shift-alt-down":
|
542
|
+
add(tui.AltSDown)
|
543
|
+
case "alt-shift-left", "shift-alt-left":
|
544
|
+
add(tui.AltSLeft)
|
545
|
+
case "alt-shift-right", "shift-alt-right":
|
546
|
+
add(tui.AltSRight)
|
547
|
+
case "shift-up":
|
548
|
+
add(tui.SUp)
|
549
|
+
case "shift-down":
|
550
|
+
add(tui.SDown)
|
551
|
+
case "shift-left":
|
552
|
+
add(tui.SLeft)
|
553
|
+
case "shift-right":
|
554
|
+
add(tui.SRight)
|
555
|
+
case "left-click":
|
556
|
+
add(tui.LeftClick)
|
557
|
+
case "right-click":
|
558
|
+
add(tui.RightClick)
|
559
|
+
case "double-click":
|
560
|
+
add(tui.DoubleClick)
|
561
|
+
case "f10":
|
562
|
+
add(tui.F10)
|
563
|
+
case "f11":
|
564
|
+
add(tui.F11)
|
565
|
+
case "f12":
|
566
|
+
add(tui.F12)
|
567
|
+
default:
|
568
|
+
runes := []rune(key)
|
569
|
+
if len(key) == 10 && strings.HasPrefix(lkey, "ctrl-alt-") && isAlphabet(lkey[9]) {
|
570
|
+
chords[tui.CtrlAltKey(rune(key[9]))] = key
|
571
|
+
} else if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
|
572
|
+
add(tui.EventType(tui.CtrlA.Int() + int(lkey[5]) - 'a'))
|
573
|
+
} else if len(runes) == 5 && strings.HasPrefix(lkey, "alt-") {
|
574
|
+
r := runes[4]
|
575
|
+
switch r {
|
576
|
+
case escapedColon:
|
577
|
+
r = ':'
|
578
|
+
case escapedComma:
|
579
|
+
r = ','
|
580
|
+
case escapedPlus:
|
581
|
+
r = '+'
|
582
|
+
}
|
583
|
+
chords[tui.AltKey(r)] = key
|
584
|
+
} else if len(key) == 2 && strings.HasPrefix(lkey, "f") && key[1] >= '1' && key[1] <= '9' {
|
585
|
+
add(tui.EventType(tui.F1.Int() + int(key[1]) - '1'))
|
586
|
+
} else if len(runes) == 1 {
|
587
|
+
chords[tui.Key(runes[0])] = key
|
588
|
+
} else {
|
589
|
+
errorExit("unsupported key: " + key)
|
590
|
+
}
|
591
|
+
}
|
592
|
+
}
|
593
|
+
return chords
|
594
|
+
}
|
595
|
+
|
596
|
+
func parseTiebreak(str string) []criterion {
|
597
|
+
criteria := []criterion{byScore}
|
598
|
+
hasIndex := false
|
599
|
+
hasLength := false
|
600
|
+
hasBegin := false
|
601
|
+
hasEnd := false
|
602
|
+
check := func(notExpected *bool, name string) {
|
603
|
+
if *notExpected {
|
604
|
+
errorExit("duplicate sort criteria: " + name)
|
605
|
+
}
|
606
|
+
if hasIndex {
|
607
|
+
errorExit("index should be the last criterion")
|
608
|
+
}
|
609
|
+
*notExpected = true
|
610
|
+
}
|
611
|
+
for _, str := range strings.Split(strings.ToLower(str), ",") {
|
612
|
+
switch str {
|
613
|
+
case "index":
|
614
|
+
check(&hasIndex, "index")
|
615
|
+
case "length":
|
616
|
+
check(&hasLength, "length")
|
617
|
+
criteria = append(criteria, byLength)
|
618
|
+
case "begin":
|
619
|
+
check(&hasBegin, "begin")
|
620
|
+
criteria = append(criteria, byBegin)
|
621
|
+
case "end":
|
622
|
+
check(&hasEnd, "end")
|
623
|
+
criteria = append(criteria, byEnd)
|
624
|
+
default:
|
625
|
+
errorExit("invalid sort criterion: " + str)
|
626
|
+
}
|
627
|
+
}
|
628
|
+
return criteria
|
629
|
+
}
|
630
|
+
|
631
|
+
func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
|
632
|
+
dupe := *theme
|
633
|
+
return &dupe
|
634
|
+
}
|
635
|
+
|
636
|
+
func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
637
|
+
theme := dupeTheme(defaultTheme)
|
638
|
+
rrggbb := regexp.MustCompile("^#[0-9a-fA-F]{6}$")
|
639
|
+
for _, str := range strings.Split(strings.ToLower(str), ",") {
|
640
|
+
switch str {
|
641
|
+
case "dark":
|
642
|
+
theme = dupeTheme(tui.Dark256)
|
643
|
+
case "light":
|
644
|
+
theme = dupeTheme(tui.Light256)
|
645
|
+
case "16":
|
646
|
+
theme = dupeTheme(tui.Default16)
|
647
|
+
case "bw", "no":
|
648
|
+
theme = tui.NoColorTheme()
|
649
|
+
default:
|
650
|
+
fail := func() {
|
651
|
+
errorExit("invalid color specification: " + str)
|
652
|
+
}
|
653
|
+
// Color is disabled
|
654
|
+
if theme == nil {
|
655
|
+
continue
|
656
|
+
}
|
657
|
+
|
658
|
+
components := strings.Split(str, ":")
|
659
|
+
if len(components) < 2 {
|
660
|
+
fail()
|
661
|
+
}
|
662
|
+
|
663
|
+
mergeAttr := func(cattr *tui.ColorAttr) {
|
664
|
+
for _, component := range components[1:] {
|
665
|
+
switch component {
|
666
|
+
case "regular":
|
667
|
+
cattr.Attr = tui.AttrRegular
|
668
|
+
case "bold", "strong":
|
669
|
+
cattr.Attr |= tui.Bold
|
670
|
+
case "dim":
|
671
|
+
cattr.Attr |= tui.Dim
|
672
|
+
case "italic":
|
673
|
+
cattr.Attr |= tui.Italic
|
674
|
+
case "underline":
|
675
|
+
cattr.Attr |= tui.Underline
|
676
|
+
case "blink":
|
677
|
+
cattr.Attr |= tui.Blink
|
678
|
+
case "reverse":
|
679
|
+
cattr.Attr |= tui.Reverse
|
680
|
+
case "black":
|
681
|
+
cattr.Color = tui.Color(0)
|
682
|
+
case "red":
|
683
|
+
cattr.Color = tui.Color(1)
|
684
|
+
case "green":
|
685
|
+
cattr.Color = tui.Color(2)
|
686
|
+
case "yellow":
|
687
|
+
cattr.Color = tui.Color(3)
|
688
|
+
case "blue":
|
689
|
+
cattr.Color = tui.Color(4)
|
690
|
+
case "magenta":
|
691
|
+
cattr.Color = tui.Color(5)
|
692
|
+
case "cyan":
|
693
|
+
cattr.Color = tui.Color(6)
|
694
|
+
case "white":
|
695
|
+
cattr.Color = tui.Color(7)
|
696
|
+
case "bright-black", "gray", "grey":
|
697
|
+
cattr.Color = tui.Color(8)
|
698
|
+
case "bright-red":
|
699
|
+
cattr.Color = tui.Color(9)
|
700
|
+
case "bright-green":
|
701
|
+
cattr.Color = tui.Color(10)
|
702
|
+
case "bright-yellow":
|
703
|
+
cattr.Color = tui.Color(11)
|
704
|
+
case "bright-blue":
|
705
|
+
cattr.Color = tui.Color(12)
|
706
|
+
case "bright-magenta":
|
707
|
+
cattr.Color = tui.Color(13)
|
708
|
+
case "bright-cyan":
|
709
|
+
cattr.Color = tui.Color(14)
|
710
|
+
case "bright-white":
|
711
|
+
cattr.Color = tui.Color(15)
|
712
|
+
case "":
|
713
|
+
default:
|
714
|
+
if rrggbb.MatchString(component) {
|
715
|
+
cattr.Color = tui.HexToColor(component)
|
716
|
+
} else {
|
717
|
+
ansi32, err := strconv.Atoi(component)
|
718
|
+
if err != nil || ansi32 < -1 || ansi32 > 255 {
|
719
|
+
fail()
|
720
|
+
}
|
721
|
+
cattr.Color = tui.Color(ansi32)
|
722
|
+
}
|
723
|
+
}
|
724
|
+
}
|
725
|
+
}
|
726
|
+
switch components[0] {
|
727
|
+
case "query", "input":
|
728
|
+
mergeAttr(&theme.Input)
|
729
|
+
case "disabled":
|
730
|
+
mergeAttr(&theme.Disabled)
|
731
|
+
case "fg":
|
732
|
+
mergeAttr(&theme.Fg)
|
733
|
+
case "bg":
|
734
|
+
mergeAttr(&theme.Bg)
|
735
|
+
case "preview-fg":
|
736
|
+
mergeAttr(&theme.PreviewFg)
|
737
|
+
case "preview-bg":
|
738
|
+
mergeAttr(&theme.PreviewBg)
|
739
|
+
case "fg+":
|
740
|
+
mergeAttr(&theme.Current)
|
741
|
+
case "bg+":
|
742
|
+
mergeAttr(&theme.DarkBg)
|
743
|
+
case "gutter":
|
744
|
+
mergeAttr(&theme.Gutter)
|
745
|
+
case "hl":
|
746
|
+
mergeAttr(&theme.Match)
|
747
|
+
case "hl+":
|
748
|
+
mergeAttr(&theme.CurrentMatch)
|
749
|
+
case "border":
|
750
|
+
mergeAttr(&theme.Border)
|
751
|
+
case "prompt":
|
752
|
+
mergeAttr(&theme.Prompt)
|
753
|
+
case "spinner":
|
754
|
+
mergeAttr(&theme.Spinner)
|
755
|
+
case "info":
|
756
|
+
mergeAttr(&theme.Info)
|
757
|
+
case "pointer":
|
758
|
+
mergeAttr(&theme.Cursor)
|
759
|
+
case "marker":
|
760
|
+
mergeAttr(&theme.Selected)
|
761
|
+
case "header":
|
762
|
+
mergeAttr(&theme.Header)
|
763
|
+
default:
|
764
|
+
fail()
|
765
|
+
}
|
766
|
+
}
|
767
|
+
}
|
768
|
+
return theme
|
769
|
+
}
|
770
|
+
|
771
|
+
var executeRegexp *regexp.Regexp
|
772
|
+
|
773
|
+
func firstKey(keymap map[tui.Event]string) tui.Event {
|
774
|
+
for k := range keymap {
|
775
|
+
return k
|
776
|
+
}
|
777
|
+
return tui.EventType(0).AsEvent()
|
778
|
+
}
|
779
|
+
|
780
|
+
const (
|
781
|
+
escapedColon = 0
|
782
|
+
escapedComma = 1
|
783
|
+
escapedPlus = 2
|
784
|
+
)
|
785
|
+
|
786
|
+
func init() {
|
787
|
+
// Backreferences are not supported.
|
788
|
+
// "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| "#{c}[^#{c}]*#{c}" }.join('|')
|
789
|
+
executeRegexp = regexp.MustCompile(
|
790
|
+
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
|
791
|
+
}
|
792
|
+
|
793
|
+
func parseKeymap(keymap map[tui.Event][]action, str string) {
|
794
|
+
masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) string {
|
795
|
+
symbol := ":"
|
796
|
+
if strings.HasPrefix(src, "+") {
|
797
|
+
symbol = "+"
|
798
|
+
}
|
799
|
+
prefix := symbol + "execute"
|
800
|
+
if strings.HasPrefix(src[1:], "reload") {
|
801
|
+
prefix = symbol + "reload"
|
802
|
+
} else if strings.HasPrefix(src[1:], "preview") {
|
803
|
+
prefix = symbol + "preview"
|
804
|
+
} else if strings.HasPrefix(src[1:], "unbind") {
|
805
|
+
prefix = symbol + "unbind"
|
806
|
+
} else if strings.HasPrefix(src[1:], "change-prompt") {
|
807
|
+
prefix = symbol + "change-prompt"
|
808
|
+
} else if src[len(prefix)] == '-' {
|
809
|
+
c := src[len(prefix)+1]
|
810
|
+
if c == 's' || c == 'S' {
|
811
|
+
prefix += "-silent"
|
812
|
+
} else {
|
813
|
+
prefix += "-multi"
|
814
|
+
}
|
815
|
+
}
|
816
|
+
return prefix + "(" + strings.Repeat(" ", len(src)-len(prefix)-2) + ")"
|
817
|
+
})
|
818
|
+
masked = strings.Replace(masked, "::", string([]rune{escapedColon, ':'}), -1)
|
819
|
+
masked = strings.Replace(masked, ",:", string([]rune{escapedComma, ':'}), -1)
|
820
|
+
masked = strings.Replace(masked, "+:", string([]rune{escapedPlus, ':'}), -1)
|
821
|
+
|
822
|
+
idx := 0
|
823
|
+
for _, pairStr := range strings.Split(masked, ",") {
|
824
|
+
origPairStr := str[idx : idx+len(pairStr)]
|
825
|
+
idx += len(pairStr) + 1
|
826
|
+
|
827
|
+
pair := strings.SplitN(pairStr, ":", 2)
|
828
|
+
if len(pair) < 2 {
|
829
|
+
errorExit("bind action not specified: " + origPairStr)
|
830
|
+
}
|
831
|
+
var key tui.Event
|
832
|
+
if len(pair[0]) == 1 && pair[0][0] == escapedColon {
|
833
|
+
key = tui.Key(':')
|
834
|
+
} else if len(pair[0]) == 1 && pair[0][0] == escapedComma {
|
835
|
+
key = tui.Key(',')
|
836
|
+
} else if len(pair[0]) == 1 && pair[0][0] == escapedPlus {
|
837
|
+
key = tui.Key('+')
|
838
|
+
} else {
|
839
|
+
keys := parseKeyChords(pair[0], "key name required")
|
840
|
+
key = firstKey(keys)
|
841
|
+
}
|
842
|
+
|
843
|
+
idx2 := len(pair[0]) + 1
|
844
|
+
specs := strings.Split(pair[1], "+")
|
845
|
+
actions := make([]action, 0, len(specs))
|
846
|
+
appendAction := func(types ...actionType) {
|
847
|
+
actions = append(actions, toActions(types...)...)
|
848
|
+
}
|
849
|
+
prevSpec := ""
|
850
|
+
for specIndex, maskedSpec := range specs {
|
851
|
+
spec := origPairStr[idx2 : idx2+len(maskedSpec)]
|
852
|
+
idx2 += len(maskedSpec) + 1
|
853
|
+
spec = prevSpec + spec
|
854
|
+
specLower := strings.ToLower(spec)
|
855
|
+
switch specLower {
|
856
|
+
case "ignore":
|
857
|
+
appendAction(actIgnore)
|
858
|
+
case "beginning-of-line":
|
859
|
+
appendAction(actBeginningOfLine)
|
860
|
+
case "abort":
|
861
|
+
appendAction(actAbort)
|
862
|
+
case "accept":
|
863
|
+
appendAction(actAccept)
|
864
|
+
case "accept-non-empty":
|
865
|
+
appendAction(actAcceptNonEmpty)
|
866
|
+
case "print-query":
|
867
|
+
appendAction(actPrintQuery)
|
868
|
+
case "refresh-preview":
|
869
|
+
appendAction(actRefreshPreview)
|
870
|
+
case "replace-query":
|
871
|
+
appendAction(actReplaceQuery)
|
872
|
+
case "backward-char":
|
873
|
+
appendAction(actBackwardChar)
|
874
|
+
case "backward-delete-char":
|
875
|
+
appendAction(actBackwardDeleteChar)
|
876
|
+
case "backward-delete-char/eof":
|
877
|
+
appendAction(actBackwardDeleteCharEOF)
|
878
|
+
case "backward-word":
|
879
|
+
appendAction(actBackwardWord)
|
880
|
+
case "clear-screen":
|
881
|
+
appendAction(actClearScreen)
|
882
|
+
case "delete-char":
|
883
|
+
appendAction(actDeleteChar)
|
884
|
+
case "delete-char/eof":
|
885
|
+
appendAction(actDeleteCharEOF)
|
886
|
+
case "deselect":
|
887
|
+
appendAction(actDeselect)
|
888
|
+
case "end-of-line":
|
889
|
+
appendAction(actEndOfLine)
|
890
|
+
case "cancel":
|
891
|
+
appendAction(actCancel)
|
892
|
+
case "clear-query":
|
893
|
+
appendAction(actClearQuery)
|
894
|
+
case "clear-selection":
|
895
|
+
appendAction(actClearSelection)
|
896
|
+
case "forward-char":
|
897
|
+
appendAction(actForwardChar)
|
898
|
+
case "forward-word":
|
899
|
+
appendAction(actForwardWord)
|
900
|
+
case "jump":
|
901
|
+
appendAction(actJump)
|
902
|
+
case "jump-accept":
|
903
|
+
appendAction(actJumpAccept)
|
904
|
+
case "kill-line":
|
905
|
+
appendAction(actKillLine)
|
906
|
+
case "kill-word":
|
907
|
+
appendAction(actKillWord)
|
908
|
+
case "unix-line-discard", "line-discard":
|
909
|
+
appendAction(actUnixLineDiscard)
|
910
|
+
case "unix-word-rubout", "word-rubout":
|
911
|
+
appendAction(actUnixWordRubout)
|
912
|
+
case "yank":
|
913
|
+
appendAction(actYank)
|
914
|
+
case "backward-kill-word":
|
915
|
+
appendAction(actBackwardKillWord)
|
916
|
+
case "toggle-down":
|
917
|
+
appendAction(actToggle, actDown)
|
918
|
+
case "toggle-up":
|
919
|
+
appendAction(actToggle, actUp)
|
920
|
+
case "toggle-in":
|
921
|
+
appendAction(actToggleIn)
|
922
|
+
case "toggle-out":
|
923
|
+
appendAction(actToggleOut)
|
924
|
+
case "toggle-all":
|
925
|
+
appendAction(actToggleAll)
|
926
|
+
case "toggle-search":
|
927
|
+
appendAction(actToggleSearch)
|
928
|
+
case "select":
|
929
|
+
appendAction(actSelect)
|
930
|
+
case "select-all":
|
931
|
+
appendAction(actSelectAll)
|
932
|
+
case "deselect-all":
|
933
|
+
appendAction(actDeselectAll)
|
934
|
+
case "close":
|
935
|
+
appendAction(actClose)
|
936
|
+
case "toggle":
|
937
|
+
appendAction(actToggle)
|
938
|
+
case "down":
|
939
|
+
appendAction(actDown)
|
940
|
+
case "up":
|
941
|
+
appendAction(actUp)
|
942
|
+
case "first", "top":
|
943
|
+
appendAction(actFirst)
|
944
|
+
case "last":
|
945
|
+
appendAction(actLast)
|
946
|
+
case "page-up":
|
947
|
+
appendAction(actPageUp)
|
948
|
+
case "page-down":
|
949
|
+
appendAction(actPageDown)
|
950
|
+
case "half-page-up":
|
951
|
+
appendAction(actHalfPageUp)
|
952
|
+
case "half-page-down":
|
953
|
+
appendAction(actHalfPageDown)
|
954
|
+
case "previous-history":
|
955
|
+
appendAction(actPreviousHistory)
|
956
|
+
case "next-history":
|
957
|
+
appendAction(actNextHistory)
|
958
|
+
case "toggle-preview":
|
959
|
+
appendAction(actTogglePreview)
|
960
|
+
case "toggle-preview-wrap":
|
961
|
+
appendAction(actTogglePreviewWrap)
|
962
|
+
case "toggle-sort":
|
963
|
+
appendAction(actToggleSort)
|
964
|
+
case "preview-top":
|
965
|
+
appendAction(actPreviewTop)
|
966
|
+
case "preview-bottom":
|
967
|
+
appendAction(actPreviewBottom)
|
968
|
+
case "preview-up":
|
969
|
+
appendAction(actPreviewUp)
|
970
|
+
case "preview-down":
|
971
|
+
appendAction(actPreviewDown)
|
972
|
+
case "preview-page-up":
|
973
|
+
appendAction(actPreviewPageUp)
|
974
|
+
case "preview-page-down":
|
975
|
+
appendAction(actPreviewPageDown)
|
976
|
+
case "preview-half-page-up":
|
977
|
+
appendAction(actPreviewHalfPageUp)
|
978
|
+
case "preview-half-page-down":
|
979
|
+
appendAction(actPreviewHalfPageDown)
|
980
|
+
case "enable-search":
|
981
|
+
appendAction(actEnableSearch)
|
982
|
+
case "disable-search":
|
983
|
+
appendAction(actDisableSearch)
|
984
|
+
case "put":
|
985
|
+
if key.Type == tui.Rune && unicode.IsGraphic(key.Char) {
|
986
|
+
appendAction(actRune)
|
987
|
+
} else {
|
988
|
+
errorExit("unable to put non-printable character: " + pair[0])
|
989
|
+
}
|
990
|
+
default:
|
991
|
+
t := isExecuteAction(specLower)
|
992
|
+
if t == actIgnore {
|
993
|
+
if specIndex == 0 && specLower == "" {
|
994
|
+
actions = append(keymap[key], actions...)
|
995
|
+
} else {
|
996
|
+
errorExit("unknown action: " + spec)
|
997
|
+
}
|
998
|
+
} else {
|
999
|
+
var offset int
|
1000
|
+
switch t {
|
1001
|
+
case actReload:
|
1002
|
+
offset = len("reload")
|
1003
|
+
case actPreview:
|
1004
|
+
offset = len("preview")
|
1005
|
+
case actChangePrompt:
|
1006
|
+
offset = len("change-prompt")
|
1007
|
+
case actUnbind:
|
1008
|
+
offset = len("unbind")
|
1009
|
+
case actExecuteSilent:
|
1010
|
+
offset = len("execute-silent")
|
1011
|
+
case actExecuteMulti:
|
1012
|
+
offset = len("execute-multi")
|
1013
|
+
default:
|
1014
|
+
offset = len("execute")
|
1015
|
+
}
|
1016
|
+
var actionArg string
|
1017
|
+
if spec[offset] == ':' {
|
1018
|
+
if specIndex == len(specs)-1 {
|
1019
|
+
actionArg = spec[offset+1:]
|
1020
|
+
actions = append(actions, action{t: t, a: actionArg})
|
1021
|
+
} else {
|
1022
|
+
prevSpec = spec + "+"
|
1023
|
+
continue
|
1024
|
+
}
|
1025
|
+
} else {
|
1026
|
+
actionArg = spec[offset+1 : len(spec)-1]
|
1027
|
+
actions = append(actions, action{t: t, a: actionArg})
|
1028
|
+
}
|
1029
|
+
if t == actUnbind {
|
1030
|
+
parseKeyChords(actionArg, "unbind target required")
|
1031
|
+
}
|
1032
|
+
}
|
1033
|
+
}
|
1034
|
+
prevSpec = ""
|
1035
|
+
}
|
1036
|
+
keymap[key] = actions
|
1037
|
+
}
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
func isExecuteAction(str string) actionType {
|
1041
|
+
matches := executeRegexp.FindAllStringSubmatch(":"+str, -1)
|
1042
|
+
if matches == nil || len(matches) != 1 {
|
1043
|
+
return actIgnore
|
1044
|
+
}
|
1045
|
+
prefix := matches[0][1]
|
1046
|
+
if len(prefix) == 0 {
|
1047
|
+
prefix = matches[0][2]
|
1048
|
+
}
|
1049
|
+
switch prefix {
|
1050
|
+
case "reload":
|
1051
|
+
return actReload
|
1052
|
+
case "unbind":
|
1053
|
+
return actUnbind
|
1054
|
+
case "preview":
|
1055
|
+
return actPreview
|
1056
|
+
case "change-prompt":
|
1057
|
+
return actChangePrompt
|
1058
|
+
case "execute":
|
1059
|
+
return actExecute
|
1060
|
+
case "execute-silent":
|
1061
|
+
return actExecuteSilent
|
1062
|
+
case "execute-multi":
|
1063
|
+
return actExecuteMulti
|
1064
|
+
}
|
1065
|
+
return actIgnore
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
func parseToggleSort(keymap map[tui.Event][]action, str string) {
|
1069
|
+
keys := parseKeyChords(str, "key name required")
|
1070
|
+
if len(keys) != 1 {
|
1071
|
+
errorExit("multiple keys specified")
|
1072
|
+
}
|
1073
|
+
keymap[firstKey(keys)] = toActions(actToggleSort)
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
func strLines(str string) []string {
|
1077
|
+
return strings.Split(strings.TrimSuffix(str, "\n"), "\n")
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
func parseSize(str string, maxPercent float64, label string) sizeSpec {
|
1081
|
+
var val float64
|
1082
|
+
percent := strings.HasSuffix(str, "%")
|
1083
|
+
if percent {
|
1084
|
+
val = atof(str[:len(str)-1])
|
1085
|
+
if val < 0 {
|
1086
|
+
errorExit(label + " must be non-negative")
|
1087
|
+
}
|
1088
|
+
if val > maxPercent {
|
1089
|
+
errorExit(fmt.Sprintf("%s too large (max: %d%%)", label, int(maxPercent)))
|
1090
|
+
}
|
1091
|
+
} else {
|
1092
|
+
if strings.Contains(str, ".") {
|
1093
|
+
errorExit(label + " (without %) must be a non-negative integer")
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
val = float64(atoi(str))
|
1097
|
+
if val < 0 {
|
1098
|
+
errorExit(label + " must be non-negative")
|
1099
|
+
}
|
1100
|
+
}
|
1101
|
+
return sizeSpec{val, percent}
|
1102
|
+
}
|
1103
|
+
|
1104
|
+
func parseHeight(str string) sizeSpec {
|
1105
|
+
size := parseSize(str, 100, "height")
|
1106
|
+
return size
|
1107
|
+
}
|
1108
|
+
|
1109
|
+
func parseLayout(str string) layoutType {
|
1110
|
+
switch str {
|
1111
|
+
case "default":
|
1112
|
+
return layoutDefault
|
1113
|
+
case "reverse":
|
1114
|
+
return layoutReverse
|
1115
|
+
case "reverse-list":
|
1116
|
+
return layoutReverseList
|
1117
|
+
default:
|
1118
|
+
errorExit("invalid layout (expected: default / reverse / reverse-list)")
|
1119
|
+
}
|
1120
|
+
return layoutDefault
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
func parseInfoStyle(str string) infoStyle {
|
1124
|
+
switch str {
|
1125
|
+
case "default":
|
1126
|
+
return infoDefault
|
1127
|
+
case "inline":
|
1128
|
+
return infoInline
|
1129
|
+
case "hidden":
|
1130
|
+
return infoHidden
|
1131
|
+
default:
|
1132
|
+
errorExit("invalid info style (expected: default / inline / hidden)")
|
1133
|
+
}
|
1134
|
+
return infoDefault
|
1135
|
+
}
|
1136
|
+
|
1137
|
+
func parsePreviewWindow(opts *previewOpts, input string) {
|
1138
|
+
delimRegex := regexp.MustCompile("[:,]") // : for backward compatibility
|
1139
|
+
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
|
1140
|
+
offsetRegex := regexp.MustCompile(`^(\+{-?[0-9]+})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`)
|
1141
|
+
headerRegex := regexp.MustCompile("^~(0|[1-9][0-9]*)$")
|
1142
|
+
tokens := delimRegex.Split(input, -1)
|
1143
|
+
for _, token := range tokens {
|
1144
|
+
switch token {
|
1145
|
+
case "":
|
1146
|
+
case "default":
|
1147
|
+
*opts = defaultPreviewOpts(opts.command)
|
1148
|
+
case "hidden":
|
1149
|
+
opts.hidden = true
|
1150
|
+
case "nohidden":
|
1151
|
+
opts.hidden = false
|
1152
|
+
case "wrap":
|
1153
|
+
opts.wrap = true
|
1154
|
+
case "nowrap":
|
1155
|
+
opts.wrap = false
|
1156
|
+
case "cycle":
|
1157
|
+
opts.cycle = true
|
1158
|
+
case "nocycle":
|
1159
|
+
opts.cycle = false
|
1160
|
+
case "up", "top":
|
1161
|
+
opts.position = posUp
|
1162
|
+
case "down", "bottom":
|
1163
|
+
opts.position = posDown
|
1164
|
+
case "left":
|
1165
|
+
opts.position = posLeft
|
1166
|
+
case "right":
|
1167
|
+
opts.position = posRight
|
1168
|
+
case "rounded", "border", "border-rounded":
|
1169
|
+
opts.border = tui.BorderRounded
|
1170
|
+
case "sharp", "border-sharp":
|
1171
|
+
opts.border = tui.BorderSharp
|
1172
|
+
case "noborder", "border-none":
|
1173
|
+
opts.border = tui.BorderNone
|
1174
|
+
case "border-horizontal":
|
1175
|
+
opts.border = tui.BorderHorizontal
|
1176
|
+
case "border-vertical":
|
1177
|
+
opts.border = tui.BorderVertical
|
1178
|
+
case "border-top":
|
1179
|
+
opts.border = tui.BorderTop
|
1180
|
+
case "border-bottom":
|
1181
|
+
opts.border = tui.BorderBottom
|
1182
|
+
case "border-left":
|
1183
|
+
opts.border = tui.BorderLeft
|
1184
|
+
case "border-right":
|
1185
|
+
opts.border = tui.BorderRight
|
1186
|
+
case "follow":
|
1187
|
+
opts.follow = true
|
1188
|
+
case "nofollow":
|
1189
|
+
opts.follow = false
|
1190
|
+
default:
|
1191
|
+
if headerRegex.MatchString(token) {
|
1192
|
+
opts.headerLines = atoi(token[1:])
|
1193
|
+
} else if sizeRegex.MatchString(token) {
|
1194
|
+
opts.size = parseSize(token, 99, "window size")
|
1195
|
+
} else if offsetRegex.MatchString(token) {
|
1196
|
+
opts.scroll = token
|
1197
|
+
} else {
|
1198
|
+
errorExit("invalid preview window option: " + token)
|
1199
|
+
}
|
1200
|
+
}
|
1201
|
+
}
|
1202
|
+
}
|
1203
|
+
|
1204
|
+
func parseMargin(opt string, margin string) [4]sizeSpec {
|
1205
|
+
margins := strings.Split(margin, ",")
|
1206
|
+
checked := func(str string) sizeSpec {
|
1207
|
+
return parseSize(str, 49, opt)
|
1208
|
+
}
|
1209
|
+
switch len(margins) {
|
1210
|
+
case 1:
|
1211
|
+
m := checked(margins[0])
|
1212
|
+
return [4]sizeSpec{m, m, m, m}
|
1213
|
+
case 2:
|
1214
|
+
tb := checked(margins[0])
|
1215
|
+
rl := checked(margins[1])
|
1216
|
+
return [4]sizeSpec{tb, rl, tb, rl}
|
1217
|
+
case 3:
|
1218
|
+
t := checked(margins[0])
|
1219
|
+
rl := checked(margins[1])
|
1220
|
+
b := checked(margins[2])
|
1221
|
+
return [4]sizeSpec{t, rl, b, rl}
|
1222
|
+
case 4:
|
1223
|
+
return [4]sizeSpec{
|
1224
|
+
checked(margins[0]), checked(margins[1]),
|
1225
|
+
checked(margins[2]), checked(margins[3])}
|
1226
|
+
default:
|
1227
|
+
errorExit("invalid " + opt + ": " + margin)
|
1228
|
+
}
|
1229
|
+
return defaultMargin()
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
func parseOptions(opts *Options, allArgs []string) {
|
1233
|
+
var historyMax int
|
1234
|
+
if opts.History == nil {
|
1235
|
+
historyMax = defaultHistoryMax
|
1236
|
+
} else {
|
1237
|
+
historyMax = opts.History.maxSize
|
1238
|
+
}
|
1239
|
+
setHistory := func(path string) {
|
1240
|
+
h, e := NewHistory(path, historyMax)
|
1241
|
+
if e != nil {
|
1242
|
+
errorExit(e.Error())
|
1243
|
+
}
|
1244
|
+
opts.History = h
|
1245
|
+
}
|
1246
|
+
setHistoryMax := func(max int) {
|
1247
|
+
historyMax = max
|
1248
|
+
if historyMax < 1 {
|
1249
|
+
errorExit("history max must be a positive integer")
|
1250
|
+
}
|
1251
|
+
if opts.History != nil {
|
1252
|
+
opts.History.maxSize = historyMax
|
1253
|
+
}
|
1254
|
+
}
|
1255
|
+
validateJumpLabels := false
|
1256
|
+
validatePointer := false
|
1257
|
+
validateMarker := false
|
1258
|
+
for i := 0; i < len(allArgs); i++ {
|
1259
|
+
arg := allArgs[i]
|
1260
|
+
switch arg {
|
1261
|
+
case "-h", "--help":
|
1262
|
+
help(exitOk)
|
1263
|
+
case "-x", "--extended":
|
1264
|
+
opts.Extended = true
|
1265
|
+
case "-e", "--exact":
|
1266
|
+
opts.Fuzzy = false
|
1267
|
+
case "--extended-exact":
|
1268
|
+
// Note that we now don't have --no-extended-exact
|
1269
|
+
opts.Fuzzy = false
|
1270
|
+
opts.Extended = true
|
1271
|
+
case "+x", "--no-extended":
|
1272
|
+
opts.Extended = false
|
1273
|
+
case "+e", "--no-exact":
|
1274
|
+
opts.Fuzzy = true
|
1275
|
+
case "-q", "--query":
|
1276
|
+
opts.Query = nextString(allArgs, &i, "query string required")
|
1277
|
+
case "-f", "--filter":
|
1278
|
+
filter := nextString(allArgs, &i, "query string required")
|
1279
|
+
opts.Filter = &filter
|
1280
|
+
case "--literal":
|
1281
|
+
opts.Normalize = false
|
1282
|
+
case "--no-literal":
|
1283
|
+
opts.Normalize = true
|
1284
|
+
case "--algo":
|
1285
|
+
opts.FuzzyAlgo = parseAlgo(nextString(allArgs, &i, "algorithm required (v1|v2)"))
|
1286
|
+
case "--expect":
|
1287
|
+
for k, v := range parseKeyChords(nextString(allArgs, &i, "key names required"), "key names required") {
|
1288
|
+
opts.Expect[k] = v
|
1289
|
+
}
|
1290
|
+
case "--no-expect":
|
1291
|
+
opts.Expect = make(map[tui.Event]string)
|
1292
|
+
case "--enabled", "--no-phony":
|
1293
|
+
opts.Phony = false
|
1294
|
+
case "--disabled", "--phony":
|
1295
|
+
opts.Phony = true
|
1296
|
+
case "--tiebreak":
|
1297
|
+
opts.Criteria = parseTiebreak(nextString(allArgs, &i, "sort criterion required"))
|
1298
|
+
case "--bind":
|
1299
|
+
parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"))
|
1300
|
+
case "--color":
|
1301
|
+
_, spec := optionalNextString(allArgs, &i)
|
1302
|
+
if len(spec) == 0 {
|
1303
|
+
opts.Theme = tui.EmptyTheme()
|
1304
|
+
} else {
|
1305
|
+
opts.Theme = parseTheme(opts.Theme, spec)
|
1306
|
+
}
|
1307
|
+
case "--toggle-sort":
|
1308
|
+
parseToggleSort(opts.Keymap, nextString(allArgs, &i, "key name required"))
|
1309
|
+
case "-d", "--delimiter":
|
1310
|
+
opts.Delimiter = delimiterRegexp(nextString(allArgs, &i, "delimiter required"))
|
1311
|
+
case "-n", "--nth":
|
1312
|
+
opts.Nth = splitNth(nextString(allArgs, &i, "nth expression required"))
|
1313
|
+
case "--with-nth":
|
1314
|
+
opts.WithNth = splitNth(nextString(allArgs, &i, "nth expression required"))
|
1315
|
+
case "-s", "--sort":
|
1316
|
+
opts.Sort = optionalNumeric(allArgs, &i, 1)
|
1317
|
+
case "+s", "--no-sort":
|
1318
|
+
opts.Sort = 0
|
1319
|
+
case "--tac":
|
1320
|
+
opts.Tac = true
|
1321
|
+
case "--no-tac":
|
1322
|
+
opts.Tac = false
|
1323
|
+
case "-i":
|
1324
|
+
opts.Case = CaseIgnore
|
1325
|
+
case "+i":
|
1326
|
+
opts.Case = CaseRespect
|
1327
|
+
case "-m", "--multi":
|
1328
|
+
opts.Multi = optionalNumeric(allArgs, &i, maxMulti)
|
1329
|
+
case "+m", "--no-multi":
|
1330
|
+
opts.Multi = 0
|
1331
|
+
case "--ansi":
|
1332
|
+
opts.Ansi = true
|
1333
|
+
case "--no-ansi":
|
1334
|
+
opts.Ansi = false
|
1335
|
+
case "--no-mouse":
|
1336
|
+
opts.Mouse = false
|
1337
|
+
case "+c", "--no-color":
|
1338
|
+
opts.Theme = tui.NoColorTheme()
|
1339
|
+
case "+2", "--no-256":
|
1340
|
+
opts.Theme = tui.Default16
|
1341
|
+
case "--black":
|
1342
|
+
opts.Black = true
|
1343
|
+
case "--no-black":
|
1344
|
+
opts.Black = false
|
1345
|
+
case "--bold":
|
1346
|
+
opts.Bold = true
|
1347
|
+
case "--no-bold":
|
1348
|
+
opts.Bold = false
|
1349
|
+
case "--layout":
|
1350
|
+
opts.Layout = parseLayout(
|
1351
|
+
nextString(allArgs, &i, "layout required (default / reverse / reverse-list)"))
|
1352
|
+
case "--reverse":
|
1353
|
+
opts.Layout = layoutReverse
|
1354
|
+
case "--no-reverse":
|
1355
|
+
opts.Layout = layoutDefault
|
1356
|
+
case "--cycle":
|
1357
|
+
opts.Cycle = true
|
1358
|
+
case "--no-cycle":
|
1359
|
+
opts.Cycle = false
|
1360
|
+
case "--keep-right":
|
1361
|
+
opts.KeepRight = true
|
1362
|
+
case "--no-keep-right":
|
1363
|
+
opts.KeepRight = false
|
1364
|
+
case "--hscroll":
|
1365
|
+
opts.Hscroll = true
|
1366
|
+
case "--no-hscroll":
|
1367
|
+
opts.Hscroll = false
|
1368
|
+
case "--hscroll-off":
|
1369
|
+
opts.HscrollOff = nextInt(allArgs, &i, "hscroll offset required")
|
1370
|
+
case "--scroll-off":
|
1371
|
+
opts.ScrollOff = nextInt(allArgs, &i, "scroll offset required")
|
1372
|
+
case "--filepath-word":
|
1373
|
+
opts.FileWord = true
|
1374
|
+
case "--no-filepath-word":
|
1375
|
+
opts.FileWord = false
|
1376
|
+
case "--info":
|
1377
|
+
opts.InfoStyle = parseInfoStyle(
|
1378
|
+
nextString(allArgs, &i, "info style required"))
|
1379
|
+
case "--no-info":
|
1380
|
+
opts.InfoStyle = infoHidden
|
1381
|
+
case "--inline-info":
|
1382
|
+
opts.InfoStyle = infoInline
|
1383
|
+
case "--no-inline-info":
|
1384
|
+
opts.InfoStyle = infoDefault
|
1385
|
+
case "--jump-labels":
|
1386
|
+
opts.JumpLabels = nextString(allArgs, &i, "label characters required")
|
1387
|
+
validateJumpLabels = true
|
1388
|
+
case "-1", "--select-1":
|
1389
|
+
opts.Select1 = true
|
1390
|
+
case "+1", "--no-select-1":
|
1391
|
+
opts.Select1 = false
|
1392
|
+
case "-0", "--exit-0":
|
1393
|
+
opts.Exit0 = true
|
1394
|
+
case "+0", "--no-exit-0":
|
1395
|
+
opts.Exit0 = false
|
1396
|
+
case "--read0":
|
1397
|
+
opts.ReadZero = true
|
1398
|
+
case "--no-read0":
|
1399
|
+
opts.ReadZero = false
|
1400
|
+
case "--print0":
|
1401
|
+
opts.Printer = func(str string) { fmt.Print(str, "\x00") }
|
1402
|
+
opts.PrintSep = "\x00"
|
1403
|
+
case "--no-print0":
|
1404
|
+
opts.Printer = func(str string) { fmt.Println(str) }
|
1405
|
+
opts.PrintSep = "\n"
|
1406
|
+
case "--print-query":
|
1407
|
+
opts.PrintQuery = true
|
1408
|
+
case "--no-print-query":
|
1409
|
+
opts.PrintQuery = false
|
1410
|
+
case "--prompt":
|
1411
|
+
opts.Prompt = nextString(allArgs, &i, "prompt string required")
|
1412
|
+
case "--pointer":
|
1413
|
+
opts.Pointer = nextString(allArgs, &i, "pointer sign string required")
|
1414
|
+
validatePointer = true
|
1415
|
+
case "--marker":
|
1416
|
+
opts.Marker = nextString(allArgs, &i, "selected sign string required")
|
1417
|
+
validateMarker = true
|
1418
|
+
case "--sync":
|
1419
|
+
opts.Sync = true
|
1420
|
+
case "--no-sync":
|
1421
|
+
opts.Sync = false
|
1422
|
+
case "--async":
|
1423
|
+
opts.Sync = false
|
1424
|
+
case "--no-history":
|
1425
|
+
opts.History = nil
|
1426
|
+
case "--history":
|
1427
|
+
setHistory(nextString(allArgs, &i, "history file path required"))
|
1428
|
+
case "--history-size":
|
1429
|
+
setHistoryMax(nextInt(allArgs, &i, "history max size required"))
|
1430
|
+
case "--no-header":
|
1431
|
+
opts.Header = []string{}
|
1432
|
+
case "--no-header-lines":
|
1433
|
+
opts.HeaderLines = 0
|
1434
|
+
case "--header":
|
1435
|
+
opts.Header = strLines(nextString(allArgs, &i, "header string required"))
|
1436
|
+
case "--header-lines":
|
1437
|
+
opts.HeaderLines = atoi(
|
1438
|
+
nextString(allArgs, &i, "number of header lines required"))
|
1439
|
+
case "--header-first":
|
1440
|
+
opts.HeaderFirst = true
|
1441
|
+
case "--no-header-first":
|
1442
|
+
opts.HeaderFirst = false
|
1443
|
+
case "--preview":
|
1444
|
+
opts.Preview.command = nextString(allArgs, &i, "preview command required")
|
1445
|
+
case "--no-preview":
|
1446
|
+
opts.Preview.command = ""
|
1447
|
+
case "--preview-window":
|
1448
|
+
parsePreviewWindow(&opts.Preview,
|
1449
|
+
nextString(allArgs, &i, "preview window layout required: [up|down|left|right][,SIZE[%]][,border-BORDER_OPT][,wrap][,cycle][,hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]"))
|
1450
|
+
case "--height":
|
1451
|
+
opts.Height = parseHeight(nextString(allArgs, &i, "height required: HEIGHT[%]"))
|
1452
|
+
case "--min-height":
|
1453
|
+
opts.MinHeight = nextInt(allArgs, &i, "height required: HEIGHT")
|
1454
|
+
case "--no-height":
|
1455
|
+
opts.Height = sizeSpec{}
|
1456
|
+
case "--no-margin":
|
1457
|
+
opts.Margin = defaultMargin()
|
1458
|
+
case "--no-padding":
|
1459
|
+
opts.Padding = defaultMargin()
|
1460
|
+
case "--no-border":
|
1461
|
+
opts.BorderShape = tui.BorderNone
|
1462
|
+
case "--border":
|
1463
|
+
hasArg, arg := optionalNextString(allArgs, &i)
|
1464
|
+
opts.BorderShape = parseBorder(arg, !hasArg)
|
1465
|
+
case "--no-unicode":
|
1466
|
+
opts.Unicode = false
|
1467
|
+
case "--unicode":
|
1468
|
+
opts.Unicode = true
|
1469
|
+
case "--margin":
|
1470
|
+
opts.Margin = parseMargin(
|
1471
|
+
"margin",
|
1472
|
+
nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
|
1473
|
+
case "--padding":
|
1474
|
+
opts.Padding = parseMargin(
|
1475
|
+
"padding",
|
1476
|
+
nextString(allArgs, &i, "padding required (TRBL / TB,RL / T,RL,B / T,R,B,L)"))
|
1477
|
+
case "--tabstop":
|
1478
|
+
opts.Tabstop = nextInt(allArgs, &i, "tab stop required")
|
1479
|
+
case "--clear":
|
1480
|
+
opts.ClearOnExit = true
|
1481
|
+
case "--no-clear":
|
1482
|
+
opts.ClearOnExit = false
|
1483
|
+
case "--version":
|
1484
|
+
opts.Version = true
|
1485
|
+
default:
|
1486
|
+
if match, value := optString(arg, "--algo="); match {
|
1487
|
+
opts.FuzzyAlgo = parseAlgo(value)
|
1488
|
+
} else if match, value := optString(arg, "-q", "--query="); match {
|
1489
|
+
opts.Query = value
|
1490
|
+
} else if match, value := optString(arg, "-f", "--filter="); match {
|
1491
|
+
opts.Filter = &value
|
1492
|
+
} else if match, value := optString(arg, "-d", "--delimiter="); match {
|
1493
|
+
opts.Delimiter = delimiterRegexp(value)
|
1494
|
+
} else if match, value := optString(arg, "--border="); match {
|
1495
|
+
opts.BorderShape = parseBorder(value, false)
|
1496
|
+
} else if match, value := optString(arg, "--prompt="); match {
|
1497
|
+
opts.Prompt = value
|
1498
|
+
} else if match, value := optString(arg, "--pointer="); match {
|
1499
|
+
opts.Pointer = value
|
1500
|
+
validatePointer = true
|
1501
|
+
} else if match, value := optString(arg, "--marker="); match {
|
1502
|
+
opts.Marker = value
|
1503
|
+
validateMarker = true
|
1504
|
+
} else if match, value := optString(arg, "-n", "--nth="); match {
|
1505
|
+
opts.Nth = splitNth(value)
|
1506
|
+
} else if match, value := optString(arg, "--with-nth="); match {
|
1507
|
+
opts.WithNth = splitNth(value)
|
1508
|
+
} else if match, _ := optString(arg, "-s", "--sort="); match {
|
1509
|
+
opts.Sort = 1 // Don't care
|
1510
|
+
} else if match, value := optString(arg, "-m", "--multi="); match {
|
1511
|
+
opts.Multi = atoi(value)
|
1512
|
+
} else if match, value := optString(arg, "--height="); match {
|
1513
|
+
opts.Height = parseHeight(value)
|
1514
|
+
} else if match, value := optString(arg, "--min-height="); match {
|
1515
|
+
opts.MinHeight = atoi(value)
|
1516
|
+
} else if match, value := optString(arg, "--layout="); match {
|
1517
|
+
opts.Layout = parseLayout(value)
|
1518
|
+
} else if match, value := optString(arg, "--info="); match {
|
1519
|
+
opts.InfoStyle = parseInfoStyle(value)
|
1520
|
+
} else if match, value := optString(arg, "--toggle-sort="); match {
|
1521
|
+
parseToggleSort(opts.Keymap, value)
|
1522
|
+
} else if match, value := optString(arg, "--expect="); match {
|
1523
|
+
for k, v := range parseKeyChords(value, "key names required") {
|
1524
|
+
opts.Expect[k] = v
|
1525
|
+
}
|
1526
|
+
} else if match, value := optString(arg, "--tiebreak="); match {
|
1527
|
+
opts.Criteria = parseTiebreak(value)
|
1528
|
+
} else if match, value := optString(arg, "--color="); match {
|
1529
|
+
opts.Theme = parseTheme(opts.Theme, value)
|
1530
|
+
} else if match, value := optString(arg, "--bind="); match {
|
1531
|
+
parseKeymap(opts.Keymap, value)
|
1532
|
+
} else if match, value := optString(arg, "--history="); match {
|
1533
|
+
setHistory(value)
|
1534
|
+
} else if match, value := optString(arg, "--history-size="); match {
|
1535
|
+
setHistoryMax(atoi(value))
|
1536
|
+
} else if match, value := optString(arg, "--header="); match {
|
1537
|
+
opts.Header = strLines(value)
|
1538
|
+
} else if match, value := optString(arg, "--header-lines="); match {
|
1539
|
+
opts.HeaderLines = atoi(value)
|
1540
|
+
} else if match, value := optString(arg, "--preview="); match {
|
1541
|
+
opts.Preview.command = value
|
1542
|
+
} else if match, value := optString(arg, "--preview-window="); match {
|
1543
|
+
parsePreviewWindow(&opts.Preview, value)
|
1544
|
+
} else if match, value := optString(arg, "--margin="); match {
|
1545
|
+
opts.Margin = parseMargin("margin", value)
|
1546
|
+
} else if match, value := optString(arg, "--padding="); match {
|
1547
|
+
opts.Padding = parseMargin("padding", value)
|
1548
|
+
} else if match, value := optString(arg, "--tabstop="); match {
|
1549
|
+
opts.Tabstop = atoi(value)
|
1550
|
+
} else if match, value := optString(arg, "--hscroll-off="); match {
|
1551
|
+
opts.HscrollOff = atoi(value)
|
1552
|
+
} else if match, value := optString(arg, "--scroll-off="); match {
|
1553
|
+
opts.ScrollOff = atoi(value)
|
1554
|
+
} else if match, value := optString(arg, "--jump-labels="); match {
|
1555
|
+
opts.JumpLabels = value
|
1556
|
+
validateJumpLabels = true
|
1557
|
+
} else {
|
1558
|
+
errorExit("unknown option: " + arg)
|
1559
|
+
}
|
1560
|
+
}
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
if opts.HeaderLines < 0 {
|
1564
|
+
errorExit("header lines must be a non-negative integer")
|
1565
|
+
}
|
1566
|
+
|
1567
|
+
if opts.HscrollOff < 0 {
|
1568
|
+
errorExit("hscroll offset must be a non-negative integer")
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
if opts.ScrollOff < 0 {
|
1572
|
+
errorExit("scroll offset must be a non-negative integer")
|
1573
|
+
}
|
1574
|
+
|
1575
|
+
if opts.Tabstop < 1 {
|
1576
|
+
errorExit("tab stop must be a positive integer")
|
1577
|
+
}
|
1578
|
+
|
1579
|
+
if len(opts.JumpLabels) == 0 {
|
1580
|
+
errorExit("empty jump labels")
|
1581
|
+
}
|
1582
|
+
|
1583
|
+
if validateJumpLabels {
|
1584
|
+
for _, r := range opts.JumpLabels {
|
1585
|
+
if r < 32 || r > 126 {
|
1586
|
+
errorExit("non-ascii jump labels are not allowed")
|
1587
|
+
}
|
1588
|
+
}
|
1589
|
+
}
|
1590
|
+
|
1591
|
+
if validatePointer {
|
1592
|
+
if err := validateSign(opts.Pointer, "pointer"); err != nil {
|
1593
|
+
errorExit(err.Error())
|
1594
|
+
}
|
1595
|
+
}
|
1596
|
+
|
1597
|
+
if validateMarker {
|
1598
|
+
if err := validateSign(opts.Marker, "marker"); err != nil {
|
1599
|
+
errorExit(err.Error())
|
1600
|
+
}
|
1601
|
+
}
|
1602
|
+
}
|
1603
|
+
|
1604
|
+
func validateSign(sign string, signOptName string) error {
|
1605
|
+
if sign == "" {
|
1606
|
+
return fmt.Errorf("%v cannot be empty", signOptName)
|
1607
|
+
}
|
1608
|
+
for _, r := range sign {
|
1609
|
+
if !unicode.IsGraphic(r) {
|
1610
|
+
return fmt.Errorf("invalid character in %v", signOptName)
|
1611
|
+
}
|
1612
|
+
}
|
1613
|
+
if runewidth.StringWidth(sign) > 2 {
|
1614
|
+
return fmt.Errorf("%v display width should be up to 2", signOptName)
|
1615
|
+
}
|
1616
|
+
return nil
|
1617
|
+
}
|
1618
|
+
|
1619
|
+
func postProcessOptions(opts *Options) {
|
1620
|
+
if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
|
1621
|
+
errorExit("--height option is currently not supported on this platform")
|
1622
|
+
}
|
1623
|
+
// Default actions for CTRL-N / CTRL-P when --history is set
|
1624
|
+
if opts.History != nil {
|
1625
|
+
if _, prs := opts.Keymap[tui.CtrlP.AsEvent()]; !prs {
|
1626
|
+
opts.Keymap[tui.CtrlP.AsEvent()] = toActions(actPreviousHistory)
|
1627
|
+
}
|
1628
|
+
if _, prs := opts.Keymap[tui.CtrlN.AsEvent()]; !prs {
|
1629
|
+
opts.Keymap[tui.CtrlN.AsEvent()] = toActions(actNextHistory)
|
1630
|
+
}
|
1631
|
+
}
|
1632
|
+
|
1633
|
+
// Extend the default key map
|
1634
|
+
keymap := defaultKeymap()
|
1635
|
+
for key, actions := range opts.Keymap {
|
1636
|
+
for _, act := range actions {
|
1637
|
+
if act.t == actToggleSort {
|
1638
|
+
opts.ToggleSort = true
|
1639
|
+
}
|
1640
|
+
}
|
1641
|
+
keymap[key] = actions
|
1642
|
+
}
|
1643
|
+
opts.Keymap = keymap
|
1644
|
+
|
1645
|
+
// If we're not using extended search mode, --nth option becomes irrelevant
|
1646
|
+
// if it contains the whole range
|
1647
|
+
if !opts.Extended || len(opts.Nth) == 1 {
|
1648
|
+
for _, r := range opts.Nth {
|
1649
|
+
if r.begin == rangeEllipsis && r.end == rangeEllipsis {
|
1650
|
+
opts.Nth = make([]Range, 0)
|
1651
|
+
return
|
1652
|
+
}
|
1653
|
+
}
|
1654
|
+
}
|
1655
|
+
|
1656
|
+
if opts.Bold {
|
1657
|
+
theme := opts.Theme
|
1658
|
+
boldify := func(c tui.ColorAttr) tui.ColorAttr {
|
1659
|
+
dup := c
|
1660
|
+
if !theme.Colored {
|
1661
|
+
dup.Attr |= tui.Bold
|
1662
|
+
} else if (c.Attr & tui.AttrRegular) == 0 {
|
1663
|
+
dup.Attr |= tui.Bold
|
1664
|
+
}
|
1665
|
+
return dup
|
1666
|
+
}
|
1667
|
+
theme.Current = boldify(theme.Current)
|
1668
|
+
theme.CurrentMatch = boldify(theme.CurrentMatch)
|
1669
|
+
theme.Prompt = boldify(theme.Prompt)
|
1670
|
+
theme.Input = boldify(theme.Input)
|
1671
|
+
theme.Cursor = boldify(theme.Cursor)
|
1672
|
+
theme.Spinner = boldify(theme.Spinner)
|
1673
|
+
}
|
1674
|
+
}
|
1675
|
+
|
1676
|
+
// ParseOptions parses command-line options
|
1677
|
+
func ParseOptions() *Options {
|
1678
|
+
opts := defaultOptions()
|
1679
|
+
|
1680
|
+
// Options from Env var
|
1681
|
+
words, _ := shellwords.Parse(os.Getenv("FZF_DEFAULT_OPTS"))
|
1682
|
+
if len(words) > 0 {
|
1683
|
+
parseOptions(opts, words)
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
// Options from command-line arguments
|
1687
|
+
parseOptions(opts, os.Args[1:])
|
1688
|
+
|
1689
|
+
postProcessOptions(opts)
|
1690
|
+
return opts
|
1691
|
+
}
|