summaryrefslogtreecommitdiffstats
path: root/shell/completion.zsh
blob: 9dc4ce01480d250b19c85d112a01e509ec50b48c (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
149
150
151
152
153
154
#!/bin/zsh
#     ____      ____
#    / __/___  / __/
#   / /_/_  / / /_
#  / __/ / /_/ __/
# /_/   /___/_/-completion.zsh
#
# - $FZF_TMUX               (default: 1)
# - $FZF_TMUX_HEIGHT        (default: '40%')
# - $FZF_COMPLETION_TRIGGER (default: '**')
# - $FZF_COMPLETION_OPTS    (default: empty)

_fzf_path_completion() {
  local base lbuf find_opts fzf_opts suffix tail fzf dir leftover matches nnm
  base=$1
  lbuf=$2
  find_opts=$3
  fzf_opts=$4
  suffix=$5
  tail=$6
  [ ${FZF_TMUX:-1} -eq 1 ] && fzf="fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%}" || fzf="fzf"

  if ! setopt | grep nonomatch > /dev/null; then
    nnm=1
    setopt nonomatch
  fi
  dir="$base"
  while [ 1 ]; do
    if [ -z "$dir" -o -d ${~dir} ]; then
      leftover=${base/#"$dir"}
      leftover=${leftover/#\/}
      [ "$dir" = './' ] && dir=''
      dir=${~dir}
      matches=$(\find -L $dir* ${=find_opts} 2> /dev/null | ${=fzf} ${=FZF_COMPLETION_OPTS} ${=fzf_opts} -q "$leftover" | while read item; do
        printf "%q$suffix " "$item"
      done)
      matches=${matches% }
      if [ -n "$matches" ]; then
        LBUFFER="$lbuf$matches$tail"
        zle redisplay
      fi
      break
    fi
    dir=$(dirname "$dir")
    dir=${dir%/}/
  done
  [ -n "$nnm" ] && unsetopt nonomatch
}

_fzf_all_completion() {
  _fzf_path_completion "$1" "$2" \
    "-name .git -prune -o -name .svn -prune -o -type d -print -o -type f -print -o -type l -print" \
    "-m" "" " "
}

_fzf_dir_completion() {
  _fzf_path_completion "$1" "$2" \
    "-name .git -prune -o -name .svn -prune -o -type d -print" \
    "" "/" ""
}

_fzf_list_completion() {
  local prefix lbuf fzf_opts src fzf matches
  prefix=$1
  lbuf=$2
  fzf_opts=$3
  read -r src
  [ ${FZF_TMUX:-1} -eq 1 ] && fzf="fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%}" || fzf="fzf"

  matches=$(eval "$src" | ${=fzf} ${=FZF_COMPLETION_OPTS} ${=fzf_opts} -q "$prefix")
  if [ -n "$matches" ]; then
    LBUFFER="$lbuf$matches "
    zle redisplay
  fi
}

_fzf_telnet_completion() {
  _fzf_list_completion "$1" "$2" '+m' << "EOF"
  \grep -v '^\s*\(#\|$\)' /etc/hosts | \grep -Fv '0.0.0.0' | awk '{if (length($2) > 0) {print $2}}' | sort -u
EOF
}

_fzf_ssh_completion() {
  _fzf_list_completion "$1" "$2" '+m' << "EOF"
    cat <(cat ~/.ssh/config /etc/ssh/ssh_config 2> /dev/null | \grep -i ^host | \grep -v '*') <(\grep -v '^\s*\(#\|$\)' /etc/hosts | \grep -Fv '0.0.0.0') | awk '{if (length($2) > 0) {print $2}}' | sort -u
EOF
}

_fzf_env_var_completion() {
  _fzf_list_completion "$1" "$2" '+m' << "EOF"
  declare -xp | sed 's/=.*//' | sed 's/.* //'
EOF
}

_fzf_alias_completion() {
  _fzf_list_completion "$1" "$2" '+m' << "EOF"
  alias | sed 's/=.*//'
EOF
}

fzf-completion() {
  local tokens cmd prefix trigger tail fzf matches lbuf d_cmds

  # http://zsh.sourceforge.net/FAQ/zshfaq03.html
  tokens=(${=LBUFFER})
  if [ ${#tokens} -lt 1 ]; then
    zle expand-or-complete
    return
  fi

  cmd=${tokens[1]}

  # Explicitly allow for empty trigger.
  trigger=${FZF_COMPLETION_TRIGGER-'**'}
  [ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")

  tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
  # Kill completion (do not require trigger sequence)
  if [ $cmd = kill -a ${LBUFFER[-1]} = ' ' ]; then
    [ ${FZF_TMUX:-1} -eq 1 ] && fzf="fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%}" || fzf="fzf"
    matches=$(ps -ef | sed 1d | ${=fzf} ${=FZF_COMPLETION_OPTS} -m | awk '{print $2}' | tr '\n' ' ')
    if [ -n "$matches" ]; then
      LBUFFER="$LBUFFER$matches"
      zle redisplay
    fi
  # Trigger sequence given
  elif [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
    d_cmds=(cd pushd rmdir)

    [ -z "$trigger"      ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
    [ -z "${tokens[-1]}" ] && lbuf=$LBUFFER        || lbuf=${LBUFFER:0:-${#tokens[-1]}}

    if [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
      _fzf_dir_completion "$prefix" $lbuf
    elif [ $cmd = telnet ]; then
      _fzf_telnet_completion "$prefix" $lbuf
    elif [ $cmd = ssh ]; then
      _fzf_ssh_completion "$prefix" $lbuf
    elif [ $cmd = unset -o $cmd = export ]; then
      _fzf_env_var_completion "$prefix" $lbuf
    elif [ $cmd = unalias ]; then
      _fzf_alias_completion "$prefix" $lbuf
    else
      _fzf_all_completion "$prefix" $lbuf
    fi
  # Fall back to default completion
  else
    zle expand-or-complete
  fi
}

zle     -N   fzf-completion
bindkey '^I' fzf-completion