1# Copyright (c) 2017 Henry Chang 2 3__zic_fzf_prog() { 4 [ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ] \ 5 && echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf" 6} 7 8__zic_matched_subdir_list() { 9 local dir length seg starts_with_dir 10 if [[ "$1" == */ ]]; then 11 dir="$1" 12 if [[ "$dir" != / ]]; then 13 dir="${dir: : -1}" 14 fi 15 length=$(echo -n "$dir" | wc -c) 16 if [ "$dir" = "/" ]; then 17 length=0 18 fi 19 find -L "$dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \ 20 | cut -b $(( ${length} + 2 ))- | sed '/^$/d' | while read -r line; do 21 if [[ "${line[1]}" == "." ]]; then 22 continue 23 fi 24 echo "$line" 25 done 26 else 27 dir=$(dirname -- "$1") 28 length=$(echo -n "$dir" | wc -c) 29 if [ "$dir" = "/" ]; then 30 length=0 31 fi 32 seg=$(basename -- "$1") 33 starts_with_dir=$( \ 34 find -L "$dir" -mindepth 1 -maxdepth 1 -type d \ 35 2>/dev/null | cut -b $(( ${length} + 2 ))- | sed '/^$/d' \ 36 | while read -r line; do 37 if [[ "${seg[1]}" != "." && "${line[1]}" == "." ]]; then 38 continue 39 fi 40 if [[ "$line" == "$seg"* ]]; then 41 echo "$line" 42 fi 43 done 44 ) 45 if [ -n "$starts_with_dir" ]; then 46 echo "$starts_with_dir" 47 else 48 find -L "$dir" -mindepth 1 -maxdepth 1 -type d \ 49 2>/dev/null | cut -b $(( ${length} + 2 ))- | sed '/^$/d' \ 50 | while read -r line; do 51 if [[ "${seg[1]}" != "." && "${line[1]}" == "." ]]; then 52 continue 53 fi 54 if [[ "$line" == *"$seg"* ]]; then 55 echo "$line" 56 fi 57 done 58 fi 59 fi 60} 61 62_zic_list_generator() { 63 __zic_matched_subdir_list "${(Q)@[-1]}" | sort 64} 65 66_zic_complete() { 67 setopt localoptions nonomatch 68 local l matches fzf tokens base 69 70 l=$(_zic_list_generator $@) 71 72 if [ -z "$l" ]; then 73 zle ${__zic_default_completion:-expand-or-complete} 74 return 75 fi 76 77 fzf=$(__zic_fzf_prog) 78 79 if [ $(echo $l | wc -l) -eq 1 ]; then 80 matches=${(q)l} 81 else 82 matches=$(echo $l \ 83 | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} \ 84 --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS \ 85 --bind 'shift-tab:up,tab:down'" ${=fzf} \ 86 | while read -r item; do 87 echo -n "${(q)item} " 88 done) 89 fi 90 91 matches=${matches% } 92 if [ -n "$matches" ]; then 93 tokens=(${(z)LBUFFER}) 94 base="${(Q)@[-1]}" 95 if [[ "$base" != */ ]]; then 96 if [[ "$base" == */* ]]; then 97 base="$(dirname -- "$base")" 98 if [[ ${base[-1]} != / ]]; then 99 base="$base/" 100 fi 101 else 102 base="" 103 fi 104 fi 105 LBUFFER="${tokens[1]} " 106 if [ -n "$base" ]; then 107 base="${(q)base}" 108 if [ "${tokens[2][1]}" = "~" ]; then 109 base="${base/#$HOME/~}" 110 fi 111 LBUFFER="${LBUFFER}${base}" 112 fi 113 LBUFFER="${LBUFFER}${matches}/" 114 fi 115 zle redisplay 116 typeset -f zle-line-init >/dev/null && zle zle-line-init 117} 118 119zic-completion() { 120 setopt localoptions noshwordsplit noksh_arrays noposixbuiltins 121 local tokens cmd 122 123 tokens=(${(z)LBUFFER}) 124 cmd=${tokens[1]} 125 126 if [[ "$LBUFFER" =~ "^\ *cd$" ]]; then 127 zle ${__zic_default_completion:-expand-or-complete} 128 elif [ "$cmd" = cd ]; then 129 _zic_complete ${tokens[2,${#tokens}]/#\~/$HOME} 130 else 131 zle ${__zic_default_completion:-expand-or-complete} 132 fi 133} 134 135[ -z "$__zic_default_completion" ] && { 136 binding=$(bindkey '^I') 137 # $binding[(s: :w)2] 138 # The command substitution and following word splitting to determine the 139 # default zle widget for ^I formerly only works if the IFS parameter contains 140 # a space via $binding[(w)2]. Now it specifically splits at spaces, regardless 141 # of IFS. 142 [[ $binding =~ 'undefined-key' ]] || __zic_default_completion=$binding[(s: :w)2] 143 unset binding 144} 145 146zle -N zic-completion 147bindkey -M emacs '^I' zic-completion 148bindkey -M viins '^I' zic-completion 149