summaryrefslogtreecommitdiff
path: root/plugins/zsh-interactive-cd/zsh-interactive-cd.plugin.zsh
blob: b0520c23936b25db6ec40cf18b1a41f23792c96b (plain)
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
# Copyright (c) 2017 Henry Chang

__zic_fzf_prog() {
  [ -n "$TMUX_PANE" ] && [ "${FZF_TMUX:-0}" != 0 ] && [ ${LINES:-40} -gt 15 ] \
    && echo "fzf-tmux -d${FZF_TMUX_HEIGHT:-40%}" || echo "fzf"
}

__zic_matched_subdir_list() {
  local dir length seg starts_with_dir
  if [[ "$1" == */ ]]; then
    dir="$1"
    if [[ "$dir" != / ]]; then
      dir="${dir: : -1}"
    fi
    length=$(echo -n "$dir" | wc -c)
    if [ "$dir" = "/" ]; then
      length=0
    fi
    find -L "$dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null \
        | cut -b $(( ${length} + 2 ))- | sed '/^$/d' | while read -r line; do
      if [[ "${line[1]}" == "." ]]; then
        continue
      fi
      echo "$line"
    done
  else
    dir=$(dirname -- "$1")
    length=$(echo -n "$dir" | wc -c)
    if [ "$dir" = "/" ]; then
      length=0
    fi
    seg=$(basename -- "$1")
    starts_with_dir=$( \
      find -L "$dir" -mindepth 1 -maxdepth 1 -type d \
          2>/dev/null | cut -b $(( ${length} + 2 ))- | sed '/^$/d' \
          | while read -r line; do
        if [[ "${seg[1]}" != "." && "${line[1]}" == "." ]]; then
          continue
        fi
        if [[ "$line" == "$seg"* ]]; then
          echo "$line"
        fi
      done
    )
    if [ -n "$starts_with_dir" ]; then
      echo "$starts_with_dir"
    else
      find -L "$dir" -mindepth 1 -maxdepth 1 -type d \
          2>/dev/null | cut -b $(( ${length} + 2 ))- | sed '/^$/d' \
          | while read -r line; do
        if [[ "${seg[1]}" != "." && "${line[1]}" == "." ]]; then
          continue
        fi
        if [[ "$line" == *"$seg"* ]]; then
          echo "$line"
        fi
      done
    fi
  fi
}

_zic_list_generator() {
  __zic_matched_subdir_list "${(Q)@[-1]}" | sort
}

_zic_complete() {
  setopt localoptions nonomatch
  local l matches fzf tokens base

  l=$(_zic_list_generator $@)

  if [ -z "$l" ]; then
    zle ${__zic_default_completion:-expand-or-complete}
    return
  fi

  fzf=$(__zic_fzf_prog)

  if [ $(echo $l | wc -l) -eq 1 ]; then
    matches=${(q)l}
  else
    matches=$(echo $l \
        | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} \
          --reverse $FZF_DEFAULT_OPTS $FZF_COMPLETION_OPTS \
          --bind 'shift-tab:up,tab:down'" ${=fzf} \
        | while read -r item; do
      echo -n "${(q)item} "
    done)
  fi

  matches=${matches% }
  if [ -n "$matches" ]; then
    tokens=(${(z)LBUFFER})
    base="${(Q)@[-1]}"
    if [[ "$base" != */ ]]; then
      if [[ "$base" == */* ]]; then
        base="$(dirname -- "$base")"
        if [[ ${base[-1]} != / ]]; then
          base="$base/"
        fi
      else
        base=""
      fi
    fi
    LBUFFER="${tokens[1]} "
    if [ -n "$base" ]; then
      base="${(q)base}"
      if [ "${tokens[2][1]}" = "~" ]; then
        base="${base/#$HOME/~}"
      fi
      LBUFFER="${LBUFFER}${base}"
    fi
    LBUFFER="${LBUFFER}${matches}/"
  fi
  zle redisplay
  typeset -f zle-line-init >/dev/null && zle zle-line-init
}

zic-completion() {
  setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
  local tokens cmd

  tokens=(${(z)LBUFFER})
  cmd=${tokens[1]}

  if [[ "$LBUFFER" =~ "^\ *cd$" ]]; then
    zle ${__zic_default_completion:-expand-or-complete}
  elif [ "$cmd" = cd ]; then
    _zic_complete ${tokens[2,${#tokens}]/#\~/$HOME}
  else
    zle ${__zic_default_completion:-expand-or-complete}
  fi
}

[ -z "$__zic_default_completion" ] && {
  binding=$(bindkey '^I')
  # $binding[(s: :w)2]
  # The command substitution and following word splitting to determine the
  # default zle widget for ^I formerly only works if the IFS parameter contains
  # a space via $binding[(w)2]. Now it specifically splits at spaces, regardless
  # of IFS.
  [[ $binding =~ 'undefined-key' ]] || __zic_default_completion=$binding[(s: :w)2]
  unset binding
}

zle -N zic-completion
bindkey -M emacs '^I' zic-completion
bindkey -M viins '^I' zic-completion