about summary refs log tree commit diff
path: root/Completion/Unix/Type/_canonical_paths
blob: c33892c8426c5a2b5a48b39e8bc8492d19fdeb01 (plain) (blame)
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
#autoload

# This completion function completes all paths given to it, and also tries to
# offer completions which point to the same file as one of the paths given
# (relative path when an absolute path is given, and vice versa; when ..'s are
# present in the word to be completed, and some paths got from symlinks.

# Usage: _canonical_paths [-A var] [-N] [-MJV12nfX] tag desc paths...

# -A, if specified, takes the paths from the array variable specified. Paths can
# also be specified on the command line as shown above. -N, if specified,
# prevents canonicalizing the paths given before using them for completion, in
# case they are already so. `tag' and `desc' arguments are well, obvious :) In
# addition, the options -M, -J, -V, -1, -2, -n, -F, -X are passed to compadd.

local __index
typeset -a __gopts __opts

zparseopts -D -a __gopts M: J: V: 1 2 n F: X: A:=__opts N=__opts

: ${1:=canonical-paths} ${2:=path}

__index=$__opts[(I)-A]
(( $__index )) && set -- $@ ${(P)__opts[__index+1]}

local expl ret=1 tag=$1 desc=$2

shift 2

if (( ! $+commands[readlink] )); then
  _wanted "$tag" expl "$desc" compadd $__gopts $@ && ret=0
  return ret
fi

typeset -a matches files

if (( $__opts[(I)-N] )); then
  files=($@)
else
  for __index in $@; do
    files+=$(readlink -qf $__index)
  done
fi

_canonical_paths_add_paths () {
  local origpref=$1 expref rltrim curpref canpref subdir
  [[ $2 != add ]] && matches=()
  expref=${~origpref}
  [[ $origpref == (|*/). ]] && rltrim=.
  curpref=${${expref%$rltrim}:-./}
  canpref=$(readlink -qf $curpref)
  if [[ $? -eq 0 ]]; then
    [[ $curpref == */ && $canpref == *[^/] ]] && canpref+=/
    canpref+=$rltrim
    [[ $expref == *[^/] && $canpref == */ ]] && origpref+=/
    matches+=(${${(M)files:#$canpref*}/$canpref/$origpref})
  fi
  for subdir in $expref?*(@); do
    _canonical_paths_add_paths ${subdir/$expref/$origpref} add
  done
}

local base=$PREFIX
typeset -i blimit

_canonical_paths_add_paths $base

if [[ -z $base ]]; then
  _canonical_paths_add_paths / add
elif [[ $base == ..(/.(|.))#(|/) ]]; then

  # This style controls how many parent directory links (..) to chase searching
  # for possible completions. The default is 8. Note that this chasing is
  # triggered only when the user enters atleast a .. and the path completed
  # contains only . or .. components. A value of 0 turns off .. link chasing
  # altogether.

  zstyle -s ":completion:${curcontext}:$tag" \
    canonical-paths-back-limit blimit || blimit=8

  if [[ $base != */ ]]; then
    [[ $base != *.. ]] && base+=.
    base+=/
  fi
  until [[ $base.. -ef $base || blimit -le 0 ]]; do
    base+=../
    _canonical_paths_add_paths $base add
    blimit+=-1
  done
fi

_wanted "$tag" expl "$desc" compadd $__gopts -Q -U -a matches && ret=0

return ret