about summary refs log tree commit diff
path: root/Completion/Base
diff options
context:
space:
mode:
Diffstat (limited to 'Completion/Base')
-rw-r--r--Completion/Base/_arguments571
-rw-r--r--Completion/Base/_default30
-rw-r--r--Completion/Base/_describe164
-rw-r--r--Completion/Base/_jobs80
-rw-r--r--Completion/Base/_subscript58
-rw-r--r--Completion/Base/_tilde55
-rw-r--r--Completion/Base/_values383
7 files changed, 575 insertions, 766 deletions
diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments
index 5170acb84..bf263d6e9 100644
--- a/Completion/Base/_arguments
+++ b/Completion/Base/_arguments
@@ -3,386 +3,345 @@
 # Complete the arguments of the current command according to the
 # descriptions given as arguments to this function.
 
-local long args rest ws cur nth def nm expl descr action opt arg tmp
+local long cmd="$words[1]" descr mesg subopts opt usecc autod
+local oldcontext="$curcontext" hasopts
 
-# Associative arrays used to collect information about the options.
+long=$argv[(I)--]
+if (( long )); then
+  local name tmp tmpargv
 
-typeset -A opts mopts dopts dmopts odopts odmopts
-
-# See if we support long options, too.
+  if [[ long -eq 1 ]]; then
+    tmpargv=()
+  else
+    tmpargv=( "${(@)argv[1,long-1]}" )
+  fi
 
