Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: PATCH: completion



Tanaka Akira wrote:

> In article <199908261220.OAA15915@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>,
>   Sven Wischnowsky <wischnow@xxxxxxxxxxxxxxxxxxxxxxx> writes:
> 
> > @@ -236,7 +240,11 @@
> > -          inopt=yes
> > +	  if [[ -n "$def" ]]; then
> > +            inopt=yes
> > +	  else
> > +            inopt=''
> > +	  fi
> 
> In article <199908261352.PAA16045@xxxxxxxxxxxxxxxxxxxxxxxxxxxx>,
>   Sven Wischnowsky <wischnow@xxxxxxxxxxxxxxxxxxxxxxx> writes:
> 
> > -          inopt=yes
> > +	  inopt=yes
> 
> These two hunks cause conflicts.  So I couldn't update _arguments to
> latest.

I have no idea how this happened, sorry.

We could either try this patchlet:

--- oc/Base/_arguments	Fri Aug 27 08:58:50 1999
+++ Completion/Base/_arguments	Fri Aug 27 08:59:00 1999
@@ -236,11 +236,7 @@
 	  def="$opts[$tmp]"
 	  optbeg="$beg"
 	  argbeg="$beg"
-	  if [[ -n "$def" ]]; then
-            inopt=yes
-	  else
-            inopt=''
-	  fi
+          inopt=yes
 	  uns="${ws[1][2,-1]}"
 	  opt=''
 	fi

Or -- in case something else went wrong -- just use the version of
`_arguments' I currently have...

Bye
 Sven

#autoload

# Complete the arguments of the current command according to the
# descriptions given as arguments to this function.

setopt localoptions extendedglob

local long args rest ws cur nth def nm expl descr action opt arg tmp
local single uns ret=1 soptseq soptseq1 sopts prefix line
local beg optbeg argbeg nargbeg inopt fromrest

# Associative arrays used to collect information about the options.

typeset -A opts dopts odopts
typeset -A oneshot

# Fill the cache if we were called with different arguments.

if [[ "$*" != "$_args_cache_descr" ]]; then
  _args_cache_descr="$*"

  unset _args_cache_{opts,dopts,odopts,oneshot}
  typeset -gA _args_cache_{opts,dopts,odopts,oneshot}

  unset _args_cache_{long,single,rest,args,sopts,soptseq,soptseq1}

  # See if we are using single-letter options.

  if [[ "$1" = -s ]]; then
    shift
    _args_cache_single=yes
  fi

  # See if we support long options, too.

  nth=$argv[(I)--]
  if (( nth )); then
    _args_cache_long=( "${(@)argv[nth+1,-1]}" )
    _args_cache_long_nth=$(( nth - 1 ))
  else
    _args_cache_long=()
  fi

  # Now parse the arguments...

  args=()
  nth=1
  while (( $# )); do

    # This describes a one-shot option.

    if [[ "$1" = [-+]* ]]; then
      if [[ "$1" = *:* ]]; then

        # 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.

        if [[ "$1" = [^:]##-:* ]]; then
  	  tmp="${${1%%:*}[1,-2]}"
          _args_cache_dopts[$tmp]="${1#*:}"
        elif [[ "$1" = [^:]##+:* ]]; then
  	  tmp="${${1%%:*}[1,-2]}"
          _args_cache_odopts[$tmp]="${1#*:}"
        else
          tmp="${1%%:*}"
          _args_cache_opts[$tmp]="${1#*:}"
        fi
      else
        tmp="$1"
        _args_cache_opts[$tmp]=''
      fi
      _args_cache_oneshot[$tmp]=yes
    elif [[ "$1" = \*[-+]* ]]; then

      # The same for options that may appear more than once.

      if [[ "$1" = *:* ]]; then
        if [[ "$1" = [^:]##-:* ]]; then
          tmp="${${1[2,-1]%%:*}[1,-2]}"
          _args_cache_dopts[$tmp]="${1#*:}"
        elif [[ "$1" = [^:]##+:* ]]; then
          tmp="${${1[2,-1]%%:*}[1,-2]}"
          _args_cache_odopts[$tmp]="${1#*:}"
        else
          tmp="${1[2,-1]%%:*}"
          _args_cache_opts[$tmp]="${1#*:}"
        fi
      else
        tmp="${1[2,-1]}"
        _args_cache_opts[$tmp]=''
      fi
      unset "_args_cache_oneshot[$tmp]"
    elif [[ "$1" = \*::* ]]; then

      # This is `*:...', describing `all other arguments', with argument 
      # range restriction.

      if [[ "$1" = \*:::* ]]; then
        _args_cache_rest="*${1[3,-1]}"
      else
        _args_cache_rest="$1"
      fi
    elif [[ "$1" = \*:* ]]; then

      # This is `*:...', describing `all other arguments'.

      _args_cache_rest="${1[3,-1]}"
    elif [[ "$1" = :* ]]; then

      # This is `:...', describing `the next argument'.

      _args_cache_args[nth++]="${1#*:}"
    else

      # And this is `n:...', describing the `n'th argument.

      _args_cache_args[${1%%:*}]="${1#*:}"
      nth=$(( ${1%%:*} + 1 ))
    fi
    shift
  done

  if [[ -n "$_args_cache_single" ]]; then
    _args_cache_soptseq="${(@j::)${(@M)${(@k)opts[(R)]}:#[-+]?}#[-+]}"
    if [[ -n "$_args_cache_soptseq" ]]; then
      _args_cache_soptseq="[$_args_cache_soptseq]#"
      _args_cache_soptseq1="$_args_cache_soptseq#"
    else
      _args_cache_soptseq=''
      _args_cache_soptseq1=''
    fi
    _args_cache_sopts="${(@j::)${(@M)${(@k)opts}:#[-+]?}#[-+]}${(@j::)${(@M)${(@k)dopts}:#[-+]?}#[-+]}${(@j::)${(@M)${(@k)odopts}:#[-+]?}#[-+]}"
  else
    _args_cache_soptseq=''
    _args_cache_soptseq1=''
    _args_cache_sopts=''
  fi
