# 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'