#autoload

# The main loop of the completion code. This is what is called when 
# completion is attempted from the command line.


# If you want to complete only set or unset options for the unsetopt
# and setopt builtin, un-comment these lines:
#
#   local _set_options _unset_options
#
#   _set_options=(${(k)options[(R)on]})
#   _unset_options=(${(k)options[(R)off]})
#
# This is needed because completion functions may set options locally
# which makes the output of setopt and unsetopt reflect a different
# state than the global one for which you are completing.

setopt localoptions nullglob rcexpandparam extendedglob
unsetopt markdirs globsubst shwordsplit nounset ksharrays

local comp post ret=1 _compskip _prio_num=1 format _comp_ignore \
      context state line opt_args val_args curcontext="$curcontext" \
      _last_nmatches=-1 _last_menu_style _def_menu_style _menu_style sel \
      _saved_exact="${compstate[exact]}" \
      _saved_lastprompt="${compstate[last_prompt]}" \
      _saved_list="${compstate[list]}" \
      _saved_insert="${compstate[insert]}"

typeset -U _offered_tags _tried_tags _failed_tags _used_tags _unused_tags

_offered_tags=()
_tried_tags=()
_failed_tags=()

typeset -U _lastdescr

# Special completion contexts after `~' and `='.

if compset -P 1 '='; then
  compstate[context]=equal
elif [[ "$PREFIX" != */* && "$PREFIX[1]" = '~' ]]; then
  compset -p 1
  compstate[context]=tilde
fi

# Initial setup.

_setup default
_def_menu_style=( "$_last_menu_style[@]" )
_last_menu_style=()

# Get the names of the completers to use in the positional parameters.

if (( ! $# )); then
  local tmp

  zstyle -a ":completion${curcontext}:" completer tmp
  set -- "$tmp[@]"
fi

# And now just call the completer functions defined.

for comp; do
  if "$comp"; then
    ret=0
    break;
  fi
done

if (( $compstate[nmatches] )); then
  [[ _last_nmatches -ge 0 && _last_nmatches -ne compstate[nmatches] ]] &&
      _menu_style=( "$_last_menu_style[@]" "$_menu_style[@]" )

  if [[ "$compstate[insert]" = "$_saved_insert" ]]; then
    if [[ -n "$_menu_style[(r)(yes|true|1|on)]" ||
          ( -n "$_menu_style[(r)auto*]" &&
            "$compstate[insert]" = automenu ) ]]; then
      compstate[insert]=menu
    elif [[ -n "$_menu_style[(r)auto*]" &&
            "$compstate[insert]" != automenu ]]; then
      compstate[insert]=automenu-unambiguous
    elif [[ -n "$_menu_style[(r)(no|false|0|off)]" ]]; then
      compstate[insert]=unambiguous
    elif [[ -n "$_def_menu_style[(r)(yes|true|1|on)]" ||
          ( -n "$_def_menu_style[(r)auto*]" &&
            "$compstate[insert]" = automenu ) ]]; then
      compstate[insert]=menu
    elif [[ -n "$_def_menu_style[(r)auto*]" &&
            "$compstate[insert]" != automenu ]]; then
      compstate[insert]=automenu-unambiguous
    elif [[ -n "$_def_menu_style[(r)(no|false|0|off)]" ]]; then
      compstate[insert]=unambiguous
    fi
  fi

  _menu_style=( "$_menu_style[@]" "$_def_menu_style[@]" )

  if [[ "$compstate[insert]" = *menu ]]; then
    if [[ -n "$_menu_style[(r)no-select*]" ]]; then
      unset SELECTMIN
    else
      sel=( "${(@M)_menu_style:#select*}" )

      if (( $# )); then
        local min=9999999 i num

        for i in "$sel[@]"; do
          if [[ "$i" = *\=* ]]; then
  	    num="${i#*\=}"
  	    [[ num -lt 0 ]] && num=0
  	  else
  	    num=0
  	  fi
  	  [[ num -lt min ]] && min="$num"
  
	  (( min )) || break
        done

        SELECTMIN="$min"
      fi
    fi
  fi
elif [[ compstate[matcher] -eq compstate[total_matchers] &&
        $#_lastdescr -ne 0 ]] &&
     zstyle -s ":completion${curcontext}:warnings" format format; then
  local str

  _lastdescr=( "\`${(@)^_lastdescr:#}'" )

  compstate[list]='list force'
  compstate[insert]=''

  case $#_lastdescr in
  1) str="$_lastdescr[1]";;
  2) str="$_lastdescr[1] or $_lastdescr[2]";;
  *) str="${(j:, :)_lastdescr[1,-2]}, or $_lastdescr[-1]";;
  esac

  zformat -f format "$format" "d:$str"
  compadd -UX "$format" -n ''
fi

if [[ compstate[matcher] -eq compstate[total_matchers] ||
      compstate[nmatches] -ne 0 ]]; then

  # See which tags were or were not used.

  _used_tags=( "${(@)_tried_tags:#${(j:|:)~${(@)_failed_tags//\[/\\[}//\]/\\]}}" )
  _unused_tags=( "${(@)_offered_tags:#${(j:|:)~${(@)_used_tags//\[/\\[}//\]/\\]}}" )

  # Now call the post-functions.

  for post in "$comppostfuncs[@]"; do
    "$post"
  done
  comppostfuncs=()

  _lastcomp=( "${(@kv)compstate}" )
  _lastcomp[completer]="$comp"
  _lastcomp[prefix]="$PREFIX"
  _lastcomp[suffix]="$SUFFIX"
  _lastcomp[iprefix]="$IPREFIX"
  _lastcomp[isuffix]="$ISUFFIX"
  _lastcomp[qiprefix]="$QIPREFIX"
  _lastcomp[qisuffix]="$QISUFFIX"
  _lastcomp[offered_tags]="${(j.:.)_offered_tags}"
  _lastcomp[tried_tags]="${(j.:.)_tried_tags}"
  _lastcomp[failed_tags]="${(j.:.)_failed_tags}"
  _lastcomp[unused_tags]="${(j.:.)_unused_tags}"
  _lastcomp[used_tags]="${(j.:.)_used_tags}"
fi

return ret