fi

soptseq="$_args_cache_soptseq"
soptseq1="$_args_cache_soptseq1"
sopts="$_args_cache_sopts"
args=( "$_args_cache_args[@]" )
rest="$_args_cache_rest"
opts=( "${(@kv)_args_cache_opts}" )
dopts=( "${(@kv)_args_cache_dopts}" )
odopts=( "${(@kv)_args_cache_odopts}" )
oneshot=( "${(@kv)_args_cache_oneshot}" )
single="$_args_cache_single"
long=( "$_args_cache_long[@]" )

argv=( "${(@)argv[1,_args_cache_long_nth]}" )

# Parse the command line...

ws=( "${(@)words[2,-1]}" )
cur=$(( CURRENT-2 ))
nth=1
line=( "$words[1]" )
beg=2
argbeg=1
optbeg=1
nargbeg=1

# ...until the current word is reached.

while [[ cur -gt 0 ]]; do

  # `def' holds the description for the option we are currently after.
  # Check if the next argument for the option is optional.

  if [[ "$def" = :* ]]; then
    opt=yes
  else
    opt=''
  fi
  arg=''

  # See if we are after an option getting n arguments ended by something
  # that matches the current word.

  if [[ "$def" = \**[^\\]:* && "$ws[1]" = ${~${(M)def#*[^\\]:}[2,-2]} ]]; then
    def=''
    shift 1 ws
    (( cur-- ))
    (( beg++ ))
    continue
  fi

  # Remove one description/action pair from `def' if that isn't empty.

  if [[ -n "$def" && "$def" != \** ]]; then
    if [[ "$def" = ?*[^\\]:*[^\\]:* ]]; then
      def="${def#?*[^\\]:*[^\\]:}"
      argbeg="$beg"
    else
      def=''
    fi
  elif [[ -z "$def" ]]; then

    # 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.

    if [[ $#long -eq 0 || "$ws[1]" != --* ]]; then
      opt=yes
      arg=yes
    else
      def=''
    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 don't offer them again.

      def="$opts[$ws[1]]"
      optbeg="$beg"
      argbeg="$beg"
      inopt=yes
      [[ -n "$oneshot[$ws[1]]" ]] && unset "opts[$ws[1]]"
    else
      uns=''
      if [[ -n "$sopts" && "$ws[1]" = [-+]${~soptseq}[$sopts] ]]; then
	tmp="${ws[1][1]}${ws[1][-1]}"
	if (( $+opts[$tmp] )); then
	  def="$opts[$tmp]"
	  optbeg="$beg"
	  argbeg="$beg"
          inopt=yes
	  uns="${ws[1][2,-1]}"
	  opt=''
	fi
      fi

      # 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 two loops looking very much alike.

      if [[ -n "$opt" && $#dopts -ne 0 ]]; then

	# First we get the option names.

	tmp=( "${(@k)dopts}" )

	# Then we loop over them and see if the current word begins
	# with one of the option names.

	while (( $#tmp )); do
          if [[ -n "$sopts" && $tmp[1] = [-+]? ]]; then
	    if [[ "$ws[1]" = ${tmp[1][1]}${~soptseq}${tmp[1][2]}* ]]; then
	      uns="${ws[1][2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
	      break;
	    fi
	  elif [[ "$ws[1]" = ${tmp[1]}* ]]; then
            break
	  fi
	  shift 1 tmp
	done

	if (( $#tmp )); then

	  # 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.

	  opt=''
	  def="$dopts[$tmp[1]]"
	  optbeg="$beg"
	  argbeg="$beg"
	  inopt=yes
	  [[ -n "$oneshot[$tmp[1]]" ]] && unset "dopts[$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
          if [[ -n "$sopts" && $tmp[1] = [-+]? ]]; then
	    if [[ "$ws[1]" = ${tmp[1][1]}${~soptseq}${tmp[1][2]}* ]]; then
	      uns="${ws[1][2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
	      break;
	    fi
	  elif [[ "$ws[1]" = ${tmp[1]}* ]]; then
	    break
	  fi
	  shift 1 tmp
	done

	if (( $#tmp )); then
	  opt=''
	  def="$odopts[$tmp[1]]"
	  optbeg="$beg"
	  argbeg="$beg"
	  inopt=yes
	  [[ -n "$oneshot[$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 [[ ( -z "$sopts" && "$ws[1]" != "$tmp[1]" ) ||
                ( -n "$sopts" && ( ( $tmp[1] = [-+]? && "$ws[1]" != "${tmp[1][1]}"${~soptseq}"${tmp[1][2]}" ) ||
		  		   ( $tmp[1] != [-+]? && "$ws[1]" != "$tmp[1]" ) ) ) ]]; then
	    if [[ "$def" = [^*]*[^\\]:*[^\\]:* ]]; then
              def="${def#?*[^\\]:*[^\\]:}"
	      optbeg="$beg"
	      argbeg="$beg"
            else
              def=''
            fi
	  fi
        fi
      fi

      [[ -n "$sopts" && -n "$opt" && "$ws[1]" = [-+]${~soptseq} ]] && \
          uns="${ws[1][2,-1]}"

      if [[ -n "$uns" ]]; then
	uns="${(j::)${(@k)oneshot[(I)${ws[1][1]}[$uns]]#[-+]}}"
	tmp=(
	  "opts[${(@)^opts[(I)${ws[1][1]}[$uns]]}]"
	  "dopts[${(@)^dopts[(I)${ws[1][1]}[$uns]]}]"
	  "odopts[${(@)^odopts[(I)${ws[1][1]}[$uns]]}]"
	)
	(( $#tmp )) && unset "$tmp[@]"
      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=''
	line=( "$line[@]" "$ws[1]" )
	[[ -n "$inopt" ]] && nargbeg=$(( beg - 1 ))
	inopt=''
	(( nth++ ))
      fi
    fi
  fi

  shift 1 ws
  (( cur-- ))
  (( beg++ ))
done

[[ -n "$inopt" ]] && nargbeg=$(( beg - 1 ))

# Now generate the matches.

if [[ $#long -ne 0 && "$PREFIX" = --* ]]; then

  # If the current words starts with `--' and we should use long
  # options, just call...

  _long_options "$long[@]"

else

  nm="$compstate[nmatches]"

  if [[ -z "$def" || "$def" = :* ]]; then
      local pre="$PREFIX"

      uns=''

    # We either don't have a description for an argument of an option
    # or we have a description for a optional argument.

    if [[ -z "$def" ]]; then

      # If we have none at all, use the one for this argument position.

      def="$args[nth]"
      if [[ -z "$def" ]]; then
        def="$rest"
	optbeg="$nargbeg"
	argbeg="$nargbeg"
	fromrest=yes
      fi
    fi

    # In any case, we have to complete option names here, but we may
    # be in a string that starts with an option name and continues with
    # the first argument, test that (again, two loops).

    opt=yes
    if (( $#dopts )); then

      # Get the option names.

      tmp=( "${(@k)dopts}" )
      prefix="$PREFIX"
      while (( $#tmp )); do
        if [[ -n "$sopts" && $tmp[1] = [-+]? ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}"; then
          def="$dopts[$tmp[1]]"
	  opt=''
	  uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
	  break
        elif compset -P "$tmp[1]"; then

	  # The current string starts with the option name, so ignore
	  # that and complete the rest of the string.

	  def="$dopts[$tmp[1]]"
	  opt=''
	  break
        fi
	shift 1 tmp
      done
    fi
    if [[ -n "$opt" && $#odopts -ne 0 ]]; then
      tmp=( "${(@k)odopts}" )
      prefix="$PREFIX"
      while (( $#tmp )); do
        if [[ -n "$sopts" && $tmp[1] = [-+]? ]] && compset -P "${tmp[1][1]}${~soptseq}${tmp[1][2]}"; then
          def="$odopts[$tmp[1]]"
	  opt=''
	  uns="${prefix[2,-1]%%${tmp[1][2]}*}${tmp[1][2]}"
	  break
        elif compset -P "$tmp[1]"; then
	  def="$odopts[$tmp[1]]"
	  opt=''
	  break
        fi
	shift 1 tmp
      done
    fi

    [[ -n "$sopts" && -n "$opt" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]] && \
        uns="${PREFIX[2,-1]}"

    if [[ -n "$uns" ]]; then
      uns="${(j::)${(@k)oneshot[(I)${ws[1][1]}[$uns]]#[-+]}}"
      tmp=(
	"opts[${(@)^opts[(I)${pre[1]}[$uns]]}]"
	"dopts[${(@)^dopts[(I)${pre[1]}[$uns]]}]"
	"odopts[${(@)^odopts[(I)${pre[1]}[$uns]]}]"
      )
      (( $#tmp )) && unset "$tmp[@]"
    fi

    if [[ -n "$opt" && ( "$def" != \** ||
                         ( -n "$fromrest" && CURRENT -eq argbeg+1 ) ) ]]; then

      # We aren't in an argument directly after a option name, so
      # all option names are possible matches.

      if [[ "$compconfig[option_prefix]" != *(short|all)* ||
            "$PREFIX" = [-+]* ]]; then
        _description expl option
	if [[ -n "$sopts" && -n "$PREFIX" && "$PREFIX" = [-+]${~soptseq}[$sopts] ]]; then
	  if [[ "$PREFIX" = [-+]${~soptseq1} ]]; then
	    compadd "$expl[@]" -Q \
                    -y "( ${(j: :)${(@M)${(@k)opts}:#[-+]?}} ${(j: :)${(@M)${(@k)dopts}:#[-+]?}} ${(j: :)${(@M)${(@k)odopts}:#[-+]?}} )" - \
                    "${PREFIX}${(@k)^opts[(I)${PREFIX[1]}?]#?}" \
		    "${PREFIX}${(@k)^dopts[(I)${PREFIX[1]}?]#?}" \
		    "${PREFIX}${(@k)^odopts[(I)${PREFIX[1]}?]#?}" && ret=0
	  else
	    # The last option takes an argument in next word.
	    compadd "$expl[@]" -Q - "${PREFIX}" && ret=0
	  fi
	else
          compadd "$expl[@]" -Q - "${(@k)opts}" "${(@k)odopts}" && ret=0
	  compadd "$expl[@]" -QS '' - "${(@k)dopts}" && ret=0
        fi
      fi
      [[ $#long -ne 0 &&
         ( "$compconfig[option_prefix]" != *(long|all)* ||
           "$PREFIX" = --* ) ]] && \
	  _long_options "$long[@]" && ret=0
    fi
  fi

  # Now add the matches from the description, if any.

  if [[ -n "$def" ]]; then

    # Ignore the leading colon or `*...' describing optional arguments.

    if [[ "$def" = :* ]]; then
      def="$def[2,-1]"
    elif [[ "$def" = \** ]]; then
      tmp="${${(M)def#*[^\\]:}[2,-2]}"
      def="${def#*[^\\]:}"

      if [[ "$def" = :* ]]; then
        if [[ "$def" = ::* ]]; then
          def="$def[3,-1]"
	  beg=$argbeg
	else
	  def="$def[2,-1]"
	  beg=$optbeg
	fi

	[[ beg -ge $#words ]] && beg=$(( $#words - 1 ))

	shift beg words
	(( CURRENT -= beg ))

	if [[ -n "$tmp" ]]; then
          tmp="$words[(ib:CURRENT:)${~tmp}]"
	  [[ tmp -le $#words ]] && words=( "${(@)words[1,tmp-1]}" )
	fi
      fi
    fi

    # Get the description and the action.

    descr="${${${(M)def#*[^\\]:}[1,-2]}//\\\\:/:}"
    if [[ "$def" = *[^\\]:*[^\\]:* ]]; then
      action="${${${(M)${def#*[^\\]:}#*[^\\]:}[1,-2]}//\\\\:/:}"
    else
      action="${${def#*[^\\]:}//\\\\:/:}"
    fi

    _description expl "$descr"

    if [[ -z "$action" ]]; then

      # An empty action means that we should just display a message.
      _message "$descr"
      return ret
    elif [[ "$action" = \(*\) ]]; then

      # Anything inside `(...)' is added directly.

      compadd "$expl[@]" - ${=action[2,-2]}
    elif [[ "$action" = \{*\} ]]; then

      # A string in braces is evaluated.

      eval "$action[2,-2]"

    elif [[ "$action" = \ * ]]; then

      # If the action starts with a space, we just call it.

      ${(e)=~action}
    else

      # Otherwise we call it with the description-arguments built above.

      action=( $=action )
      ${(e)action[1]} "$expl[@]" ${(e)~action[2,-1]}
    fi
  fi

  # Set the return value.

  [[ nm -ne "$compstate[nmatches]" ]]
fi

--
Sven Wischnowsky                         wischnow@xxxxxxxxxxxxxxxxxxxxxxx



Messages sorted by: Reverse Date, Date, Thread, Author