-
Notifications
You must be signed in to change notification settings - Fork 97
/
fzf-tab.zsh
440 lines (378 loc) · 13.5 KB
/
fzf-tab.zsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# temporarily change options
'builtin' 'local' '-a' '_ftb_opts'
[[ ! -o 'aliases' ]] || _ftb_opts+=('aliases')
[[ ! -o 'sh_glob' ]] || _ftb_opts+=('sh_glob')
[[ ! -o 'no_brace_expand' ]] || _ftb_opts+=('no_brace_expand')
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
# disable aliases
typeset _ftb_aliases="$(builtin alias -Lm '[^+]*')"
builtin unalias -m '[^+]*'
# thanks Valodim/zsh-capture-completion
-ftb-compadd() {
# parse all options
local -A apre hpre dscrs _oad _mesg
local -a isfile _opts __ expl
zparseopts -a _opts P:=apre p:=hpre d:=dscrs X+:=expl O:=_oad A:=_oad D:=_oad f=isfile \
i: S: s: I: x:=_mesg r: R: W: F: M+: E: q e Q n U C \
J:=__ V:=__ a=__ l=__ k=__ o::=__ 1=__ 2=__
# store $curcontext for further usage
_ftb_curcontext=${curcontext#:}
# just delegate and leave if any of -O, -A or -D are given or fzf-tab is not enabled
# or fzf-tab is disabled in the current context
if (( $#_oad != 0 || ! IN_FZF_TAB )) \
|| { -ftb-zstyle -m disabled-on "any" } \
|| ({ -ftb-zstyle -m disabled-on "files" } && [[ -n $isfile ]]); then
builtin compadd "$@"
return
fi
# store matches in $__hits and descriptions in $__dscr
local -a __hits __dscr
if (( $#dscrs == 1 )); then
__dscr=( "${(@P)${(v)dscrs}}" )
fi
builtin compadd -A __hits -D __dscr "$@"
local ret=$?
if (( $#__hits == 0 )); then
if is-at-least 5.9 && (( $#_mesg != 0 )); then
builtin compadd -x $_mesg
fi
return $ret
fi
# only store the fist `-X`
expl=$expl[2]
# keep order of group description
[[ -n $expl ]] && _ftb_groups+=$expl
# store these values in _ftb_compcap
local -a keys=(apre hpre PREFIX SUFFIX IPREFIX ISUFFIX)
local key expanded __tmp_value=$'<\0>' # placeholder
for key in $keys; do
expanded=${(P)key}
if [[ -n $expanded ]]; then
__tmp_value+=$'\0'$key$'\0'$expanded
fi
done
if [[ -n $expl ]]; then
# store group index
__tmp_value+=$'\0group\0'$_ftb_groups[(ie)$expl]
fi
if [[ -n $isfile ]]; then
# NOTE: need a extra ${} here or ~ expansion won't work
__tmp_value+=$'\0realdir\0'${${(Qe)~${:-$IPREFIX$hpre}}}
fi
_opts+=("${(@kv)apre}" "${(@kv)hpre}" $isfile)
__tmp_value+=$'\0args\0'${(pj:\1:)_opts}
if (( $+builtins[fzf-tab-compcap-generate] )); then
fzf-tab-compcap-generate __hits __dscr __tmp_value
else
# dscr - the string to show to users
# word - the string to be inserted
local dscr word i
for i in {1..$#__hits}; do
word=$__hits[i] dscr=$__dscr[i]
if [[ -n $dscr ]]; then
dscr=${dscr//$'\n'}
elif [[ -n $word ]]; then
dscr=$word
fi
_ftb_compcap+=$dscr$'\2'$__tmp_value$'\0word\0'$word
done
fi
# tell zsh that the match is successful
builtin compadd "$@"
}
-ftb-zstyle() {
zstyle $1 ":fzf-tab:$_ftb_curcontext" ${@:2}
}
-ftb-complete() {
local -Ua _ftb_groups
local choice choices _ftb_curcontext continuous_trigger print_query accept_line bs=$'\2' nul=$'\0'
local ret=0
# must run with user options; don't move `emulate -L zsh` above this line
(( $+builtins[fzf-tab-compcap-generate] )) && fzf-tab-compcap-generate -i
COLUMNS=500 _ftb__main_complete "$@" || ret=$?
(( $+builtins[fzf-tab-compcap-generate] )) && fzf-tab-compcap-generate -o
emulate -L zsh -o extended_glob
local _ftb_query _ftb_complist=() _ftb_headers=() command opts
-ftb-generate-complist # sets `_ftb_complist`
-ftb-zstyle -s continuous-trigger continuous_trigger || {
[[ $OSTYPE == msys ]] && continuous_trigger=// || continuous_trigger=/
}
case $#_ftb_complist in
0) return 1;;
1)
choices=("EXPECT_KEY" "${_ftb_compcap[1]%$bs*}")
if (( _ftb_continue_last )); then
choices[1]=$continuous_trigger
fi
;;
*)
if (( ! _ftb_continue_last )) \
&& [[ $compstate[insert] == *"unambiguous" ]] \
&& [[ -n $compstate[unambiguous] ]] \
&& [[ "$compstate[unambiguous]" != "$compstate[quote]$IPREFIX$PREFIX$compstate[quote]" ]]; then
compstate[list]=
compstate[insert]=unambiguous
_ftb_finish=1
return 0
fi
-ftb-generate-query # sets `_ftb_query`
-ftb-generate-header # sets `_ftb_headers`
-ftb-zstyle -s print-query print_query || print_query=alt-enter
-ftb-zstyle -s accept-line accept_line
choices=("${(@f)"$(builtin print -rl -- $_ftb_headers $_ftb_complist | -ftb-fzf)"}")
ret=$?
# choices=(query_string expect_key returned_word)
# insert query string directly
if [[ $choices[2] == $print_query ]] || [[ -n $choices[1] && $#choices == 1 ]] ; then
local -A v=("${(@0)${_ftb_compcap[1]}}")
local -a args=("${(@ps:\1:)v[args]}")
[[ -z $args[1] ]] && args=() # don't pass an empty string
IPREFIX=$v[IPREFIX] PREFIX=$v[PREFIX] SUFFIX=$v[SUFFIX] ISUFFIX=$v[ISUFFIX]
# NOTE: should I use `-U` here?, ../f\tabcd -> ../abcd
builtin compadd "${args[@]:--Q}" -Q -- $choices[1]
compstate[list]=
compstate[insert]=
if (( $#choices[1] > 0 )); then
compstate[insert]='1'
[[ $RBUFFER == ' '* ]] || compstate[insert]+=' '
fi
_ftb_finish=1
return $ret
fi
choices[1]=()
choices=("${(@)${(@)choices%$nul*}#*$nul}")
unset CTXT
;;
esac
if [[ -n $choices[1] && $choices[1] == $continuous_trigger ]]; then
typeset -gi _ftb_continue=1
typeset -gi _ftb_continue_last=1
fi
if [[ -n $choices[1] && $choices[1] == $accept_line ]]; then
typeset -gi _ftb_accept=1
fi
choices[1]=()
_ftb_choices=("${(@)choices}")
compstate[list]=
compstate[insert]=
return $ret
}
_fzf-tab-apply() {
local choice bs=$'\2'
for choice in "$_ftb_choices[@]"; do
local -A v=("${(@0)${_ftb_compcap[(r)${(b)choice}$bs*]#*$bs}}")
local -a args=("${(@ps:\1:)v[args]}")
[[ -z $args[1] ]] && args=() # don't pass an empty string
IPREFIX=$v[IPREFIX] PREFIX=$v[PREFIX] SUFFIX=$v[SUFFIX] ISUFFIX=$v[ISUFFIX]
builtin compadd "${args[@]:--Q}" -Q -- "$v[word]"
done
compstate[list]=
if (( $#_ftb_choices == 1 )); then
compstate[insert]='1'
[[ $RBUFFER == ' '* ]] || compstate[insert]+=' '
elif (( $#_ftb_choices > 1 )); then
compstate[insert]='all'
fi
}
fzf-tab-debug() {
(( $+_ftb_debug_cnt )) || typeset -gi _ftb_debug_cnt
local tmp=${TMPPREFIX:-/tmp/zsh}-$$-fzf-tab-$(( ++_ftb_debug_cnt )).log
local -i debug_fd=-1 IN_FZF_TAB=1
{
exec {debug_fd}>&2 2>| $tmp
local -a debug_indent; debug_indent=( '%'{3..20}'(e. .)' )
local PROMPT4 PS4="${(j::)debug_indent}+%N:%i> "
functions -t -- -ftb-complete _fzf-tab-apply fzf-tab-complete
{
echo $ZSH_NAME $ZSH_VERSION
echo fzf-tab: $(-ftb-version)
typeset -p FZF_DEFAULT_OPTS
echo $commands[fzf] $(fzf --version)
} >&2
zle fzf-tab-complete
if (( debug_fd != -1 )); then
zle -M "fzf-tab-debug: Trace output left in $tmp"
fi
} always {
functions +t -- -ftb-complete _fzf-tab-apply fzf-tab-complete
(( debug_fd != -1 )) && exec 2>&$debug_fd {debug_fd}>&-
}
}
fzf-tab-complete() {
# this name must be ugly to avoid clashes
local -i _ftb_continue=1 _ftb_continue_last=0 _ftb_accept=0 ret=0
# hide the cursor until finishing completion, so that users won't see cursor up and down
# NOTE: MacOS Terminal doesn't support civis & cnorm
echoti civis >/dev/tty 2>/dev/null
while (( _ftb_continue )); do
local _ftb_choices=() _ftb_compcap=() _ftb_finish=0
_ftb_continue=0
local IN_FZF_TAB=1
{
zle .fzf-tab-orig-$_ftb_orig_widget || ret=$?
if (( ! ret && ! _ftb_finish )); then
zle _fzf-tab-apply || ret=$?
fi
} always {
IN_FZF_TAB=0
}
if (( _ftb_continue )); then
zle .split-undo
zle .reset-prompt
zle -R
zle fzf-tab-dummy
fi
done
echoti cnorm >/dev/tty 2>/dev/null
zle .redisplay
(( _ftb_accept )) && zle .accept-line
return $ret
}
# this function does nothing, it is used to be wrapped by other plugins like f-sy-h.
# this make it possible to call the wrapper function without causing any other side effects.
fzf-tab-dummy() { }
zle -N fzf-tab-debug
zle -N fzf-tab-complete
zle -N fzf-tab-dummy
# this is registered as a completion widget
# so that we can have a clean completion list to only insert the results user selected
zle -C _fzf-tab-apply complete-word _fzf-tab-apply
disable-fzf-tab() {
emulate -L zsh -o extended_glob
(( $+_ftb_orig_widget )) || return 0
bindkey '^I' $_ftb_orig_widget
case $_ftb_orig_list_grouped in
0) zstyle ':completion:*' list-grouped false ;;
1) zstyle ':completion:*' list-grouped true ;;
2) zstyle -d ':completion:*' list-grouped ;;
esac
unset _ftb_orig_widget _ftb_orig_list_groupded
# unhook compadd so that _approximate can work properply
unfunction compadd 2>/dev/null
functions[_main_complete]=$functions[_ftb__main_complete]
functions[_approximate]=$functions[_ftb__approximate]
# Don't remove .fzf-tab-orig-$_ftb_orig_widget as we won't be able to reliably
# create it if enable-fzf-tab is called again.
}
enable-fzf-tab() {
emulate -L zsh -o extended_glob
(( ! $+_ftb_orig_widget )) || disable-fzf-tab
typeset -g _ftb_orig_widget="${${$(builtin bindkey '^I')##* }:-expand-or-complete}"
if (( ! $+widgets[.fzf-tab-orig-$_ftb_orig_widget] )); then
# Widgets that get replaced by compinit.
local compinit_widgets=(
complete-word
delete-char-or-list
expand-or-complete
expand-or-complete-prefix
list-choices
menu-complete
menu-expand-or-complete
reverse-menu-complete
)
# Note: We prefix the name of the widget with '.' so that it doesn't get wrapped.
if [[ $widgets[$_ftb_orig_widget] == builtin &&
$compinit_widgets[(Ie)$_ftb_orig_widget] != 0 ]]; then
# We are initializing before compinit and being asked to fall back to a completion
# widget that isn't defined yet. Create our own copy of the widget ahead of time.
zle -C .fzf-tab-orig-$_ftb_orig_widget .$_ftb_orig_widget _main_complete
else
# Copy the widget before it's wrapped by zsh-autosuggestions and zsh-syntax-highlighting.
zle -A $_ftb_orig_widget .fzf-tab-orig-$_ftb_orig_widget
fi
fi
zstyle -t ':completion:*' list-grouped false
typeset -g _ftb_orig_list_grouped=$?
zstyle ':completion:*' list-grouped false
bindkey -M emacs '^I' fzf-tab-complete
bindkey -M viins '^I' fzf-tab-complete
bindkey -M emacs '^X.' fzf-tab-debug
bindkey -M viins '^X.' fzf-tab-debug
# make sure we can copy them
autoload +X -Uz _main_complete _approximate
# hook compadd
functions[compadd]=$functions[-ftb-compadd]
# hook _main_complete to trigger fzf-tab
functions[_ftb__main_complete]=$functions[_main_complete]
function _main_complete() { -ftb-complete "$@" }
# TODO: This is not a full support, see #47
# _approximate will also hook compadd
# let it call -ftb-compadd instead of builtin compadd so that fzf-tab can capture result
# make sure _approximate has been loaded.
functions[_ftb__approximate]=$functions[_approximate]
function _approximate() {
# if not called by fzf-tab, don't do anything with compadd
(( ! IN_FZF_TAB )) || unfunction compadd
_ftb__approximate
(( ! IN_FZF_TAB )) || functions[compadd]=$functions[-ftb-compadd]
}
}
toggle-fzf-tab() {
emulate -L zsh -o extended_glob
if (( $+_ftb_orig_widget )); then
disable-fzf-tab
else
enable-fzf-tab
fi
}
build-fzf-tab-module() {
{
pushd -q $FZF_TAB_HOME/modules
if -ftb-build-module $@; then
print -P "%F{green}%BThe module has been built successfully. Please restart zsh to apply it.%f%b"
else
print -P -u2 "%F{red}%BThe module building has failed. See the output above for details.%f%b"
return 1
fi
} always {
popd -q
}
}
zmodload zsh/zutil
zmodload zsh/mapfile
zmodload -F zsh/stat b:zstat
0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}"
0="${${(M)0:#/*}:-$PWD/$0}"
FZF_TAB_HOME="${0:A:h}"
source "$FZF_TAB_HOME"/lib/zsh-ls-colors/ls-colors.zsh fzf-tab-lscolors
typeset -ga _ftb_group_colors=(
$'\x1b[94m' $'\x1b[32m' $'\x1b[33m' $'\x1b[35m' $'\x1b[31m' $'\x1b[38;5;27m' $'\x1b[36m'
$'\x1b[38;5;100m' $'\x1b[38;5;98m' $'\x1b[91m' $'\x1b[38;5;80m' $'\x1b[92m'
$'\x1b[38;5;214m' $'\x1b[38;5;165m' $'\x1b[38;5;124m' $'\x1b[38;5;120m'
)
# init
() {
emulate -L zsh -o extended_glob
if (( ! $fpath[(I)$FZF_TAB_HOME/lib] )); then
fpath+=($FZF_TAB_HOME/lib)
fi
autoload -Uz is-at-least -- $FZF_TAB_HOME/lib/-#ftb*(:t)
if (( $+FZF_TAB_COMMAND || $+FZF_TAB_OPTS || $+FZF_TAB_QUERY || $+FZF_TAB_SINGLE_GROUP || $+fzf_tab_preview_init )) \
|| zstyle -m ":fzf-tab:*" command '*' \
|| zstyle -m ":fzf-tab:*" extra-opts '*'; then
print -P "%F{red}%B[fzf-tab] Sorry, your configuration is not supported anymore\n" \
"See https://github.com/Aloxaf/fzf-tab/pull/132 for more information%f%b"
fi
if [[ -n $FZF_TAB_HOME/modules/Src/aloxaf/fzftab.(so|bundle)(#qN) ]]; then
module_path+=("$FZF_TAB_HOME/modules/Src")
zmodload aloxaf/fzftab
if [[ $FZF_TAB_MODULE_VERSION != "0.2.2" ]]; then
zmodload -u aloxaf/fzftab
local rebuild
print -Pn "%F{yellow}fzftab module needs to be rebuild, rebuild now?[Y/n]:%f"
read -q rebuild
if [[ $rebuild == y ]]; then
build-fzf-tab-module
zmodload aloxaf/fzftab
fi
fi
fi
}
enable-fzf-tab
zle -N toggle-fzf-tab
# restore aliases
eval "$_ftb_aliases"
builtin unset _ftb_aliases
# restore options
(( ${#_ftb_opts} )) && setopt ${_ftb_opts[@]}
'builtin' 'unset' '_ftb_opts'