1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-03 08:11:03 +02:00
zsh/Completion/Core/_path_files
2000-01-09 21:56:08 +00:00

519 lines
14 KiB
Text

#autoload
# Utility function for in-path completion. This allows `/u/l/b<TAB>'
# to complete to `/usr/local/bin'.
local linepath realpath donepath prepath testpath exppath
local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre
local pats haspats=no ignore group expl addpfx addsfx remsfx
local nm=$compstate[nmatches] menu mspec matcher mopts atmp sort match
typeset -U prepaths exppaths
setopt localoptions nullglob rcexpandparam
unsetopt markdirs globsubst shwordsplit nounset
local sopt='-' gopt='' opt
exppaths=()
prepaths=('')
ignore=()
group=()
pats=()
addpfx=()
addsfx=()
remsfx=()
expl=()
matcher=()
mopts=()
# Get the options.
while getopts "P:S:qr:R:W:F:J:V:X:f/g:M:12n" opt; do
case "$opt" in
[12n]) mopts=( "$mopts[@]" "-$opt" )
;;
P) addpfx=(-P "$OPTARG")
;;
S) addsfx=(-S "$OPTARG")
;;
q) tmp1=yes
;;
[rR]) remsfx=("-$opt" "$OPTARG")
;;
W) tmp1="$OPTARG"
if [[ "$tmp1[1]" = '(' ]]; then
prepaths=( ${^=tmp1[2,-2]%/}/ )
elif [[ "$tmp1[1]" = '/' ]]; then
prepaths=( "$tmp1/" )
else
# In this case the variable should be an array, so
# don't use an extra ${=...}.
prepaths=( ${(P)^tmp1%/}/ )
(( ! $#prepaths )) && prepaths=( ${tmp1%/}/ )
fi
(( ! $#prepaths )) && prepaths=( '' )
;;
F) tmp1="$OPTARG"
if [[ "$tmp1[1]" = '(' ]]; then
ignore=( ${^=tmp1[2,-2]}/ )
else
ignore=( ${(P)${tmp1}} )
fi
(( $#ignore )) && ignore=(-F "( $ignore )")
;;
[JV]) group=("-$opt" "$OPTARG")
;;
X) expl=(-X "$OPTARG")
;;
f) sopt="${sopt}f"
pats=("$pats[@]" '*')
;;
/) sopt="${sopt}/"
pats=("$pats[@]" '*(-/)')
haspats=yes
;;
g) gopt='-g'
pats=("$pats[@]" ${=OPTARG})
haspats=yes
;;
M) mspec="$OPTARG"
matcher=(-M "$OPTARG")
;;
esac
done
if (( ! ( $#group + $#expl ) )); then
if [[ -z "$gopt" && "$sopt" = -/ ]]; then
_description directories expl directory
else
_description files expl file
fi
tmp1=$expl[(I)-M]
if (( tmp1 )); then
mspec="$mspec $expl[1+tmp1]"
if (( $#matcher )); then
matcher[2]="$matcher[2] $expl[1+tmp1]"
else
matcher=(-M "$expl[1+tmp1]")
fi
fi
fi
[[ -n "$tmp1" && $#addsfx -ne 0 ]] && addsfx[1]=-qS
# If we were given no file selection option, we behave as if we were given
# a `-f'.
if [[ "$sopt" = - ]]; then
if [[ -z "$gopt" ]]; then
sopt='-f'
pats=('*')
else
unset sopt
fi
fi
if zstyle -s ":completion${curcontext}:files" sort tmp1; then
case "$tmp1" in
*size*) sort=oL;;
*links*) sort=ol;;
*(time|date|modi)*) sort=om;;
*access*) sort=oa;;
*(inode|change)*) sort=oc;;
*) sort=on;;
esac
[[ "$tmp1" = *rev* ]] && sort[1]=O
if [[ "$sort" = on ]]; then
sort=''
else
group=( "${(@)group/#-J/-V}" )
expl=( "${(@)expl/#-J/-V}" )
tmp2=()
for tmp1 in "$pats[@]"; do
if [[ "$tmp1" = (#b)(?*)(\(\([^\|~]##\)\)) ]]; then
tmp2=( "$tmp2[@]" "${match[1]}((${sort}${match[2][3,-1]}" )
elif [[ "$tmp1" = (#b)(?*)(\([^\|~]##\)) ]]; then
tmp2=( "$tmp2[@]" "${match[1]}(${sort}${match[2][2,-1]}" )
else
tmp2=( "$tmp2[@]" "${tmp1}(${sort})" )
fi
done
pats=( "$tmp2[@]" )
fi
fi
# We get the prefix and the suffix from the line and save the whole
# original string. Then we see if we will do menucompletion.
pre="$PREFIX"
suf="$SUFFIX"
opre="$PREFIX"
osuf="$SUFFIX"
orig="${PREFIX}${SUFFIX}"
eorig="$orig"
[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
( $#compstate[pattern_match] -ne 0 &&
"${orig#\~}" != "${${orig#\~}:q}" ) ]] && menu=yes
# If given no `-F' option, we want to use the `ignored-suffixes'-style.
if [[ $#ignore -eq 0 && -z $gopt ]]; then
if zstyle -a ":completion${curcontext}:files" ignored-suffixes ignore; then
ignore=(-F "( $ignore )")
else
# For now we still use the fignore parameter.
# This may be removed some day.
ignore=(-F fignore)
fi
fi
# Now let's have a closer look at the string to complete.
if [[ "$pre[1]" = \~ ]]; then
# It begins with `~', so remember anything before the first slash to be able
# to report it to the completion code. Also get an expanded version of it
# (in `realpath'), so that we can generate the matches. Then remove that
# prefix from the string to complete, set `donepath' to build the correct
# paths and make sure that the loop below is run only once with an empty
# prefix path by setting `prepaths'.
linepath="${pre%%/*}/"
realpath=$~linepath
[[ "$realpath" = "$linepath" ]] && return 1
pre="${pre#*/}"
orig="${orig#*/}"
donepath=''
prepaths=( '' )
elif [[ "$pre" = *\$*/* ]]; then
# If there is a parameter expansion in the word from the line, we try
# to complete the beast by expanding the prefix and completing anything
# after the first slash after the parameter expansion.
# This fails for things like `f/$foo/b/<TAB>' where the first `f' is
# meant as a partial path.
linepath="${(M)pre##*\$[^/]##/}"
realpath=${(e)~linepath}
[[ "$realpath" = "$linepath" ]] && return 1
pre="${pre#${linepath}}"
i="${#linepath//[^\\/]}"
orig="${orig[1,(in:i:)/][1,-2]}"
donepath=''
prepaths=( '' )
else
# If the string does not start with a `~' we don't remove a prefix from the
# string.
linepath=''
realpath=''
if [[ "$pre[1]" = / ]]; then
# If it is a absolut path name, we remove the first slash and put it in
# `donepath' meaning that we treat it as the path that was already handled.
# Also, we don't use the paths from `-W'.
pre="$pre[2,-1]"
orig="$orig[2,-1]"
donepath='/'
prepaths=( '' )
else
# The common case, we just use the string as it is, unless it begins with
# `./' or `../' in which case we don't use the paths from `-W'.
[[ "$pre" = (.|..)/* ]] && prepaths=( '' )
donepath=''
fi
fi
# Now we generate the matches. First we loop over all prefix paths given
# with the `-W' option.
for prepath in "$prepaths[@]"; do
# Get local copies of the prefix, suffix, and the prefix path to use
# in the following loop, which walks through the pathname components
# in the string from the line.
tpre="$pre"
tsuf="$suf"
testpath="$donepath"
tmp1=( "$prepath$realpath$donepath" )
while true; do
# Skip over `./' and `../'.
if [[ "$tpre" = (.|..)/* ]]; then
tmp1=( ${^tmp1}${tpre%%/*}/ )
tpre="${tpre#*/}"
continue
fi
# Get the prefix and suffix for matching.
if [[ "$tpre" = */* ]]; then
PREFIX="${tpre%%/*}"
SUFFIX=""
else
PREFIX="${tpre}"
SUFFIX="${tsuf%%/*}"
fi
# Get the matching files by globbing.
if [[ "$tpre$tsuf" = */* ]]; then
tmp2=( ${^tmp1}*(-/) )
if [[ ! -o globdots && "$PREFIX" = .* ]]; then
tmp2=( "$tmp2[@]" ${^tmp1}.*(-/) )
if zstyle -s ":completion${curcontext}:paths" special-dirs atmp; then
if [[ "$atmp" = (yes|true|1|on) ]]; then
tmp2=( "$tmp2[@]" . .. )
elif [[ "$atmp" = .. ]]; then
tmp2=( "$tmp2[@]" .. )
fi
fi
fi
else
tmp2=( ${^tmp1}${^~pats} )
if [[ ! -o globdots && "$PREFIX" = .* ]]; then
tmp2=( "$tmp2[@]" ${^tmp1}.${^~pats} )
if [[ "$sopt" = */* ]] &&
zstyle -s ":completion${curcontext}:paths" special-dirs atmp; then
if [[ "$atmp" = (yes|true|1|on) ]]; then
tmp2=( "$tmp2[@]" . .. )
elif [[ "$atmp" = .. ]]; then
tmp2=( "$tmp2[@]" .. )
fi
fi
fi
fi
tmp1=( "$tmp2[@]" )
if [[ -n "$PREFIX$SUFFIX" ]]; then
# See which of them match what's on the line.
builtin compadd -D tmp1 "$ignore[@]" "$matcher[@]" - "${(@)tmp1:t}"
if [[ $#tmp1 -eq 0 && -n "$_comp_correct" ]]; then
tmp1=( "$tmp2[@]" )
compadd -D tmp1 "$ignore[@]" "$matcher[@]" - "${(@)tmp2:t}"
fi
# If no file matches, save the expanded path and continue with
# the outer loop.
if [[ $#tmp1 -eq 0 ]]; then
if [[ "$tmp2[1]" = */* ]]; then
tmp2=( "${(@)tmp2#${prepath}${realpath}}" )
if [[ "$tmp2[1]" = */* ]]; then
tmp2=( "${(@)tmp2:h}" )
compquote tmp2
if [[ "$tmp2" = */ ]]; then
exppaths=( "$exppaths[@]" ${^tmp2}${tpre}${tsuf} )
else
exppaths=( "$exppaths[@]" ${^tmp2}/${tpre}${tsuf} )
fi
else
exppaths=( "$exppaths[@]" ${tpre}${tsuf} )
fi
fi
continue 2
fi
elif (( ! $#tmp1 )); then
# A little extra hack: if we were completing `foo/<TAB>' and `foo'
# contains no files, this will normally produce no matches and other
# completers might think that's it's their time now. But if the next
# completer is _correct or something like that, this will result in
# an attempt to correct a valid directory name. So we just add the
# original string in such a case so that the command line doesn't
# change but other completers still think there are matches.
# We do this only if we weren't given a `-g' or `-/' option because
# otherwise this would keep `_files' from completing all filenames
# if none of the patterns match.
if [[ -z "$tpre$tsuf" && -n "$pre$suf" ]]; then
tmp1=( "$tmp2[@]" )
addsfx=(-S '')
remsfx=()
break;
elif [[ "$haspats" = no && -z "$tpre$tsuf" &&
"$pre" = */ && -z "$suf" ]]; then
PREFIX="${opre}"
SUFFIX="${osuf}"
compadd -nQS '' - "$linepath$donepath$orig"
tmp4=-
fi
continue 2
fi
# Step over to the next component, if any.
if [[ "$tpre" = */* ]]; then
tpre="${tpre#*/}"
elif [[ "$tsuf" = */* ]]; then
tpre="${tsuf#*/}"
tsuf=""
else
break
fi
# There are more components, so add a slash to the files we are
# collecting.
tmp1=( ${^tmp1}/ )
done
# The next loop searches the first ambiguous component.
tmp3="$pre$suf"
tpre="$pre"
tsuf="$suf"
tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" )
while true; do
# First we check if some of the files match the original string
# for this component. If there are some we remove all other
# names. This avoids having `foo' complete to `foo' and `foobar'.
if [[ "$tmp3" = */* ]]; then
tmp4=( "${(@M)tmp1:#${tmp3%%/*}/*}" )
if (( $#tmp4 )); then
tmp1=( "$tmp4[@]" )
fi
fi
# Next we see if this component is ambiguous.
if [[ "$tmp3" = */* ]]; then
tmp4=( "${(@)tmp1:#${tmp1[1]%%/*}/*}" )
else
tmp4=( "${(@)tmp1:#${tmp1[1]}}" )
fi
if (( $#tmp4 )); then
# It is. For menucompletion we now add the possible completions
# for this component with the unambigous prefix we have built
# and the rest of the string from the line as the suffix.
# For normal completion we add the rests of the filenames
# collected as the suffixes to make the completion code expand
# it as far as possible.
if [[ "$tpre" = */* ]]; then
PREFIX="${donepath}${linepath}${cpre}${tpre%%/*}"
SUFFIX="/${tsuf#*/}"
else
PREFIX="${donepath}${linepath}${cpre}${tpre}"
SUFFIX="${tsuf}"
fi
tmp4="$testpath"
compquote tmp1 tmp4
if [[ -n $menu ]] ||
! zstyle -t ":completion${curcontext}:paths" expand suffix; then
zstyle -t ":completion${curcontext}:paths" cursor &&
compstate[to_end]=''
if [[ "$tmp3" = */* ]]; then
compadd -Qf "$mopts[@]" -p "$linepath$tmp4" -s "/${tmp3#*/}" \
-W "$prepath$realpath$testpath" "$ignore[@]" \
"$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
-M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \
- "${(@)tmp1%%/*}"
else
compadd -Qf "$mopts[@]" -p "$linepath$tmp4" \
-W "$prepath$realpath$testpath" "$ignore[@]" \
"$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
-M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \
- "$tmp1[@]"
fi
else
if [[ "$tmp3" = */* ]]; then
atmp=( -Qf "$mopts[@]" -p "$linepath$tmp4"
-W "$prepath$realpath$testpath" "$ignore[@]"
"$addpfx[@]" "$addsfx[@]" "$remsfx[@]"
-M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" )
for i in "$tmp1[@]"; do
compadd "$atmp[@]" -s "/${i#*/}" - "${i%%/*}"
done
else
compadd -Qf "$mopts[@]" -p "$linepath$tmp4" \
-W "$prepath$realpath$testpath" "$ignore[@]" \
"$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
-M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \
- "$tmp1[@]"
fi
fi
tmp4=-
break
fi
# If we have checked all components, we stop now and add the
# strings collected after the loop.
if [[ "$tmp3" != */* ]]; then
tmp4=""
break
fi
# Otherwise we add the unambiguous component to `testpath' and
# take it from the filenames.
testpath="${testpath}${tmp1[1]%%/*}/"
tmp1=( "${(@)tmp1#*/}" )
tmp3="${tmp3#*/}"
if [[ "$tpre" = */* ]]; then
cpre="${cpre}${tpre%%/*}/"
tpre="${tpre#*/}"
elif [[ "$tsuf" = */* ]]; then
cpre="${cpre}${tpre}/"
tpre="${tsuf#*/}"
tsuf=""
else
tpre=""
tsuf=""
fi
done
if [[ -z "$tmp4" ]]; then
if [[ "$osuf" = */* ]]; then
PREFIX="${opre}${osuf}"
SUFFIX=""
else
PREFIX="${opre}"
SUFFIX="${osuf}"
fi
tmp4="$testpath"
compquote tmp4 tmp1
compadd -Qf "$mopts[@]" -p "$linepath$tmp4" \
-W "$prepath$realpath$testpath" "$ignore[@]" \
"$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
-M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \
- "$tmp1[@]"
fi
done
# If we are configured to expand paths as far as possible and we collected
# expanded paths that are different from the string on the line, we add
# them as possible matches.
exppaths=( "${(@)exppaths:#$eorig}" )
if zstyle -t ":completion${curcontext}:paths" expand prefix &&
[[ $#exppaths -gt 0 && nm -eq compstate[nmatches] ]]; then
PREFIX="${opre}"
SUFFIX="${osuf}"
compadd -Q "$mopts[@]" -S '' "$group[@]" "$expl[@]" \
-M "r:|/=* r:|=* $mspec" -p "$linepath" - "$exppaths[@]"
fi
[[ nm -ne compstate[nmatches] ]]