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
|
#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 ! zmodload -F zsh/stat b:zstat 2>/dev/null; then
_wanted "$tag" expl "$desc" compadd $__gopts $@ && ret=0
return ret
fi
typeset REPLY
typeset -a matches files
_canonical_paths_get_canonical_path() {
typeset newfile
typeset -A seen
REPLY=$1
# Guard against loops.
while [[ -z ${seen[$REPLY]} ]]; do
seen[$REPLY]=1
newfile=$(zstat +link $REPLY 2>/dev/null)
if [[ -n $newfile ]]; then
REPLY=$newfile
else
break
fi
done
}
if (( $__opts[(I)-N] )); then
files=($@)
else
for __index in $@; do
_canonical_paths_get_canonical_path $__index
files+=($REPLY)
done
fi
_canonical_paths_add_paths () {
local origpref=$1 expref rltrim curpref canpref subdir
[[ $2 != add ]] && matches=()
expref=${~origpref}
[[ $origpref == (|*/). ]] && rltrim=.
curpref=${${expref%$rltrim}:-./}
if zstat $curpref >&/dev/null; then
_canonical_paths_get_canonical_path $curpref
canpref=$REPLY
else
canpref=$curpref
fi
[[ $curpref == */ && $canpref == *[^/] ]] && canpref+=/
canpref+=$rltrim
[[ $expref == *[^/] && $canpref == */ ]] && origpref+=/
matches+=(${${(M)files:#$canpref*}/$canpref/$origpref})
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
|