summaryrefslogtreecommitdiffstats
path: root/Completion/Core/_path_files
diff options
context:
space:
mode:
Diffstat (limited to 'Completion/Core/_path_files')
-rw-r--r--Completion/Core/_path_files311
1 files changed, 311 insertions, 0 deletions
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
new file mode 100644
index 000000000..83b6e8a09
--- /dev/null
+++ b/Completion/Core/_path_files
@@ -0,0 +1,311 @@
+#autoload
+
+# Utility function for in-path completion.
+# Supported arguments are: `-f', `-/', `-g <patterns>', `-J <group>',
+# `-V <group>', `-W paths', `-X explanation', and `-F <ignore>'. All but
+# the last have the same syntax and meaning as for `complist'. The
+# `-F <ignore>' option may be used to give a list of suffixes either by
+# giving the name of an array or literally by giving them in a string
+# surrounded by parentheses. Files with one of the suffixes thus given
+# are treated like files with one of the suffixes in the `fignore' array
+# in normal completion.
+#
+# This function uses the helper functions `_match_test' and `_match_pattern'.
+
+# First see if we should generate matches for the global matcher in use.
+
+_match_test _path_files || return
+
+# Yes, so...
+
+local nm prepaths str linepath realpath donepath patstr prepath testpath rest
+local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt
+local addpfx addsfx expl
+
+setopt localoptions nullglob rcexpandparam globdots extendedglob
+unsetopt markdirs globsubst shwordsplit nounset
+
+prepaths=('')
+ignore=()
+group=()
+sopt='-'
+gopt=''
+pats=()
+addpfx=()
+addsfx=()
+expl=()
+
+# Get the options.
+
+while getopts "P:S:W:F:J:V:X:f/g:" opt; do
+ case "$opt" in
+ P) addpfx=(-P "$OPTARG")
+ ;;
+ S) addsfx=(-S "$OPTARG")
+ ;;
+ W) tmp1="$OPTARG"
+ if [[ "$tmp1[1]" = '(' ]]; then
+ prepaths=( ${^=tmp1[2,-2]}/ )
+ else
+ 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[@]" '*(-/)')
+ ;;
+ g) gopt='-g'
+ pats=("$pats[@]" ${=OPTARG})
+ ;;
+ esac
+done
+
+# 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
+
+# str holds the whole string from the command line with a `*' between
+# the prefix and the suffix.
+
+str="${PREFIX:q}*${SUFFIX:q}"
+
+# If the string began with a `~', the quoting turned this into `\~',
+# remove the slash.
+
+[[ "$str" = \\\~* ]] && str="$str[2,-1]"
+
+# We will first try normal completion called with `complist', but only if we
+# weren't given a `-F' option.
+
+if (( ! $#ignore )); then
+ # First build an array containing the `-W' option, if there is any and we
+ # want to use it. We don't want to use it if the string from the command line
+ # is a absolute path or relative to the current directory.
+
+ if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then
+ tmp1=()
+ else
+ tmp1=(-W "( $prepaths )")
+ fi
+
+ # Now call complist.
+
+ nm=$NMATCHES
+ if [[ -z "$gopt" ]]; then
+ complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt
+ else
+ complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt -g "$pats"
+ fi
+
+ # If this generated any matches, we don't want to do in-path completion.
+
+ [[ -nmatches nm ]] || return
+
+ # No `-F' option, so we want to use `fignore'.
+
+ ignore=(-F fignore)
+fi
+
+# Now let's have a closer look at the string to complete.
+
+if [[ "$str[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="${str%%/*}/"
+ eval realpath\=$linepath
+ str="${str#*/}"
+ donepath=''
+ prepaths=( '' )
+else
+ # If the string does not start with a `~' we don't remove a prefix from the
+ # string.
+
+ liniepath=''
+ realpath=''
+
+ if [[ "$str[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'.
+
+ str="$str[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'.
+
+ [[ "$str" = (.|..)/* ]] && prepaths=( '' )
+ donepath=''
+ fi
+fi
+
+# First we skip over all pathname components in `str' which really exist in
+# the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and
+# `lib5'. Pathname components skipped this way are taken from `str' and added
+# to `donepath'.
+
+while [[ "$str" = */* ]] do
+ [[ -e "$realpath$donepath${str%%/*}" ]] || break
+ donepath="$donepath${str%%/*}/"
+ str="${str#*/}"
+done
+
+# Now build the glob pattern by calling `_match_pattern'.
+patstr="$str"
+matchflags=""
+_match_pattern _path_files patstr matchflags
+
+# We almost expect the pattern to have changed `..' into `*.*.', `/.' into
+# `/*.', and probably to contain two or more consecutive `*'s. Since these
+# have special meaning for globbing, we remove them. But before that, we
+# add the pattern for matching any characters before a slash.
+
+patstr="$patstr:gs-/-*/-:gs/*.*.//:gs-/*.-/.-:gs/**/*/"
+
+# Finally, generate the matches. First we loop over all the paths from `-W'.
+# Note that in this loop `str' is used as a modifyable version of `patstr'
+# and `testpath' is a modifyable version of `donepath'.
+
+for prepath in "$prepaths[@]"; do
+ str="$patstr"
+ testpath="$donepath"
+
+ # The second loop tests the components of the path in `str' to get the
+ # possible matches.
+
+ while [[ "$str" = */* ]] do
+ # `rest' is the pathname after the first slash that is left. In `tmp1'
+ # we get the globbing matches for the pathname component currently
+ # handled.
+
+ rest="${str#*/}"
+ tmp1="${prepath}${realpath}${testpath}${~matchflags}${str%%/*}(-/)"
+ tmp1=( $~tmp1 )
+
+ if [[ $#tmp1 -eq 0 ]]; then
+ # If this didn't produce any matches, we don't need to test this path
+ # any further, so continue with the next `-W' path, if any.
+
+ continue 2
+ elif [[ $#tmp1 -gt 1 ]]; then
+ # If it produced more than one match, we want to remove those which
+ # don't have possible following pathname components matching the
+ # rest of the string we are completing. (The case with only one
+ # match is handled below.)
+ # In `collect' we will collect those of the produced pathnames that
+ # have a matching possible path-suffix. In `suffixes' we build an
+ # array containing strings build from the rest of the string to
+ # complete and the glob patterns we were given as arguments.
+
+ collect=()
+ suffixes=( $rest$^pats )
+ suffixes=( "${(@)suffixes:gs.**.*.}" )
+
+ # In the loop the prefixes from the `tmp1' array produced above and
+ # the suffixes we just built are used to produce possible matches
+ # via globbing.
+
+ for i in $tmp1; do
+ tmp2=( ${~i}/${~matchflags}${~suffixes} )
+ [[ $#tmp2 -ne 0 ]] && collect=( $collect $i )
+ done
+
+ # If this test showed that none of the matches from the glob in `tmp1'
+ # has a possible sub-path matching what's on the line, we give up and
+ # continue with the next `-W' path.
+
+ if [[ $#collect -eq 0 ]]; then
+ continue 2
+ elif [[ $#collect -ne 1 ]]; then
+ # If we have more than one possible match, this means that the
+ # pathname component currently handled is ambiguous, so we give
+ # it to the completion code.
+ # First we build the full path prefix in `tmp1'.
+
+ tmp1="$prepath$realpath$testpath"
+
+ # Now produce all matching pathnames in `collect'.
+
+ collect=( ${~collect}/${~matchflags}${~suffixes} )
+
+ # And then remove the common path prefix from all these matches.
+
+ collect=( ${collect#$tmp1} )
+
+ # Finally, we add all these matches with the common (unexpanded)
+ # pathprefix (the `-p' option), the path-prefix (the `-W' option)
+ # to allow the completion code to test file type, and the path-
+ # suffix (the `-s' option). We also tell the completion code that
+ # these are file names and that `fignore' should be used as usual
+ # (the `-f' and `-F' options).
+
+ for i in $collect; do
+ compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$tmp1" -s "/${i#*/}" -f "$ignore[@]" - "${i%%/*}"
+ done
+
+ # We have just finished handling all the matches from above, so we
+ # can continue with the next `-W' path.
+
+ continue 2
+ fi
+ # We reach this point if only one of the path prefixes in `tmp1'
+ # has a existing path-suffix matching the string from the line.
+ # In this case we accept this match and continue with the next
+ # path-name component.
+
+ tmp1=( "$collect[1]" )
+ fi
+ # This is also reached if the first globbing produced only one match
+ # in this case we just continue with the next pathname component, too.
+
+ tmp1="$tmp1[1]"
+ testpath="$testpath${tmp1##*/}/"
+ str="$rest"
+ done
+
+ # We are here if all pathname components except the last one (which is still
+ # not tested) are unambiguous. So we add matches with the full path prefix,
+ # no path suffix, the `-W' we are currently handling, all the matches we
+ # can produce in this directory, if any.
+
+ tmp1="$prepath$realpath$testpath"
+ suffixes=( $str$^pats )
+ suffixes=( "${(@)suffixes:gs.**.*.}" )
+ tmp2=( ${~tmp1}${~matchflags}${~suffixes} )
+ if [[ $#tmp2 -eq 0 && "$sopt" = */* ]]; then
+ [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]"
+ compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -f - "$linepath$testpath"
+ else
+ compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${(@)tmp2#$tmp1}
+ fi
+done