about summary refs log tree commit diff
path: root/Completion/Core
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:18:42 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:18:42 +0000
commit7a0415cfd70a02b2280d27556c6c54cef1c86e1a (patch)
tree37a88a1c4611ee37f2a3209873fc9a34a2624587 /Completion/Core
parent904b939cbd81a542303da2c58288b95b153106f5 (diff)
downloadzsh-7a0415cfd70a02b2280d27556c6c54cef1c86e1a.tar.gz
zsh-7a0415cfd70a02b2280d27556c6c54cef1c86e1a.tar.xz
zsh-7a0415cfd70a02b2280d27556c6c54cef1c86e1a.zip
zsh-3.1.5-pws-11 zsh-3.1.5-pws-11
Diffstat (limited to 'Completion/Core')
-rw-r--r--Completion/Core/_comp_parts25
-rw-r--r--Completion/Core/_compalso6
-rw-r--r--Completion/Core/_files7
-rw-r--r--Completion/Core/_main_complete219
-rw-r--r--Completion/Core/_multi_parts201
-rw-r--r--Completion/Core/_normal30
-rw-r--r--Completion/Core/_path_files110
7 files changed, 502 insertions, 96 deletions
diff --git a/Completion/Core/_comp_parts b/Completion/Core/_comp_parts
index 7c24fd19d..d58669f29 100644
--- a/Completion/Core/_comp_parts
+++ b/Completion/Core/_comp_parts
@@ -21,11 +21,11 @@
 # `_match_test' and `_match_pattern' for this.
 
 local str arr sep test testarr tmparr prefix suffixes matchers autosuffix
-local matchflags opt group expl
+local matchflags opt group expl nm=$compstate[nmatches]
 
 # Test if we should use this function for the global matcher in use.
 
-_match_test _comp_parts || return
+_match_test _comp_parts || return 1
 
 # Get the options.
 
@@ -42,6 +42,7 @@ shift OPTIND-1
 # Get the string from the line.
 
 str="$PREFIX$SUFFIX"
+[[ -o globcomplete ]] && str="$str:q"
 prefix=""
 
 # Walk through the arguments to find the longest unambiguous prefix.