-nth=$argv[(I)--]
-if (( nth )); then
-  long=( "${(@)argv[nth+1,-1]}" )
-  argv=("${(@)argv[1,nth-1]}")
-else
-  long=()
-fi
+  name=${~words[1]}
+  [[ "$name" != /* ]] && tmp="$PWD/$name"
 
-# Now parse the arguments...
+  name="_args_cache_${name}"
+  name="${name//[^a-zA-Z0-9_]/_}"
 
-args=()
-nth=1
-while (( $# )); do
+  if (( ! ${(P)+name} )); then
+    local iopts sopts pattern tmpo cur cache
+    typeset -U lopts
 
-  # This describes a one-shot option.
+    cache=()
 
-  if [[ "$1" = [-+]* ]]; then
-    if [[ "$1" = *:* ]]; then
+    # We have to build a new long-option cache, get the `-i' and
+    # `-s' options.
 
-      # If the option name ends in a `-', the first argument comes
-      # directly after the option, if it ends in a `+', the first
-      # argument *may* come directly after the option, otherwise it
-      # is in the next word.
+    set -- "${(@)argv[long+1,-1]}"
 
-      if [[ "$1" = [^:]##-:* ]]; then
-        dopts[${${1%%:*}[1,-2]}]="${1#*:}"
-      elif [[ "$1" = [^:]##+:* ]]; then
-        odopts[${${1%%:*}[1,-2]}]="${1#*:}"
+    iopts=()
+    sopts=()
+    while [[ "$1" = -[is]* ]]; do
+      if [[ "$1" = -??* ]]; then
+        tmp="${1[3,-1]}"
+        cur=1
       else
-        opts[${1%%:*}]="${1#*:}"
+        tmp="$2"
+	cur=2
       fi
-    else
-      opts[$1]=''
-    fi
-  elif [[ "$1" = \*[-+]* ]]; then
-
-    # The same for options that may appear more than once.
-
-    if [[ "$1" = *:* ]]; then
-      if [[ "$1" = [^:]##-:* ]]; then
-        dmopts[${${1[2,-1]%%:*}[1,-2]}]="${1#*:}"
-      elif [[ "$1" = [^:]##+:* ]]; then
-        odmopts[${${1[2,-1]%%:*}[1,-2]}]="${1#*:}"
+      if [[ "$tmp[1]" = '(' ]]; then
+	tmp=( ${=tmp[2,-2]} )
       else
-        mopts[${1[2,-1]%%:*}]="${1#*:}"
+	tmp=( "${(@P)tmp}" )
       fi
-    else
-      mopts[${1[2,-1]}]=''
-    fi
-  elif [[ "$1" = \*:* ]]; then
+      if [[ "$1" = -i* ]]; then
+        iopts=( "$iopts[@]" "$tmp[@]" )
+      else
+        sopts=( "$sopts[@]" "$tmp[@]" )
+      fi
+      shift cur
+    done
 
-    # This is `*:...', describing `all other arguments'.
+    # Now get the long option names by calling the command with `--help'.
+    # The parameter expansion trickery first gets the lines as separate
+    # array elements. Then we select all lines whose first non-blank
+    # character is a hyphen. Since some commands document more than one
+    # option per line, separated by commas, we convert commas int
+    # newlines and then split the result again at newlines after joining 
+    # the old array elements with newlines between them. Then we select
+    # those elements that start with two hyphens, remove anything up to
+    # those hyphens and anything from the space or comma after the
+    # option up to the end. 
 
-    rest="${1[3,-1]}"
-  elif [[ "$1" = :* ]]; then
+    lopts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$(_call options ${~words[1]} --help 2>&1)//\[--/
+--}:#[ 	]#-*}//,/
+}}:#[ 	]#--*}#*--}%%[], ]*}:#}")
+    lopts=( "${(@)lopts:#--}" )
 
-    # This is `:...', describing `the next argument'.
+    # Now remove all ignored options ...
 
-    args[nth++]="${1#*:}"
-  else
+    while (( $#iopts )); do
+      lopts=( ${lopts:#$~iopts[1]} )
+      shift iopts
+    done
 
-    # And this is `n:...', describing the `n'th argument.
+    # ... and add "same" options
 
-    args[${1%%:*}]="${1#*:}"
-    nth=$(( ${1%%:*} + 1 ))
-  fi
-  shift
-done
+    while (( $#sopts )); do
+      lopts=( $lopts ${lopts/$sopts[1]/$sopts[2]} )
+      shift 2 sopts
+    done
 
-if [[ $#long -ne 0 && "$PREFIX" = --* ]]; then
+    # Then we walk through the descriptions plus a few builtin ones.
 
-   # If the current words starts with `--' and we should use long
-   # options, just call...
+    set -- "$@" '*=FILE*:file:_files' \
+           '*=(DIR|PATH)*:directory:_files -/' '*: :'
 
-  _long_options "$long[@]"
-else
+    while (( $# )); do
 
-  # Otherwise parse the command line...
+      # First, we get the pattern and the action to use and take them
+      # from the positional parameters.
 
-  ws=( "${(@)words[2,-1]}" )
-  cur=$(( CURRENT-2 ))
-  nth=1
+      pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}"
+      descr="${1#${pattern}}"
+      shift
 
-  # ...until the current word is reached.
+      # We get all options matching the pattern and take them from the
+      # list we have built. If no option matches the pattern, we
+      # continue with the next.
 
-  while [[ cur -gt 0 ]]; do
+      tmp=("${(@M)lopts:##$~pattern}")
+      lopts=("${(@)lopts:##$~pattern}")
 
-    # `def' holds the description for the option we are currently after.
-    # Check if the next argument for the option is optional.
+      (( $#tmp )) || continue
 
-    if [[ "$def" = :* ]]; then
-      opt=yes
-    else
       opt=''
-    fi
-    arg=''
-
-    # Remove one description/action pair from `def' if that isn't empty.
 
-    if [[ -n "$def" ]]; then
-      if [[ "$def" = ?*:*:* ]]; then
-        def="${def#?*:*:}"
-      else
-        def=''
-      fi
-    else
+      # If there are option strings with a `[=', we take these get an
+      # optional argument.
 
-      # If it is empty, and the word starts with `--' and we should
-      # complete long options, just ignore this word, otherwise make sure
-      # we test for options below and handle normal arguments.
+      tmpo=("${(@M)tmp:#*\[\=*}")
+      if (( $#tmpo )); then
+        tmp=("${(@)tmp:#*\[\=*}")
+        tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
 
-      if [[ $#long -eq 0 || "$ws[1]" != --* ]]; then
-        opt=yes
-	arg=yes
-      else
-        def=''
+        if [[ "$descr" = ::* ]]; then
+	  cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
+        else
+	  cache=( "$cache[@]" "${(@)^tmpo}=:${descr}" )
+        fi
       fi
-    fi
-
-    if [[ -n "$opt" ]]; then
-
-      # `opt' was set above if we have to test if the word is an option.
-      # We first test for the simple options -- those without arguments or
-      # those whose arguments have to be given as separate words.
-
-      if (( $+opts[$ws[1]] )); then
-
-        # Options that may only be given once are removed from the
-        # associative array so that we are not offered them again.
 
-        def="$opts[$ws[1]]"
-        unset "opts[$ws[1]]"
-      elif (( $+mopts[$ws[1]] )); then
-        def="$mopts[$ws[1]]"
-      else
+      # Descriptions with `=': mandatory argument.
 
-        # If the word is none of the simple options, test for those
-        # whose first argument has to or may come directly after the
-        # option. This is done in four loops looking very much alike.
+      tmpo=("${(@M)tmp:#*\=*}")
+      if (( $#tmpo )); then
+        tmp=("${(@)tmp:#*\=*}")
+        tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
 
-        if (( $#dopts )); then
+	cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
+      fi
 
-	  # First we get the option names.
+      # Everything else is just added as a option without arguments.
 
-	  tmp=( "${(@k)dopts}" )
+      if (( $#tmp )); then
+        tmp=("${(@)tmp//[^a-zA-Z0-9-]}")
+	cache=( "$cache[@]" "$tmp[@]" )
+      fi
+    done
+    eval "${name}=( \"\${(@)cache:# #}\" )"
+  fi
+  set -- "$tmpargv[@]" "${(@P)name}"
+fi
 
-	  # Then we loop over them and see if the current word begins
-	  # with one of the option names.
+subopts=()
+while [[ "$1" = -(O*|C) ]]; do
+  case "$1" in
+  -C) usecc=yes; shift ;;
+  -O) subopts=( "${(@P)2}" ); shift 2 ;;
+  *)  subopts=( "${(@P)1[3,-1]}" ); shift ;;
+  esac
+done
 
-	  while (( $#tmp )); do
-	    [[ "$ws[1]" = ${tmp[1]}* ]] && break
-	    shift 1 tmp
-	  done
+zstyle -s ":completion:${curcontext}:options" auto-description autod
 
-	  if (( $#tmp )); then
+if (( $# )) && comparguments -i "$autod" "$@"; then
+  local nm="$compstate[nmatches]" action noargs aret expl local
+  local next direct odirect equal single match matched ws tmp1 tmp2 tmp3
+  local opts subc prefix suffix
+  local origpre="$PREFIX" origipre="$IPREFIX"
 
-	    # It does. So use the description for it, but only from
-	    # the second argument on, because we are searching the
-	    # description for the next command line argument.
+  if comparguments -D descr action; then
+    comparguments -C subc
+    curcontext="${oldcontext%:*}:$subc"
 
-	    opt=''
-	    def="$dopts[$tmp[1]]"
-	    unset "dopts[$tmp[1]]"
-	    if [[ "$def" = ?*:*:* ]]; then
-              def="${def#?*:*:}"
-            else
-              def=''
-	    fi
-          fi
-        fi
-	if [[ -n "$opt" && $#dmopts -ne 0 ]]; then
-	  tmp=( "${(@k)dmopts}" )
-	  while (( $#tmp )); do
-	    [[ "$ws[1]" = ${tmp[1]}* ]] && break
-	    shift 1 tmp
-	  done
-
-	  if (( $#tmp )); then
-	    opt=''
-	    def="$dmopts[$tmp[1]]"
-	    if [[ "$def" = ?*:*:* ]]; then
-              def="${def#?*:*:}"
-            else
-              def=''
-            fi
-          fi
-	fi
-        if [[ -n "$opt" && $#odopts -ne 0 ]]; then
-	  tmp=( "${(@k)odopts}" )
-	  while (( $#tmp )); do
-	    [[ "$ws[1]" = ${tmp[1]}* ]] && break
-	    shift 1 tmp
-	  done
-
-	  if (( $#tmp )); then
-	    opt=''
-	    def="$odopts[$tmp[1]]"
-	    unset "odopts[$tmp[1]]"
-
-	    # For options whose first argument *may* come after the
-	    # option, we skip over the first description only if there
-	    # is something after the option name on the line.
-
-	    if [[ "$ws[1]" != "$tmp[1]" ]]; then
-	      if [[ "$def" = ?*:*:* ]]; then
-                def="${def#?*:*:}"
-              else
-                def=''
-              fi
-	    fi
-          fi
-        fi
-	if [[ -n "$opt" && $#odmopts -ne 0 ]]; then
-	  tmp=( "${(@k)odmopts}" )
-	  while (( $#tmp )); do
-	    [[ "$ws[1]" = ${tmp[1]}* ]] && break
-	    shift 1 tmp
-	  done
-
-	  if (( $#tmp )); then
-	    opt=''
-	    def="$odmopts[$tmp[1]]"
-	    if [[ "$ws[1]" != "$tmp[1]" ]]; then
-	      if [[ "$def" = ?*:*:* ]]; then
-                def="${def#?*:*:}"
-              else
-                def=''
-              fi
-            fi
-          fi
-	fi
-
-	# If we didn't find a matching option description and we were
-	# told to use normal argument descriptions, just increase
-	# our counter `nth'.
-
-        if [[ -n "$opt" && -n "$arg" ]]; then
-          def=''
-	  (( nth++ ))
-        fi
-      fi
+    if comparguments -O next direct odirect equal; then
+      opts=yes
+      _tags arguments options
+    else
+      _tags arguments
+    fi
+  else
+    if comparguments -a; then
+      noargs='no more arguments'
+    else
+      noargs='no arguments'
     fi
+    comparguments -O next direct odirect equal || return 1
 
-    shift 1 ws
-    (( cur-- ))
-  done
+    opts=yes
+    _tags options
+  fi
 
-  # Now generate the matches.
+  while true; do
+    while _tags; do
+      if [[ -n "$matched" ]] || _requested arguments; then
+        _description arguments expl "$descr"
+
+        if [[ "$action" = -\>* ]]; then
+          comparguments -W line opt_args
+          state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
+	  if [[ -n "$usecc" ]]; then
+	    curcontext="${oldcontext%:*}:$subc"
+	  else
+	    context="$subc"
+	  fi
+          compstate[restore]=''
+          aret=yes
+        else
+          if [[ -z "$local" ]]; then
+            local line
+            typeset -A opt_args
+            local=yes
+          fi
 
-  nm="$compstate[nmatches]"
+          comparguments -W line opt_args
 
-  if [[ -z "$def" || "$def" = :* ]]; then
+          if [[ "$action" = \ # ]]; then
 
-    # We either don't have a description for an argument of an option
-    # or we have a description for a optional argument.
+            # An empty action means that we should just display a message.
 
-    if [[ -z "$def" ]]; then
+            [[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
+            mesg="$descr"
 
-      # If we have none at all, use the one for this argument position.
+          elif [[ "$action" = \(\(*\)\) ]]; then
 
-      def="$args[nth]"
-      [[ -z "$def" ]] && def="$rest"
-    fi
+            # ((...)) contains literal strings with descriptions.
 
-    # In any case, we have to complete option names here, but we may
-    # be in a string that starts with an option names and continues with
-    # the first argument, test that (again, four loops).
+            eval ws\=\( "${action[3,-3]}" \)
 
-    opt=yes
-    if (( $#dopts )); then
+            _describe "$descr" ws -M "$match" "$subopts[@]"
 
-      # Get the option names.
+          elif [[ "$action" = \(*\) ]]; then
 
-      tmp=( "${(@k)dopts}" )
-      while (( $#tmp )); do
-        if compset -P "$tmp[1]"; then
+            # Anything inside `(...)' is added directly.
 
-	  # The current string starts with the option name, so ignore
-	  # that and complete the rest of the string.
+            _all_labels arguments expl "$descr" \
+                compadd "$subopts[@]" - ${=action[2,-2]}
+          elif [[ "$action" = \{*\} ]]; then
 
-	  def="$dopts[$tmp[1]]"
-	  opt=''
-	  break
-        fi
-	shift 1 tmp
-      done
-    fi
-    if [[ -n "$opt" && $#dmopts -ne 0 ]]; then
-      tmp=( "${(@k)dmopts}" )
-      while (( $#tmp )); do
-        if compset -P "$tmp[1]"; then
-	  def="$dmopts[$tmp[1]]"
-	  opt=''
-	  break
-        fi
-	shift 1 tmp
-      done
-    fi
-    if [[ -n "$opt" && $#odopts -ne 0 ]]; then
-      tmp=( "${(@k)odopts}" )
-      while (( $#tmp )); do
-        if compset -P "$tmp[1]"; then
-	  def="$odopts[$tmp[1]]"
-	  opt=''
-	  break
-        fi
-	shift 1 tmp
-      done
-    fi
-    if [[ -n "$opt" && $#odmopts -ne 0 ]]; then
-      tmp=( "${(@k)odmopts}" )
-      while (( $#tmp )); do
-        if compset -P "$tmp[1]"; then
-	  def="$odmopts[$tmp[1]]"
-	  opt=''
-	  break
-        fi
-	shift 1 tmp
-      done
-    fi
-    if [[ -n "$opt" ]]; then
-
-      # We aren't in an argument directly after a option name, so
-      # all option names are possible matches.
-
-      _description expl option
-      compadd "$expl[@]" - "${(@k)opts}" "${(@k)mopts}" \
-                           "${(@k)dopts}" "${(@k)dmopts}" \
-			   "${(@k)odopts}" "${(@k)odmopts}"
-    fi
-  fi
+            # A string in braces is evaluated.
 
-  # Now add the matches from the description, if any.
+            while _next_label arguments expl "$descr"; do
+              eval "$action[2,-2]"
+            done
+          elif [[ "$action" = \ * ]]; then
 
-  if [[ -n "$def" ]]; then
+            # If the action starts with a space, we just call it.
 
-    # Ignore the leading colon describing optional arguments.
+	    eval "action=( $action )"
+            while _next_label arguments expl "$descr"; do
+              "$action[@]"
+            done
+          else
 
-    [[ "$def" = :* ]] && def="$def[2,-1]"
+            # Otherwise we call it with the description-arguments.
 
-    # Get the description and the action.
+            eval "action=( $action )"
+            _all_labels arguments expl "$descr" \
+                "$action[1]" "$subopts[@]" "${(@)action[2,-1]}"
+          fi
+        fi
+      fi
 
-    descr="${def%%:*}"
-    action="${${def#*:}%%:*}"
+      if [[ -z "$matched$hasopts" ]] && _requested options &&
+          { ! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
+            [[ "$origpre" = [-+]* ||
+            ( -z "$aret$mesg" && nm -eq compstate[nmatches] ) ]] } ; then
+	local prevpre="$PREFIX" previpre="$IPREFIX"
+
+	hasopts=yes
+
+	PREFIX="$origpre"
+	IPREFIX="$origipre"
+
+        comparguments -M match
+	
+        if comparguments -s single; then
+
+          _description options expl option
+
+          if [[ "$single" = direct ]]; then
+	    compadd "$expl[@]" -QS '' - "${PREFIX}${SUFFIX}"
+          elif [[ "$single" = next ]]; then
+	    compadd "$expl[@]" -Q - "${PREFIX}${SUFFIX}"
+          elif [[ "$single" = equal ]]; then
+	    compadd "$expl[@]" -QqS= - "${PREFIX}${SUFFIX}"
+          else
+	    tmp1=( "$next[@]" "$direct[@]" "$odirect[@]" "$equal[@]" )
+	    tmp3=( "${(M@)tmp1:#[-+]?[^:]*}" )
+	    tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" )
+	    tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" )
+
+            _describe -o option \
+                      tmp1 tmp2 -Q -S '' -- \
+		      tmp3 -Q
+          fi
+          single=yes
+        else
+          next=( "$next[@]" "$odirect[@]" )
+          _describe -o option \
+            next -Q -M "$match" -- \
+            direct -QS '' -M "$match" -- \
+            equal -QqS= -M "$match"
+        fi
+	PREFIX="$prevpre"
+	IPREFIX="$previpre"
+      fi
+    done
+    if [[ -n "$opts" && -z "$aret$matched$mesg" &&
+          nm -eq compstate[nmatches] ]]; then
 
-    _description expl "$descr"
+      PREFIX="$origpre"
+      IPREFIX="$origipre"
 
-    if [[ -z "$action" ]]; then
+      prefix="${PREFIX#*\=}"
+      suffix="$SUFFIX"
+      PREFIX="${PREFIX%%\=*}"
+      SUFFIX=''
+      compadd -M "$match" -D equal - "${(@)equal%%:*}"
 
-      # An empty action means that we should just display a message.
-      _message "$descr"
-      return 1
-    elif [[ "$action[1]" = \( ]]; then
+      if [[ $#equal -eq 1 ]]; then
+        PREFIX="$prefix"
+	SUFFIX="$suffix"
+	IPREFIX="${IPREFIX}${equal[1]%%:*}="
+	matched=yes
 
-      # Anything inside `(...)' is added directly.
+	comparguments -L "${equal[1]%%:*}" descr action subc
+	curcontext="${oldcontext%:*}:$subc"
 
-      compadd "$expl[@]" - ${=action[2,-2]}
-    elif [[ "$action" = \ * ]]; then
+	_tags arguments
 
-      # If the action starts with a space, we just call it.
+	continue
+      fi
+    fi
+    break
+  done
 
-      $=action
-    else
+  [[ -z "$aret" || -z "$usecc" ]] && curcontext="$oldcontext"
 
-      # Otherwise we call it with the description-arguments built above.
+  [[ -n "$aret" ]] && return 300
 
-      action=( $=action )
-      "$action[1]" "$expl[@]" "${(@)action[2,-1]}"
-    fi
-  fi
+  [[ -n "$mesg" ]] && _message "$mesg"
+  [[ -n "$noargs" ]] && _message "$noargs"
 
   # Set the return value.
 
-  [[  nm -ne "$compstate[nmatches]" ]]
+  [[ nm -ne "$compstate[nmatches]" ]]
+else
+  return 1
 fi
diff --git a/Completion/Base/_default b/Completion/Base/_default
index 8bcf14f6a..fd5869e2e 100644
--- a/Completion/Base/_default
+++ b/Completion/Base/_default
@@ -1,13 +1,23 @@
-#defcomp -default-
+#compdef -default-
 
-# We first try the `compctl's. This is without first (-T) and default (-D)
-# completion. If you want them add `-T' and/or `-D' to this command.
-# If there is a `compctl' for the command we are working on, we return
-# immediatly. If you want to use new style completion anyway, remove the
-# `|| return'. Also, you may want to use new style completion if the 
-# `compctl' didn't produce any matches. In that case remove the `|| return'
-# and at the line `[[ -nmatches 0 ]] || return' after `compcall'.
+local ctl
 
-compcall || return
+if { zstyle -s ":completion:${curcontext}:" use-compctl ctl ||
+     zmodload -e zsh/compctl } && [[ "$ctl" != (no|false|0|off) ]]; then
+  local opt
 
-_files
+  opt=()
+  [[ "$ctl" = *first* ]] && opt=(-T)
+  [[ "$ctl" = *default* ]] && opt=("$opt[@]" -D)
+  compcall "$opt[@]" || return 0
+fi
+
+_wanted files || return 1
+
+_files && return 0
+
+# magicequalsubst allows arguments like <any-old-stuff>=~/foo to do
+# file name expansion after the =.  In that case, it's natural to
+# allow completion to handle file names after any equals sign.
+
+[[ -o magicequalsubst ]] && compset -P 1 '*=' && _files
diff --git a/Completion/Base/_describe b/Completion/Base/_describe
index e01c77509..6e6f4f4a9 100644
--- a/Completion/Base/_describe
+++ b/Completion/Base/_describe
@@ -2,154 +2,52 @@
 
 # This can be used to add options or values with descriptions as matches.
 
-setopt localoptions extendedglob
+local _opt _expl _tmps _tmpd _tmpmd _tmpms _ret=1 _showd _nm _hide _args
+local _type=values _descr
 
-local gdescr isopt cmd opt nsets tmp descr match descrs matches adescr i
-local disps disp expl tmps tmpd tmpmd tmpms name ret=1 showd _nm
+# Get the option.
 
-cmd="$words[1]"
-
-# Get the options.
-
-while getopts 'oc:' opt; do
-  if [[ "$opt" = o ]]; then
-    isopt=yes
-  else
-    cmd="$OPTARG"
-  fi
-done
-shift OPTIND-1
+if [[ "$1" = -o ]]; then
+  _type=options
+  shift
+fi
 
 # Do the tests. `showd' is set if the descriptions should be shown.
 
-if [[ -n "$isopt" ]]; then
-
-  # We take the value to test the number of patches from a non-local
-  # parameter `nm' if that exists and contains only digits. It's a hack.
-
-  if [[ "$nm" = [0-9]## ]]; then
-    _nm="$nm"
-  else
-    _nm=0
-  fi
-  [[ -n "$compconfig[option_prefix]" &&
-     "$compconfig[option_prefix]" != *\!${cmd}* &&
-     "$PREFIX" != [-+]* &&
-     ( "$compconfig[option_prefix]" = *nodefault* ||
-       _nm -ne compstate[nmatches] ) ]] && return 1
+_wanted "$_type" || return 1
 
-  [[ -n "$compconfig[describe_options]" &&
-     "$compconfig[describe_options]" != *\!${cmd}* ]] && showd=yes
-else
-  [[ -n "$compconfig[describe_values]" &&
-     "$compconfig[describe_values]" != *\!${cmd}* ]] && showd=yes
-fi
+zstyle -T ":completion:${curcontext}:$_type" verbose && _showd=yes
 
-gdescr="$1"
+_descr="$1"
 shift
 
-# Now interpret the arguments.
+[[ "$_type" = options ]] &&
+    zstyle -t ":completion:${curcontext}:options" prefix-hidden && _hide=yes
 
-nsets=0
-adescr=()
-descrs=()
-matches=()
-while (( $# )); do
-  (( nsets++ ))
-  descr="$1"
-  [[ -n "$showd" ]] && adescr=( "$adescr[@]" "${(@PM)^descr:#*:?*},$nsets" )
-  if [[ "$2" = -* ]]; then
-    match=''
-    shift
-  else
-    match="$2"
-    shift 2
-  fi
-  tmp=$argv[(i)--]
-  if [[ tmp -eq 1 ]]; then
-    opt=()
-  else
-    opt=( "${(@)argv[1,tmp-1]}" )
-  fi
-  if [[ tmp -gt $# ]]; then
-    argv=()
+while _next_label "$_type" _expl "$_descr"; do
+
+  if [[ -n "$_showd" ]]; then
+    compdescribe -I ' -- ' "$@"
   else
-    shift tmp
+    compdescribe -i "$@"
   fi
 
-  # `descr' and `matches' collect the names of the arrays containing the
-  # possible matches with descriptions and the matches to add.
-  # The options to give to `compadd' are stored in local arrays.
+  while compdescribe -g _args _tmpd _tmpmd _tmps _tmpms; do
 
-  descrs[nsets]="$descr"
-  matches[nsets]="$match"
-  typeset -a _descr_opts_$nsets
-  eval "_descr_opts_${nsets}=( \"\$opt[@]\" )"
-done
-
-(( nsets )) || return 1
-
-# Build the display strings if needed.
-
-[[ -n "$showd" ]] && _display disps "$adescr[@]"
-_description expl "$gdescr"
+    # See if we should remove the option prefix characters.
 
-# Loop through the array/option sets we have.
-
-i=0
-while [[ ++i -le nsets ]]; do
-  name=_descr_opts_$i
-  [[ -n "$showd" ]] && disp=( "${(@)${(@M)disps:#*,${i}}%,*}" )
-  descr=( "${(@P)descrs[i]}" )
-
-  # We collect the strings to display in `tmpd' (one string per line)
-  # and `tmps' (in columns) and the matches to add in `tmpmd' and `tmpms'.
-
-  tmpd=()
-  tmps=()
-  tmpmd=()
-  tmpms=()
-  if [[ -n "$matches[i]" ]]; then
-    match=( "${(@P)matches[i]}" )
-    while (( $#match )); do
-      if [[ -n "$showd" && "$descr[1]" = *:?* ]]; then
-	tmpd=( "$tmpd[@]" "$disp[1]" )
-        tmpmd=( "$tmpmd[@]" "$match[1]" )
-        shift 1 disp
-      else
-        tmps=( "$tmps[@]" "${descr[1]%%:*}" )
-        tmpms=( "$tmpms[@]" "$match[1]" )
+    if [[ -n "$_hide" ]]; then
+      if [[ "$PREFIX" = --* ]]; then
+        _tmpd=( "${(@)_tmpd#--}" )
+        _tmps=( "${(@)_tmps#--}" )
+      elif [[ "$PREFIX" = [-+]* ]]; then
+        _tmpd=( "${(@)_tmpd#[-+]}" )
+        _tmps=( "${(@)_tmps#[-+]}" )
       fi
-      shift 1 match
-      shift 1 descr
-    done
-  else
-    while (( $#descr )); do
-      if [[ -n "$showd" && "$descr[1]" = *:?* ]]; then
-	tmpd=( "$tmpd[@]" "$disp[1]" )
-        tmpmd=( "$tmpmd[@]" "${descr[1]%%:*}" )
-        shift 1 disp
-      else
-        tmps=( "$tmps[@]" "${descr[1]%%:*}" )
-        tmpms=( "$tmpms[@]" "${descr[1]%%:*}" )
-      fi
-      shift 1 descr
-    done
-  fi
-
-  # See if we should remove the option prefix characters.
-
-  if [[ -n "$isopt" && "$compconfig[option_prefix]" = hide* ]]; then
-    if [[ "$PREFIX" = --* ]]; then
-      tmpd=( "${(@)tmpd#--}" )
-      tmps=( "${(@)tmps#--}" )
-    elif [[ "$PREFIX" = [-+]* ]]; then
-      tmpd=( "${(@)tmpd#[-+]}" )
-      tmps=( "${(@)tmps#[-+]}" )
     fi
-  fi
-  compadd "${(@P)name}" "$expl[@]" -ld tmpd - "$tmpmd[@]" && ret=0
-  compadd "${(@P)name}" "$expl[@]" -d tmps - "$tmpms[@]" && ret=0
-done
 
-return ret
+    compadd "$_args[@]" "$_expl[@]" -ld _tmpd - "$_tmpmd[@]" && _ret=0
+    compadd "$_args[@]" "$_expl[@]" -d _tmps  - "$_tmpms[@]" && _ret=0
+  done
+done
+return _ret
diff --git a/Completion/Base/_jobs b/Completion/Base/_jobs
index 869aeeb8a..45983ad16 100644
--- a/Completion/Base/_jobs
+++ b/Completion/Base/_jobs
@@ -1,27 +1,85 @@
 #autoload
 
-local expl disp jobs job jids
+local expl disp jobs job jids pfx='%' desc how expls
+
+_wanted jobs || return 1
+
+if [[ "$1" = -t ]]; then
+  zstyle -T ":completion:${curcontext}:jobs" prefix-needed &&
+      [[ "$PREFIX" != %* && compstate[nmatches] -ne 0 ]] && return 1
+  shift
+fi
+zstyle -t ":completion:${curcontext}:jobs" prefix-hidden && pfx=''
+zstyle -T ":completion:${curcontext}:jobs" verbose       && desc=yes
 
 if [[ "$1" = -r ]]; then
   jids=( "${(@k)jobstates[(R)running*]}" )
   shift
-  _description expl 'running job'
+  expls='running job'
 elif [[ "$1" = -s ]]; then
   jids=( "${(@k)jobstates[(R)running*]}" )
   shift
-  _description expl 'suspended job'
+  expls='suspended job'
 else
   [[ "$1" = - ]] && shift
   jids=( "${(@k)jobtexts}" )
-  _description expl job
+  expls=job
 fi
 
-disp=()
-jobs=()
-for job in "$jids[@]"; do
-  disp=( "$disp[@]" "${(l:3:: ::%:)job} -- ${jobtexts[$job]}" )
-  jobs=( "$jobs[@]" "$job" )
-done
+if [[ -n "$desc" ]]; then
+  disp=()
+  for job in "$jids[@]"; do
+    [[ -n "$desc" ]] &&
+        disp=( "$disp[@]" "${pfx}${(r:2:: :)job} -- ${(r:COLUMNS-8:: :)jobtexts[$job]}" )
+  done
+fi
+
+zstyle -s ":completion:${curcontext}:jobs" numbers how
+
+if [[ "$how" = (yes|true|on|1) ]]; then
+  jobs=( "$jids[@]" )
+else
+  local texts i text str tmp num max=0
 
-compadd "$@" "$expl[@]" -ld disp - "%$^jobs[@]"
+  # Find shortest unambiguous strings.
 
+  texts=( "$jobtexts[@]" )
+  jobs=()
+  for i in "$jids[@]"; do
+    text="$jobtexts[$i]"
+    str="${text%% *}"
+    if [[ "$text" = *\ * ]]; then
+      text="${text#* }"
+    else
+      text=""
+    fi
+    tmp=( "${(@M)texts:#${str}*}" )
+    num=1
+    while [[ -n "$text" && $#tmp -ge 2 ]]; do
+      str="${str} ${text%% *}"
+      if [[ "$text" = *\ * ]]; then
+        text="${text#* }"
+      else
+        text=""
+      fi
+      tmp=( "${(@M)texts:#${str}*}" )
+      (( num++ ))
+    done
+
+    [[ num -gt max ]] && max="$num"
+
+    jobs=( "$jobs[@]" "$str" )
+  done
+
+  if [[ "$how" = [0-9]## && max -gt how ]]; then
+    jobs=( "$jids[@]" )
+  else
+    [[ -z "$pfx" && -n "$desc" ]] && disp=( "${(@)disp#%}" )
+  fi
+fi
+
+if [[ -n "$desc" ]]; then
+  _all_labels jobs expl "$expls" compadd "$@" -ld disp - "%$^jobs[@]"
+else
+  _all_labels jobs expl "$expls" compadd "$@" - "%$^jobs[@]"
+fi
diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript
index 2b827a117..60d45370b 100644
--- a/Completion/Base/_subscript
+++ b/Completion/Base/_subscript
@@ -1,4 +1,56 @@
-#defcomp -subscript-
+#compdef -subscript-
 
-_compalso -math- "$@"
-[[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )"
+local expl
+
+if [[ "$PREFIX" = :* ]]; then
+  _wanted characters expl 'character class' \
+      compadd -p: -S ':]' alnum alpha blank cntrl digit graph \
+                          lower print punct space upper xdigit
+elif [[ ${(Pt)${compstate[parameter]}} = assoc* ]]; then
+  local suf
+
+  [[ "$RBUFFER" != \]* ]] && suf=']'
+
+  _wanted association-keys expl 'association key' \
+      compadd -S "$suf" - "${(@kP)${compstate[parameter]}}"
+elif [[ ${(Pt)${compstate[parameter]}} = array* ]]; then
+  local list i j ret=1 disp
+
+  _tags indexes parameters
+
+  while _tags; do
+    if _requested indexes; then
+      ind=( {1..${#${(P)${compstate[parameter]}}}} )
+      if zstyle -T ":completion:${curcontext}:indexes" verbose; then
+        list=()
+        for i in "$ind[@]"; do
+          if [[ "$i" = ${PREFIX}*${SUFFIX} ]]; then
+              list=( "$list[@]" 
+	             "${i}:$(print -D ${(P)${compstate[parameter]}[$i]})" )
+	  else
+	      list=( "$list[@]" '' )
+	  fi
+        done
+        zformat -a list ' -- ' "$list[@]"
+	disp=( -d list)
+      else
+        disp=()
+      fi
+
+      if [[ "$RBUFFER" = \]* ]]; then
+        _all_labels -V indexes expl 'array index' \
+            compadd -S '' "$disp[@]" - "$ind[@]" && ret=0
+      else
+        _all_labels -V indexes expl 'array index' \
+            compadd -S ']' "$disp[@]" - "$ind[@]" && ret=0
+      fi
+    fi
+    _requested parameters && _parameters && ret=0
+
+    (( ret )) || return 0
+  done
+
+  return 1
+else
+  _compalso -math-
+fi
diff --git a/Completion/Base/_tilde b/Completion/Base/_tilde
index aef575e19..7ab058e01 100644
--- a/Completion/Base/_tilde
+++ b/Completion/Base/_tilde
@@ -1,10 +1,53 @@
-#defcomp -tilde-
+#compdef -tilde-
 
 # We use all named directories and user names here. If this is too slow
 # for you or if there are too many of them, you may want to use
-# `compgen -k friends -qS/' or something like that. To get all user names
-# if there are no matches in the `friends' array, add
-#   `(( compstate[nmatches] )) || compgen -nu -qS/'
-# below that.
+# `compadd -qS/ - "$friends[@]"' or something like that.
 
-compgen -nu -qS/
+local expl suf dirs list lines revlines i ret disp nm="$compstate[nmatches]"
+
+if [[ "$SUFFIX" = */* ]]; then
+  ISUFFIX="/${SUFFIX#*/}$ISUFFIX"
+  SUFFIX="${SUFFIX%%/*}"
+  suf=(-S '')
+else
+  suf=(-qS/)
+fi
+
+_tags users named-directories directory-stack
+
+while _tags; do
+  _requested users && _users "$suf[@]" "$@" && ret=0
+  _requested named-directories expl 'named directory' \
+      compadd "$suf[@]" "$@" - "${(@k)nameddirs}"
+
+  if _requested directory-stack &&
+     { ! zstyle -T ":completion:${curcontext}:directory-stack" prefix-needed ||
+       [[ "$PREFIX" = [-+]* || nm -eq compstate[nmatches] ]] }; then
+    if zstyle -T ":completion:${curcontext}:directory-stack" verbose; then
+      integer i
+
+      lines=("${PWD}" "${dirstack[@]}")
+
+      if [[ ( -prefix - && ! -o pushdminus ) ||
+	    ( -prefix + && -o pushdminus ) ]]; then
+        revlines=( $lines )
+        for (( i = 1; i <= $#lines; i++ )); do
+          lines[$i]="$((i-1)) -- ${revlines[-$i]}"
+        done
+      else
+        for (( i = 1; i <= $#lines; i++ )); do
+          lines[$i]="$((i-1)) -- ${lines[$i]}"
+        done
+      fi
+      list=( ${PREFIX[1]}${^lines%% *} )
+      disp=( -ld lines )
+    else
+      list=( ${PREFIX[1]}{0..${#dirstack}} )
+      disp=()
+    fi
+    _all_labels -V directory-stack expl 'directory stack' \
+        compadd "$suf[@]" "$disp[@]" -Q - "$list[@]" && ret=0
+  fi
+  (( ret )) || return 0
+done
diff --git a/Completion/Base/_values b/Completion/Base/_values
index 4be3e8203..62cf0e409 100644
--- a/Completion/Base/_values
+++ b/Completion/Base/_values
@@ -1,322 +1,103 @@
 #autoload
 
-setopt localoptions extendedglob
+local subopts opt usecc
 
-local name arg def descr xor str tmp ret=1 expl nm="$compstate[nmatches]"
-local snames odescr gdescr sep
-typeset -A names onames xors _values
+subopts=()
+while [[ "$1" = -(O*|C) ]]; do
+  case "$1" in
+  -C) usecc=yes; shift ;;
+  -O) subopts=( "${(@P)2}" ); shift 2 ;;
+  *)  subopts=( "${(@P)1[3,-1]}" ); shift ;;
+  esac
+done
 
-# Probably fill our cache.
+if compvalues -i "$@"; then
 
-if [[ "$*" != "$_vals_cache_args" ]]; then
-  _vals_cache_args="$*"
+  local noargs args opts descr action expl sep subc
+  local oldcontext="$curcontext"
 
-  unset _vals_cache_{sep,descr,names,onames,snames,xors,odescr}
+  if ! compvalues -D descr action; then
 
-  typeset -gA _vals_cache_{names,onames,xors}
-  _vals_cache_snames=()
-  _vals_cache_odescr=()
+    _wanted values || return 1
 
-  # Get the separator, if any.
+    curcontext="${oldcontext%:*}:values"
 
-  if [[ "$1" = -s ]]; then
-    _vals_cache_sep="$2"
-    shift 2
-  fi
-
-  # This is the description string for the values.
-
-  _vals_cache_descr="$1"
-  shift
-
-  # Now parse the descriptions.
-
-  while (( $# )); do
-
-    # Get the `name', anything before an unquoted colon.
-
-    if [[ "$1" = *[^\\]:* ]]; then
-      name="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}"
-    else
-      name="$1"
-    fi
-
-    descr=''
-    xor=''
-
-    # Get a description, if any.
-
-    if [[ "$name" = *\[*\] ]]; then
-      descr="${${name#*\[}[1,-2]}"
-      name="${name%%\[*}"
-    fi
-
-    # Get the names of other values that are mutually exclusive with
-    # this one.
-
-    if [[ "$name" = \(*\)* ]]; then
-      xor="${${name[2,-1]}%%\)*}"
-      name="${name#*\)}"
-    fi
-
-    # Finally see if this value may appear more than once.
-
-    if [[ "$name" = \** ]]; then
-      name="$name[2,-1]"
-    else
-      xor="$xor $name"
-    fi
-
-    # Store the information in the cache.
-
-    _vals_cache_odescr=( "$_vals_cache_odescr[@]" "${name}:$descr" )
-    [[ -n "$xor" ]] && _vals_cache_xors[$name]="${${xor##[ 	]#}%%[ 	]#}"
-
-    # Get the description and store that.
-
-    if [[ "$1" = *[^\\]:* ]]; then
-      descr=":${1#*[^\\]:}"
-    else
-      descr=''
-    fi
-
-    if [[ "$descr" = ::* ]]; then
+    compvalues -V noargs args opts
 
-       # Optional argument.
-
-      _vals_cache_onames[$name]="$descr[3,-1]"
-    elif [[ "$descr" = :* ]]; then
-
-      # Mandatory argument.
-
-      _vals_cache_names[$name]="$descr[2,-1]"
-    else
-
-      # No argument.
-
-      _vals_cache_snames=( "$_vals_cache_snames[@]" "$name" )
-    fi
-    shift
-  done
-fi
-
-snames=( "$_vals_cache_snames[@]" )
-names=( "${(@kv)_vals_cache_names}" )
-onames=( "${(@kv)_vals_cache_onames}" )
-xors=( "${(@kv)_vals_cache_xors}" )
-odescr=( "$_vals_cache_odescr[@]" )
-gdescr="$_vals_cache_descr"
-sep="$_vals_cache_sep"
-
-if [[ -n "$sep" ]]; then
-
-  # We have a separator character. We parse the PREFIX and SUFFIX to
-  # see if any of the values that must not appear more than once are
-  # already on the line.
-
-  while [[ "$PREFIX" = *${sep}* ]]; do
-
-    # Get one part, remove it from PREFIX and put it into IPREFIX.
-
-    tmp="${PREFIX%%${sep}*}"
-    PREFIX="${PREFIX#*${sep}}"
-    IPREFIX="${IPREFIX}${tmp}${sep}"
-
-    # Get the value `name'.
-
-    name="${tmp%%\=*}"
-
-    if [[ "$tmp" = *\=* ]]; then
-      _values[$name]="${tmp#*\=}"
-    else
-      _values[$name]=''
-    fi
-
-    # And remove the descriptions for the values this one makes
-    # superfluous.
-
-    if [[ -n "$xors[$name]" ]]; then
-      snames=( "${(@)snames:#(${(j:|:)~${=xors[$name]}})}" )
-      odescr=( "${(@)odescr:#(${(j:|:)~${=xors[$name]}}):*}" )
-      unset {names,onames,xors}\[${^=tmp}\]
-    fi
-  done
-  if [[ "$SUFFIX" =  *${sep}* ]]; then
-
-    # The same for the suffix.
+    if [[ "$PREFIX" = *\=* ]]; then
+      local name
 
-    str="${SUFFIX%%${sep}*}"
-    SUFFIX="${SUFFIX#*${sep}}"
-    while [[ -n "$SUFFIX" ]]; do
-      tmp="${PREFIX%%${sep}*}"
-      if [[ "$SUFFIX" = *${sep}* ]]; then
-        SUFFIX="${SUFFIX#*${sep}}"
+      name="${PREFIX%%\=*}"
+      if compvalues -L "$name" descr action; then
+        IPREFIX="${IPREFIX}${name}="
+        PREFIX="${PREFIX#*\=}"
       else
-        SUFFIX=''
+        local prefix suffix
+
+	prefix="${PREFIX#*\=}"
+	suffix="$SUFFIX"
+	PREFIX="$name"
+	SUFFIX=''
+	args=( "$args[@]" "$opts[@]" )
+	compadd -M 'r:|[_-]=* r:|=*' -D args - "${(@)args[@]%%:*}"
+
+	[[ $#args -ne 1 ]] && return 1
+
+        PREFIX="$prefix"
+	SUFFIX="$suffix"
+        IPREFIX="${IPREFIX}${args[1]%%:*}="
+	compvalues -L "${args[1]%%:*}" descr action subc
+	curcontext="${oldcontext%:*}:$subc"
       fi
-      PREFIX="${PREFIX#*${sep}}"
-      IPREFIX="${IPREFIX}${tmp}${sep}"
-
-      name="${tmp%%\=*}"
-
-      if [[ "$tmp" = *\=* ]]; then
-        _values[$name]="${tmp#*\=}"
+    else
+      compvalues -d descr
+      if [[ ${#noargs}+${#args}+${#opts} -ne 1 ]] && compvalues -s sep; then
+        sep=( "-qQS" "$sep" )
       else
-        _values[$name]=''
+        sep=()
       fi
 
-      if [[ -n "$xors[$name]" ]]; then
-        snames=( "${(@)snames:#(${(j:|:)~${=xors[$name]}})}" )
-        odescr=( "${(@)odescr:#(${(j:|:)~${=xors[$name]}}):*}" )
-        unset {names,onames,xors}\[${^=tmp}\]
-      fi
-    done
-    SUFFIX="$str"
-  fi
-fi
-
-descr=''
-str="$PREFIX$SUFFIX"
-
-if [[ "$str" = *\=* ]]; then
-
-  # The string from the line contains a `=', so we get the stuff before
-  # it and after it and see what we can do here...
-
-  name="${str%%\=*}"
-  arg="${str#*\=}"
-
-  if (( $snames[(I)${name}] )); then
-
-    # According to our information, the value doesn't get an argument,
-    # so give up.
-
-    _message "\`${name}' gets no value"
-    return 1
-  elif (( $+names[$name] )); then
-
-    # It has to get an argument, we skip over the name and complete
-    # the argument (below).
-
-    def="$names[$name]"
-    if ! compset -P '*\='; then
-      IPREFIX="${IPREFIX}${name}="
-      PREFIX="$arg"
-      SUFFIX=''
-    fi
-  elif (( $+onames[$name] )); then
+      _describe "$descr" \
+        noargs "$sep[@]" -M 'r:|[_-]=* r:|=*' -- \
+        args -S= -M 'r:|[_-]=* r:|=*' -- \
+        opts -qS= -M 'r:|[_-]=* r:|=*'
 
-    # Gets an optional argument, same as previous case.
+      curcontext="$oldcontext"
 
-    def="$onames[$name]"
-    if ! compset -P '*\='; then
-      IPREFIX="${IPREFIX}${name}="
-      PREFIX="$arg"
-      SUFFIX=''
+      return
     fi
   else
-    local pre="$PREFIX" suf="$SUFFIX"
-
-    # The part before the `=' isn't a known value name, so we see if
-    # it matches only one of the known names.
-
-    if [[ "$PREFIX" = *\=* ]]; then
-      PREFIX="${PREFIX%%\=*}"
-      pre="${pre#*\=}"
-      SUFFIX=''
-    else
-      SUFFIX="${SUFFIX%%\=*}"
-      pre="${suf#*\=}"
-      suf=''
-    fi
-
-    tmp=( "${(@k)names}" "${(@k)onames}" )
-    compadd -M 'r:|[-_]=* r:|=*' -D tmp - "$tmp[@]"
-
-    if [[ $#tmp -eq 1 ]]; then
-
-      # It does, so we use that name and immediatly start completing
-      # the argument for it.
-
-      IPREFIX="${IPREFIX}${tmp[1]}="
-      PREFIX="$pre"
-      SUFFIX="$suf"
-
-      def="$names[$tmp[1]]"
-      [[ -z "$def" ]] && def="$onames[$tmp[1]]"
-    elif (( $#tmp )); then
-      _message "ambiguous option \`${PREFIX}${SUFFIX}'"
-      return 1
-    else
-      _message "unknown option \`${PREFIX}${SUFFIX}'"
-      return 1
-    fi
+    compvalues -C subc
+    curcontext="${oldcontext%:*}:$subc"
   fi
-else
-
-  # No `=', just complete value names.
-
-  _description expl "$gdescr"
 
-  [[ -n "$sep" && ${#snames}+${#names}+${#onames} -ne 1 ]] &&
-      expl=( "-qS$sep" "$expl[@]" )
-
-  tmp=''
-  if [[ -n "$compconfig[describe_values]" &&
-        "$compconfig[describe_values]" != *\!${words[1]}* ]]; then
-    if _display tmp odescr -M 'r:|[_-]=* r:|=*'; then
-      if (( $#snames )); then
-        compadd "$expl[@]" -y tmp -M 'r:|[_-]=* r:|=*' - \
-                "$snames[@]" && ret=0
-        compadd -n -S= -J "_$gdescr" -M 'r:|[_-]=* r:|=*' - \
-                "${(@k)names}" && ret=0
-        compadd -n -qS= -J "_$gdescr" -M 'r:|[_-]=* r:|=*' - \
-                "${(@k)onames}" && ret=0
-      elif (( $#names )); then
-        compadd -n -S= "$expl[@]" -y tmp -M 'r:|[_-]=* r:|=*' - \
-                "${(@k)names}" && ret=0
-        compadd -n -qS= -J "_$gdescr" -M 'r:|[_-]=* r:|=*' - \
-                "${(@k)onames}" && ret=0
-      else
-        compadd -n -qS= "$expl[@]" -y tmp -M 'r:|[_-]=* r:|=*' - \
-                "${(@k)onames}" && ret=0
-      fi
-    fi
-  fi
-  if [[ -z "$tmp" ]]; then
-    compadd "$expl[@]" -M 'r:|[_-]=* r:|=*' - "$snames[@]" && ret=0
-    compadd -S= "$expl[@]" -M 'r:|[_-]=* r:|=*' - "${(@k)names}" && ret=0
-    compadd -qS= "$expl[@]" -M 'r:|[_-]=* r:|=*' - "${(@k)onames}" && ret=0
+  if ! _tags arguments; then
+    curcontext="$oldcontext"
+    return 1
   fi
-  return ret
-fi
-
-if [[ -z "$def" ]]; then
-  _message 'no value'
-  return 1
-else
-  local action
-
-  descr="${${${(M)def#*[^\\]:}[1,-2]}//\\\\:/:}"
-  action="${${def#*[^\\]:}//\\\\:/:}"
 
-  _description expl "$descr"
+  _description arguments expl "$descr"
 
   # We add the separator character as a autoremovable suffix unless
   # we have only one possible value left.
 
-  [[ -n "$sep" && ${#snames}+${#names}+${#onames} -ne 1 ]] &&
+  [[ ${#snames}+${#names}+${#onames} -ne 1 ]] && compvalues -s sep &&
       expl=( "-qS$sep" "$expl[@]" )
 
   if [[ "$action" = -\>* ]]; then
-    values=( "${(@kv)_values}" )
+    compvalues -v val_args
     state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
+    if [[ -n "$usecc" ]]; then
+      curcontext="${oldcontext%:*}:$subc"
+    else
+      context="$subc"
+    fi
     compstate[restore]=''
     return 1
   else
-    typeset -A values
+    typeset -A val_args
 
-    values=( "${(@kv)_values}" )
+    compvalues -v val_args
 
     if [[ "$action" = \ # ]]; then
 
@@ -332,36 +113,44 @@ else
 
       eval ws\=\( "${action[3,-3]}" \)
 
-      if _display tmp ws; then
-        compadd "$expl[@]" -y tmp - "${(@)ws%%:*}"
-      else
-        _message "$descr"
-        return 1
-      fi
+      _describe "$descr" ws -M 'r:|[_-]=* r:|=*' "$subopts[@]"
+
     elif [[ "$action" = \(*\) ]]; then
 
       # Anything inside `(...)' is added directly.
 
-      compadd "$expl[@]" - ${=action[2,-2]}
+      _all_labels arguments expl "$descr" \
+          compadd "$subopts[@]" - ${=action[2,-2]}
     elif [[ "$action" = \{*\} ]]; then
 
       # A string in braces is evaluated.
 
-      eval "$action[2,-2]"
-
+      while _next_label arguments expl "$descr"; do
+        eval "$action[2,-2]"
+      done
     elif [[ "$action" = \ * ]]; then
 
       # If the action starts with a space, we just call it.
 
-      ${(e)=~action}
+      eval "action=( $action )"
+      while _next_label arguments expl "$descr"; do
+        "$action[@]"
+      done
     else
 
       # Otherwise we call it with the description-arguments built above.
 
-      action=( $=action )
-      ${(e)action[1]} "$expl[@]" ${(e)~action[2,-1]}
+      eval "action=( $action )"
+      _all_labels arguments expl "$descr" \
+          "$action[1]" "$subopts[@]" "${(@)action[2,-1]}"
     fi
   fi
-fi
 
-[[ nm -ne "$compstate[nmatches]" ]]
+  curcontext="$oldcontext"
+
+  [[ nm -ne "$compstate[nmatches]" ]]
+else
+  curcontext="$oldcontext"
+
+  return 1;
+fi