zsh-interactive-cd.plugin.zsh 3.8 KB

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