@@ -63,12 +64,14 @@ while [[ $# -gt 1 ]]; do
   test="${str%%${sep}*}"
   matchflags=""
   _match_pattern _comp_parts test matchflags
+  [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
   test="${matchflags}${test}"
   testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+  testarr=( "${(@)testarr:#}" )
 
   # If there are no matches we give up. If there is more than one
   # match, this is the part we will complete.
-  (( $#testarr )) || return
+  (( $#testarr )) || return 1
   [[ $#testarr -gt 1 ]] && break
 
   # Only one match, add it to the prefix and skip over it in `str',
@@ -89,11 +92,13 @@ if [[ $# -le 1 || "$str" != *${2}* ]]; then
   matchflags=""
   test="$str"
   _match_pattern _comp_parts test matchflags
+  [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
   test="${matchflags}${test}"
   testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+  testarr=( "${(@)testarr:#}" )
 fi
 
-[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return
+[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1
 
 # Now we build the suffixes to give to the completion code.
 shift
@@ -114,6 +119,7 @@ while [[ $# -gt 0 && "$str" == *${1}* ]]; do
   fi
   matchflags=""
   _match_pattern _comp_parts test matchflags
+  [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
   test="${matchflags}${test}"
 
   # We incrementally add suffixes by appending to them the seperators
@@ -124,7 +130,9 @@ while [[ $# -gt 0 && "$str" == *${1}* ]]; do
     tmparr=( ${=arr[2,-2]} )
     arr=tmparr
   fi
-  suffixes=("${^suffixes[@]}${1}${(@M)^${(@P)arr}:#${~test}*}")
+  tmparr=( "${(@M)${(@P)arr}:#${~test}*}" )
+  tmparr=( "${(@)testarr:#}" )
+  suffixes=("${^suffixes[@]}${1}$^tmparr")
 
   # We want the completion code to generate the most specific suffix
   # for us, so we collect matching specifications that allow partial
@@ -143,5 +151,10 @@ done
 
 # Add the matches for each of the suffixes.
 for i in "$suffixes[@]"; do
-  compadd "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" -p "$prefix" -s "$i" - "$testarr[@]"
+  compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \
+          -i "$IPREFIX" -p "$prefix" -s "$i" - "$testarr[@]"
 done
+
+# This sets the return value to indicate that we added matches (or not).
+
+[[ nm -ne compstate[nmatches] ]]
diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso
index 23a40e2d0..6ff6cf0bf 100644
--- a/Completion/Core/_compalso
+++ b/Completion/Core/_compalso
@@ -4,10 +4,10 @@
 # It is used to include completions for another command or special context
 # into the list generated by the calling function.
 # For example the function for `-subscript-' could call this as in
-# `_compalso -math- "$@"' to get the completions that would be generated
-# for a mathematical context.
+# `_compalso -math-' to get the completions that would be generated for a
+# mathematical context.
 
 local tmp
 
 tmp="$_comps[$1]"
-[[ -z "$tmp" ]] || "$tmp" "$@"
+[[ -z "$tmp" ]] || "$tmp"
diff --git a/Completion/Core/_files b/Completion/Core/_files
index d2cce35e7..471824bfe 100644
--- a/Completion/Core/_files
+++ b/Completion/Core/_files
@@ -3,11 +3,12 @@
 # Utility function for completing files of a given type or any file.
 # In many cases you will want to call this one instead of _path_files().
 
-local nm=$NMATCHES
+local nm=$compstate[nmatches] ret
 
 _path_files "$@"
+ret=$?
 
-if [[ $# -ne 0 && -nmatches nm ]]; then
+if [[ $# -ne 0 && compstate[nmatches] -eq nm ]]; then
   local opt opts
 
   # We didn't get any matches for those types of files described by
@@ -23,4 +24,6 @@ if [[ $# -ne 0 && -nmatches nm ]]; then
   done
 
   _path_files "$opts[@]"
+else
+  return $ret
 fi
diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete
index c7f5a5a96..34c5a3d3c 100644
--- a/Completion/Core/_main_complete
+++ b/Completion/Core/_main_complete
@@ -2,47 +2,198 @@
 
 # The main loop of the completion code. This is what is called when 
 # completion is attempted from the command line.
-# The completion code gives us the special variables and the arguments
-# from the command line are given as positional parameters.
+#
+# This code will automatically try to correct the string on the
+# line based on the strings generated for the context if the
+# parameter `COMPCORRECT' is set and normal completion didn't yield
+# any matches. These corrected strings will be shown in a list and
+# one can cycle through them as in a menucompletion. To use this 
+# feature, `COMPCORRECT' should be set to a number, specifying the
+# maximum number of errors that should be accepted. If the string also
+# contains a `n' or `N', the code will use the numeric argument as the
+# maximum number of errors if a numeric argument was given. If no
+# numeric argument was given, the number from the value of
+# `COMPCORRECT' will be used. E.g. with `COMPCORRECT=2n' two errors
+# will be accepted, but if the user gives another number with the
+# numeric argument, this will be prefered. Also, with `COMPCORRECT=0n',
+# normally no automatic correction will be tried, but if a numeric
+# argument is given, automatic correction will be used. Once the
+# number of errors to accept is determined, the code will repeatedly
+# try to generate matches by allowing one error, two errors, and so
+# on.
+# If the parameter `CCORIG' is set (independent of the value), the
+# line will first be left unchanged and consecutive TABs cycle through 
+# the list.
+# When using automatic correction, one can also set the parameter
+# `CCPROMPT' to a string that will be shown when multiple
+# correction results are displayed and the code starts cycling
+# through them (this string is used with the `-X' option and thus may
+# contain the control sequences `%n', `%B',...).
 
-local comp name
+local comp name _comp_correct comax
 
 setopt localoptions nullglob rcexpandparam globdots
-unsetopt markdirs globsubst shwordsplit nounset
-
-# An entry for `-first-' is the replacement for `compctl -T'
-# Completion functions may set `_compskip' to any value to make the 
-# main loops stop calling other completion functions.
-
-comp="$_comps[-first-]"
-if [[ ! -z "$comp" ]]; then
-  "$comp" "$@"
-  if (( $+_compskip )); then
-    unset _compskip
-    return
-  fi
+unsetopt markdirs globsubst shwordsplit nounset ksharrays
+
+# Special completion contexts after `~' and `='.
+
+if [[ -iprefix '=' ]]; then
+  compstate[context]=equal
+elif [[ "$PREFIX$SUFFIX" != */* && -iprefix '~' ]]; then
+  compstate[context]=tilde
 fi
 
-# For arguments we use the `_normal function.
+# This is not an endless loop.
 
-if [[ $CONTEXT == argument || $CONTEXT == command ]]; then
-  _normal "$@"
-else
-  # Let's see if we have a special completion definition for the other
-  # possible contexts.
+while true; do
 
-  comp=''
+  # An entry for `-first-' is the replacement for `compctl -T'
+  # Completion functions may set `_compskip' to any value to make the 
+  # main loops stop calling other completion functions.
 
-  case $CONTEXT in
-  redirect)  comp="$_comps[-redirect-]";;
-  math)      comp="$_comps[-math-]";;
-  subscript) comp="$_comps[-subscript-]";;
-  value)     comp="$_comps[-value-]";;
-  condition) comp="$_comps[-condition-]";;
-  esac
+  comp="$_comps[-first-]"
+  if [[ ! -z "$comp" ]]; then
+    "$comp"
+    if (( $+_compskip )); then
+      unset _compskip
+      return
+    fi
+  fi
 
-  # If not, we use default completion, if any.
+  # For arguments and command names we use the `_normal' function.
 
-  [[ -z "$comp" ]] && comp="$_comps[-default-]"
-  [[ -z "$comp" ]] || "$comp" "$@"
-fi
+  if [[ "$compstate[context]" = command ]]; then
+    _normal
+  else
+    # Let's see if we have a special completion definition for the other
+    # possible contexts.
+
+    comp=''
+
+    case $compstate[context] in
+    equal)           comp="$_comps[-equal-]";;
+    tilde)           comp="$_comps[-tilde-]";;
+    redirect)        comp="$_comps[-redirect-]";;
+    math)            comp="$_comps[-math-]";;
+    subscript)       comp="$_comps[-subscript-]";;
+    value)           comp="$_comps[-value-]";;
+    array_value)     comp="$_comps[-array-value-]";;
+    condition)       comp="$_comps[-condition-]";;
+    parameter)       comp="$_comps[-parameter-]";;
+    brace_parameter) comp="$_comps[-brace-parameter-]";;
+    esac
+
+    # If not, we use default completion, if any.
+
+    [[ -z "$comp" ]] && comp="$_comps[-default-]"
+    [[ -z "$comp" ]] || "$comp"
+  fi
+
+  # Use automatic correction?
+
+  if (( $+COMPCORRECT )); then
+
+    # Do we have matches?
+    if (( compstate[nmatches] )); then
+
+      # Yes, were they added using correction? (More than one match?)
+
+      if [[ -n "$_comp_correct" && compstate[nmatches] -gt 1 ]]; then
+
+        # If we got more than one string from correction, we add the 
+	# original string as a possible match, let it not be shown in
+	# the list, and probably display the `CCPROMPT'.
+
+        (( $+CCORIG )) && builtin compadd -nQ - "$PREFIX$SUFFIX"
+
+	# If you always want to see the list of possible corrections,
+	# set `compstate[list]=list' here.
+      fi
+      # Since we have matches, we don't want to try again.
+      break
+    fi
+
+    # No matches, so let's see if we already tried correction.
+
+    if [[ -n "$_comp_correct" ]]; then
+
+      # Yes, give up if we reached the maximum number of tries,
+      # otherwise increment our counter.
+
+      [[ _comp_correct -eq comax ]] && break
+      (( _comp_correct++ ))
+
+    elif [[ compstate[matcher] -eq compstate[total_matchers] ]]; then
+
+      # No matches and no correction tried yet, but we just tried the
+      # last global match specification, so let's see if we should use
+      # correction now. First, get the maximum number of errors.
+
+      if [[ "$COMPCORRECT" = *[nN]* && NUMERIC -ne 1 ]]; then
+        # Prefer the numeric argument if that has a sensible value.
+        comax="$NUMERIC"
+      else
+        comax="${COMPCORRECT//[^0-9]}"
+      fi
+      # If the number of errors to accept is to small, give up.
+
+      [[ "$comax" -lt 1 ]] && break
+
+      # Otherwise temporarily define functions to use instead of
+      # the builtins that add matches. This is used to be able
+      # to stick the `(#a...)' into the right place (after an
+      # ignored prefix).
+
+      compadd() {
+        if [[ "$PREFIX" = \~*/* ]]; then
+	  PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
+	else
+          PREFIX="(#a${_comp_correct})$PREFIX"
+	fi
+	if (( $+CCPROMPT )); then
+	  builtin compadd -X "$CCPROMPT" -J _correct "$@"
+	else
+	  builtin compadd -J _correct "$@"
+	fi
+      }
+      compgen() {
+        if [[ "$PREFIX" = \~*/* ]]; then
+	  PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
+	else
+          PREFIX="(#a${_comp_correct})$PREFIX"
+	fi
+	if (( $+CCPROMPT )); then
+	  builtin compgen "$@" -X "$CCPROMPT" -J _correct
+	else
+	  builtin compgen "$@" -J _correct
+	fi
+      }
+      # Now initialise our counter. We also set `compstate[matcher]'
+      # to `-1'. This allows completion functions to use the simple
+      # `[[ compstate[matcher] -gt 1 ]] && return' to avoid being
+      # called for multiple global match specs and still be called 
+      # again when correction is done. Also, this makes it easy to
+      # test if correction is attempted since `compstate[matcher]'
+      # will never be set to a negative value by the completion code.
+
+      _comp_correct=1
+      compstate[matcher]=-1
+
+      # We also need to set `extendedglob' and to make the completion
+      # code behave as if globcomplete were set.
+
+      setopt extendedglob
+      compstate[pattern_match]=yes
+    else
+      # We are still trying global match specifications...
+      break
+    fi
+  else
+    # No automatic correction to try, just give up.
+    break
+  fi
+done
+
+# If we added wrapper functions, remove them.
+
+[[ -n "$_comp_correct" ]] && unfunction compadd compgen
diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts
new file mode 100644
index 000000000..1f51d2f6d
--- /dev/null
+++ b/Completion/Core/_multi_parts
@@ -0,0 +1,201 @@
+#autoload
+
+# This gets two arguments, a separator (which should be only one
+# character) and an array. As usual, the array may be given by it's
+# name or literal as in `(foo bar baz)' (words separated by spaces in
+# parentheses).
+# The parts of words from the array that are separated by the
+# separator character are then completed independently.
+
+local sep matches patstr orig matchflags pref i tmp1 tmp2 nm
+local group expl
+
+_match_test _multi_parts || return 1
+
+# Save the current number of matches to be able to return if we added
+# matches or not.
+
+nm=$compstate[nmatches]
+
+# Get the options.
+
+group=()
+expl=()
+while getopts "J:V:X:" opt; do
+  case "$opt" in
+  [JV]) group=("-$opt" "$OPTARG");;
+  X)    expl=(-X "$OPTARG");;
+  esac
+done
+shift OPTIND-1
+
+# Get the arguments, first the separator, then the array. The array is 
+# stored in `matches'. Further on this array will always contain those 
+# words from the original array that still match everything we have
+# tried to match while we walk through the string from the line.
+
+sep="$1"
+if [[ "${2[1]}" = '(' ]]; then
+  matches=( ${2[2,-2]} )
+else
+  matches=( "${(@P)2}" )
+fi
+
+# Now build the pattern from what we have on the line. We also save
+# the original string in `orig'. The `eval' is used to replace our
+# separator character by `*<sep>'.
+
+if [[ -o globcomplete ]]; then
+  patstr="${PREFIX}*${SUFFIX}*"
+else
+  patstr="${PREFIX:q}*${SUFFIX:q}*"
+fi
+orig="${PREFIX}${SUFFIX}"
+
+matchflags=""
+_match_pattern _path_files patstr matchflags
+[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+patstr="${${patstr//$sep/*$sep}//\*##/*}"
+#eval patstr\="\$patstr:gs-${sep}-\*${sep}-:gs/\*\*/\*/"
+
+# First we will skip over those parts of the matches for which we have 
+# exact substrings on the line. In `pref' we will build the
+# unambiguous prefix string.
+
+pref=''
+while [[ "$orig" = *${sep}* ]] do
+
+  # First build the pattern to use, then collect all strings from
+  # `matches' that match the prefix we have and the exact substring in 
+  # the array `tmp1'.
+
+  pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}${patstr##*${sep}}"
+  tmp1=( "${(@M)matches:#${~matchflags}${orig%%${sep}*}${sep}${~pat}}" )
+
+  # If there are no words matching the exact substring, stop.
+
+  (( $#tmp1 )) || break
+
+  # Otherwise add the part to the prefix, remove it from the matches
+  # (which will also remove all words not matching the string at all), 
+  # and set `patstr' and `orig' to the next component.
+
+  pref="$pref${orig%%${sep}*}${sep}"
+  matches=( "${(@)${(@)matches#${orig%%${sep}*}${sep}}:#}" )
+  orig="${orig#*${sep}}"
+  patstr="${patstr#*${sep}}"
+done
+
+# Now we get all the words that still match in `tmp1'.
+
+if [[ "$patstr" = *${sep}* ]]; then
+  tmp1="${patstr%${sep}*}${sep}"
+  pat="${tmp1//\*/[^${sep}]#}${patstr##*${sep}}"
+else
+  pat="$patstr"
+fi
+tmp1=( "${(@M)matches:#${~matchflags}${~pat}}" )
+
+if (( $#tmp1 )); then
+
+  # There are words that are matched, put them int `matches' and then
+  # move all unambiguous components from the beginning into `pref'.
+
+  matches=( "$tmp1[@]" )
+  while [[ "$matches[1]" = *${sep}* ]]; do
+
+    # We just take the first component of the first match and see if
+    # there are other matches with a different prefix (these are
+    # collected in `tmp2'). If there are any, we give up.
+
+    tmp1="${matches[1]%%${sep}*}${sep}"
+    tmp2=( "${(@)matches:#${tmp1}*}" )
+    (( $#tmp2 )) && break
+
+    # All matches have the same prefix, but it into `pref' and remove
+    # it from the matches.
+
+    pref="$pref$tmp1"
+    matches=( "${(@)${(@)matches#$tmp1}:#}" )
+
+    if [[ "$orig" = *${sep}* ]]; then
+      orig="${orig#*${sep}}"
+    else
+      orig=''
+    fi
+  done
+
+  # Now we can tell the completion code about the things we
+  # found. Strings that have a separator will be added with a suffix.
+
+  if [[ -z "$orig" && "$PREFIX$SUFFIX" != "$pref$orig" ]]; then
+    compadd -QU  "$group[@]" "$expl[@]" -i "$IPREFIX" -S '' - "${pref}${orig}"
+  elif [[ $compstate[insert] = *menu ]]; then
+    for i in "$matches[@]" ; do
+      if [[ "$i" = *${sep}* ]]; then
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
+	        -p "$pref" -qS "$sep" - "${i%%${sep}*}"
+      else
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
+	        -p "$pref" - "${i%%${sep}*}"
+      fi
+    done
+  else
+    for i in "$matches[@]" ; do
+      if [[ "$i" = *${sep}* ]]; then
+        compadd -U -i "$IPREFIX" -p "$pref" -s "${sep}${i#*${sep}}" \
+	        "$group[@]" "$expl[@]" -M "r:|${sep}=*" - "${i%%${sep}*}"
+      else
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -p "$pref" - "$i"
+      fi
+    done
+  fi
+elif [[ "$patstr" = *${sep}* ]]; then
+
+  # We had no words matching the string from the line. But we want to
+  # be friendly and at least expand the prefix as far as we can. So we 
+  # will loop through the rest of the string from the line and test
+  # the components one by one.
+
+  while [[ "$patstr" = *${sep}* ]]; do
+
+    # First we get all words matching at least this component in
+    # `tmp1'. If there are none, we give up.
+
+    tmp1=( "${(@M)matches:#${~matchflags}${~patstr%%${sep}*}${sep}*}" )
+    (( $#tmp1 )) || break
+
+    # Then we check if there are words that have a different prefix.
+
+    tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" )
+    if (( $#tmp2 )); then
+
+      # There are words with another prefix, so we have found an
+      # ambiguous component. So we just give all possible prefixes to
+      # the completion code together with our prefix and the rest of
+      # the string from the line as the suffix.
+
+      compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" \
+              -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}"
+      return 0
+    fi
+
+    # All words have the same prefix, so add it to `pref' again and
+    # try the next component.
+
+    pref="$pref${tmp1[1]%%${sep}*}${sep}"
+    matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" )
+    orig="${orig#*${sep}}"
+    patstr="${patstr#*${sep}}"
+  done
+
+  # Finally, add the unambiguous prefix and the rest of the string
+  # from the line.
+
+  compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" - "$orig"
+fi
+
+# This sets the return value to indicate that we added matches (or not).
+
+[[ nm -ne compstate[nmatches] ]]
diff --git a/Completion/Core/_normal b/Completion/Core/_normal
index 19da6d79b..f56849194 100644
--- a/Completion/Core/_normal
+++ b/Completion/Core/_normal
@@ -1,24 +1,25 @@
 #autoload
 
-local comp cmd1 cmd2 pat val name
+local comp command cmd1 cmd2 pat val name i ret
 
 # Completing in command position? If not we set up `cmd1' and `cmd2' as
 # two strings we have search in the completion definition arrays (e.g.
 # a path and the last path name component).
 
-if [[ $CONTEXT == command ]]; then
+command="$words[1]"
+if [[ CURRENT -eq 1 ]]; then
   comp="$_comps[-command-]"
-  [[ -z "$comp" ]] || "$comp" "$@"
+  [[ -z "$comp" ]] || "$comp"
   return
-elif [[ "$COMMAND[1]" == '=' ]]; then
-  eval cmd1\=$COMMAND
-  cmd2="$COMMAND[2,-1]"
-elif [[ "$COMMAND" == */* ]]; then
-  cmd1="$COMMAND"
-  cmd2="${COMMAND:t}"
+elif [[ "$command[1]" == '=' ]]; then
+  eval cmd1\=$command
+  cmd2="$command[2,-1]"
+elif [[ "$command" == */* ]]; then
+  cmd1="$command"
+  cmd2="${command:t}"
 else
-  cmd1="$COMMAND"
-  eval cmd2=$(whence -p $COMMAND)
+  cmd1="$command"
+  eval cmd2=$(whence -p $command)
 fi
 
 # See if there are any matching pattern completions.
@@ -27,10 +28,11 @@ for i in "$_patcomps[@]"; do
   pat="${i% *}"
   val="${i#* }"
   if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then
-    "$val" "$@"
+    "$val"
+    ret=$?
     if (( $+_compskip )); then
       unset _compskip
-      return
+      return $ret
     fi
   fi
 done
@@ -51,4 +53,4 @@ if [[ -z "$comp" ]]; then
   name=-default-
   comp="$_comps[-default-]"
 fi
-[[ -z "$comp" ]] || "$comp" "$@"
+[[ -z "$comp" ]] || "$comp"
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 83b6e8a09..3c03c0c61 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -3,7 +3,7 @@
 # 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
+# the last have the same syntax and meaning as for `compgen'. 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
@@ -14,13 +14,13 @@
 
 # First see if we should generate matches for the global matcher in use.
 
-_match_test _path_files || return
+_match_test _path_files || return 1
 
 # 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
+local addpfx addsfx expl orig ostr nm=$compstate[nmatches]
 
 setopt localoptions nullglob rcexpandparam globdots extendedglob
 unsetopt markdirs globsubst shwordsplit nounset
@@ -91,14 +91,14 @@ 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]"
+if [[ -o globcomplete ]]; then
+  str="${PREFIX}*${SUFFIX}"
+else
+  str="${PREFIX:q}*${SUFFIX:q}"
+fi
+orig="${PREFIX}${SUFFIX}"
 
-# We will first try normal completion called with `complist', but only if we
+# We will first try normal completion called with `compgen', but only if we
 # weren't given a `-F' option.
 
 if (( ! $#ignore )); then
@@ -112,18 +112,18 @@ if (( ! $#ignore )); then
     tmp1=(-W "( $prepaths )")
   fi
 
-  # Now call complist.
+  # Now call compgen.
 
-  nm=$NMATCHES
+  nm=$compstate[nmatches]
   if [[ -z "$gopt" ]]; then
-    complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt
+    compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt
   else
-    complist "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt -g "$pats"
+    compgen "$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
+  [[ compstate[nmatches] -eq nm ]] || return 0
 
   # No `-F' option, so we want to use `fignore'.
 
@@ -142,14 +142,16 @@ if [[ "$str[1]" = \~ ]]; then
   
   linepath="${str%%/*}/"
   eval realpath\=$linepath
+  [[ "$realpath" = "$linepath" ]] && return 1
   str="${str#*/}"
+  orig="${orig#*/}"
   donepath=''
   prepaths=( '' )
 else
   # If the string does not start with a `~' we don't remove a prefix from the
   # string.
 
-  liniepath=''
+  linepath=''
   realpath=''
 
   if [[ "$str[1]" = / ]]; then
@@ -158,6 +160,7 @@ else
     # Also, we don't use the paths from `-W'.
 
     str="$str[2,-1]"
+    orig="$orig[2,-1]"
     donepath='/'
     prepaths=( '' )
   else
@@ -169,28 +172,31 @@ else
   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
+[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
 
 # 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/**/*/"
+patstr="$patstr:gs-/-*/-:gs/*.*./../:gs-/*.-/.-:gs/**/*/:gs-.*/-./-"
+
+# 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 `orig' and added
+# to `donepath'.
+
+while [[ "$orig" = */* ]] do
+  tmp1=( ${~matchflags}$realpath$donepath${orig%%/*}/${~patstr#*/}$^pats )
+  [[ $#tmp1 -gt 0 && -e "$realpath$donepath${orig%%/*}" ]] || break
+  donepath="$donepath${orig%%/*}/"
+  orig="${orig#*/}"
+  patstr="${patstr#*/}"
+done
 
 # 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'
@@ -199,6 +205,9 @@ patstr="$patstr:gs-/-*/-:gs/*.*.//:gs-/*.-/.-:gs/**/*/"
 for prepath in "$prepaths[@]"; do
   str="$patstr"
   testpath="$donepath"
+  ostr="$orig"
+
+  [[ -z "$prepath" || "$prepath[-1]" = / ]] || prepath="${prepath}/"
 
   # The second loop tests the components of the path in `str' to get the
   # possible matches.
@@ -235,16 +244,20 @@ for prepath in "$prepaths[@]"; do
       # the suffixes we just built are used to produce possible matches
       # via globbing.
 
-      for i in $tmp1; do
+      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.
+      # has a possible sub-path matching what's on the line, we add the
+      # matches found in `tmp1' and otherwise give up and continue with the
+      # next `-W' path.
 
       if [[ $#collect -eq 0 ]]; then
+        compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
+                -i "$IPREFIX" -p "${linepath:q}${testpath:q}" -S "/${ostr#*/}" \
+		-W "$tmp1" -f "$ignore[@]" - "${(@)tmp1:q}"
         continue 2
       elif [[ $#collect -ne 1 ]]; then
         # If we have more than one possible match, this means that the
@@ -269,9 +282,17 @@ for prepath in "$prepaths[@]"; do
 	# 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
+	if [[ $compstate[insert] = *menu ]]; then
+          compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
+                  -i "$IPREFIX" -p "${linepath:q}${testpath:q}" -S "/${ostr#*/}" \
+		  -W "$tmp1" -f "$ignore[@]" - "${(@)${(@)collect%%/*}:q}"
+	else
+          for i in $collect; do
+            compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
+	            -i "$IPREFIX" -p "$linepath$testpath" -s "/${i#*/}" \
+		    -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${i%%/*}"
+          done
+	fi
 
 	# We have just finished handling all the matches from above, so we
 	# can continue with the next `-W' path.
@@ -291,6 +312,7 @@ for prepath in "$prepaths[@]"; do
     tmp1="$tmp1[1]"
     testpath="$testpath${tmp1##*/}/"
     str="$rest"
+    ostr="${ostr#*/}"
   done
 
   # We are here if all pathname components except the last one (which is still
@@ -302,10 +324,24 @@ for prepath in "$prepaths[@]"; do
   suffixes=( $str$^pats )
   suffixes=( "${(@)suffixes:gs.**.*.}" )
   tmp2=( ${~tmp1}${~matchflags}${~suffixes} )
-  if [[ $#tmp2 -eq 0 && "$sopt" = */* ]]; then
+  if [[ $#tmp2 -eq 0 ]]; then
+    # No match, insert the expanded path and add the original tail.
+
     [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]"
-    compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -f - "$linepath$testpath"
+    [[ -n "$ostr" && -n "$linepath$testpath" ]] && ostr="/$ostr"
+
+    # But only if something changed.
+    [[ "$linepath$testpath$ostr" = "$PREFIX$SUFFIX" ]] && return 1
+
+    compadd -QU -S '' "$group[@]" "$expl[@]" \
+            -i "$IPREFIX" -f - "${linepath:q}${testpath:q}$ostr"
   else
-    compadd "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${(@)tmp2#$tmp1}
+    compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
+            -i "$IPREFIX" -p "$linepath$testpath" -f "$ignore[@]" \
+	    -W "$prepath$realpath$testpath" - "${(@)tmp2#$tmp1}"
   fi
 done
+
+# This sets the return value to indicate that we added matches (or not).
+
+[[ nm -ne compstate[nmatches] ]]