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

PATCH: new fun with completion



This adds the function `_values' which can be used to complete, well,
values, which are just string. But they can also have arguments (of
the form `val=arg'). And `_values' can complete lists of such
values. The possible values are described in a way very similar to the
way options are described for `_arguments' and all the gizmos are
there (descriptions, exclusion lists, optional arguments, the
different styles for actions, etc.).

It also adds a `_nslookup' as an example for using `_values'. This
function can complete both command line options and stuff when
`nslookup' is called in interactive mode when the supplied wrapper
function `Functions/Misc/nslookup' is used (I hope someone more
familiar with `nslookup' can check/improve that function). So,
`_nslookup' and `nslookup' also serve as an example of how to use
`compcontext'.


Bye
 Sven

diff -u od/Zsh/compsys.yo Doc/Zsh/compsys.yo
--- od/Zsh/compsys.yo	Fri Sep  3 21:06:33 1999
+++ Doc/Zsh/compsys.yo	Sun Sep  5 20:38:18 1999
@@ -1021,6 +1083,48 @@
 `var(postscript file)' and makes files ending in `tt(ps)' or `tt(eps)' 
 be completed. The last description says that all other arguments are
 `var(page numbers)' but does not give possible completions.
+)
+item(tt(_values))(
+This is used to complete values (strings) and their arguments or
+lists of such values. If the first argument is the option `tt(-s)',
+the second argument is used as the character that separates multiple
+values.
+
+The first argument (after the option and separator character if they
+are given) is used as a string to print as a description before
+listing the values.
+
+All other arguments describe the possible values and their
+arguments in the same format used for the description of options by
+the tt(_arguments) function (see above). The only difference is that
+there is no required minus or plus sign at the beginning and that
+values can have only one argument.
+
+Example:
+
+example(_values -s , '...' \
+        '*foo[bar]' \
+        '(two)*one[number]:first count:' \
+        'two[another number]::second count:(1 2 3)')
+
+This describes three possible values: `tt(foo)', `tt(one)', and
+`tt(two)'. The first one is described as `tt(bar)', gets no argument 
+and may appear more than once. The second one is described as
+`tt(number)', may appear more than once, and gets one mandatory
+argument described as `tt(first count)' for which no action is
+specified so that it will not be completed automatically. The
+`tt((one))' at the beginning says that if the value `tt(one)' is on
+the line, the value `tt(two)' will not be  considered to be a possible
+completion any more. Finally, the last value (`tt(two)') is described
+as `tt(another number)' and gets an optional argument decribed as
+`tt(second count)' which will be completed from the strings `tt(1)',
+`tt(2)', and `tt(3)'. The tt(_values) function will complete lists of
+these values separated by commas.
+
+To decide if the descriptions for the values (not those for the
+arguments) should be printed, the configuration key
+tt(describe_values) is used in the same way as the key
+tt(describe_options) is used by the tt(_arguments) function.
 )
 enditem()
 
diff -u -r Completion.old/Base/_values Completion/Base/_values
--- Completion.old/Base/_values	Mon Sep  6 09:48:57 1999
+++ Completion/Base/_values	Sun Sep  5 22:00:22 1999
@@ -0,0 +1,367 @@
+#autoload
+
+setopt localoptions extendedglob
+
+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
+
+# Probably fill our cache.
+
+if [[ "$*" != "$_vals_cache_args" ]]; then
+  _vals_cache_args="$*"
+
+  unset _vals_cache_{sep,descr,names,onames,snames,xors,odescr}
+
+  typeset -gA _vals_cache_{names,onames,xors}
+  _vals_cache_snames=()
+  _vals_cache_odescr=()
+
+  # Get the separator, if any.
+
+  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
+
+       # 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.
+
+    str="${SUFFIX%%${sep}*}"
+    SUFFIX="${SUFFIX#*${sep}}"
+    while [[ -n "$SUFFIX" ]]; do
+      tmp="${PREFIX%%${sep}*}"
+      if [[ "$SUFFIX" = *${sep}* ]]; then
+        SUFFIX="${SUFFIX#*${sep}}"
+      else
+        SUFFIX=''
+      fi
+      PREFIX="${PREFIX#*${sep}}"
+      IPREFIX="${IPREFIX}${tmp}${sep}"
+
+      name="${tmp%%\=*}"
+
+      if [[ "$tmp" = *\=* ]]; then
+        _values[$name]="${tmp#*\=}"
+      else
+        _values[$name]=''
+      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
+
+    # Gets an optional argument, same as previous case.
+
+    def="$onames[$name]"
+    if ! compset -P '*\='; then
+      IPREFIX="${IPREFIX}${name}="
+      PREFIX="$arg"
+      SUFFIX=''
+    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
+  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
+  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"
+
+  # We add the separator character as a autoremovable suffix unless
+  # we have only one possible value left.
+
+  [[ -n "$sep" && ${#snames}+${#names}+${#onames} -ne 1 ]] &&
+      expl=( "-qS$sep" "$expl[@]" )
+
+  if [[ "$action" = -\>* ]]; then
+    values=( "${(@kv)_values}" )
+    state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
+    compstate[restore]=''
+    return 1
+  else
+    typeset -A values
+
+    values=( "${(@kv)_values}" )
+
+    if [[ "$action" = \ # ]]; then
+
+      # An empty action means that we should just display a message.
+
+      _message "$descr"
+      return 1
+
+    elif [[ "$action" = \(\(*\)\) ]]; then
+      local ws
+
+      # ((...)) contains literal strings with descriptions.
+
+      eval ws\=\( "${action[3,-3]}" \)
+
+      if _display tmp ws; then
+        compadd "$expl[@]" -y tmp - "${(@)ws%%:*}"
+      else
+        _message "$descr"
+        return 1
+      fi
+    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
+fi
+
+[[ nm -ne "$compstate[nmatches]" ]]
diff -u -r Completion.old/User/_hosts Completion/User/_hosts
--- Completion.old/User/_hosts	Fri Sep  3 21:06:44 1999
+++ Completion/User/_hosts	Fri Sep  3 23:05:30 1999
@@ -1,4 +1,4 @@
-#compdef ftp ncftp ping rwho rup xping traceroute nslookup telnet
+#compdef ftp ncftp ping rwho rup xping traceroute telnet
 
 local expl
diff -u Completion.old/User/_nslookup Completion/User/_nslookup
--- Completion.old/User/_nslookup	Mon Sep  6 10:48:39 1999
+++ Completion/User/_nslookup	Mon Sep  6 10:43:52 1999
@@ -0,0 +1,159 @@
+#compdef nslookup
+
+# This may also be called from the `nslookup' wrapper function during
+# `vared'iting a line.
+# In this case this function tries to call other user-defined functions
+# for certain contexts before adding completion. If these functions are
+# defined, they are called and the default completions from this function
+# are not added. The functions called are named `_nslookup_<state>', with
+# `<state>' being any of:
+#
+#  command
+#    When completing the first word on the line.
+#  redirect
+#    When completing after a redirection operator.
+#
+# Also, when completing after the first word, if the first word contains
+# only lower case letters, we try to call the function `_nslookup_<word>',
+# where `<word>' is the first word from the line. If the first word contains
+# other characters than lower case letters, we try to call the function
+# `_nslookup_host'.
+
+setopt localoptions extendedglob
+
+local state expl ret=1 setopts
+
+setopts=(
+  'all[print current values]' \
+  '(nodebug)debug[simple debugging information]' \
+  '(debug)nodebug[no simple debugging information]' \
+  '(nod2)d2[extra debugging information]' \
+  '(d2)nod2[no extra debugging information]' \
+  '(nodefname)defname[append default domain name]' \
+  '(defname)nodefname[don'"'"'t append default domain name]' \
+  '(nosearch)search[append search list]' \
+  '(search)nosearch[don'"'"'t append search list]' \
+  '(norecurse)recurse[name server may query other servers]' \
+  '(recurse)norecurse[name server may not query other servers]' \
+  '(novc)vc[use virtual circuit]' \
+  '(vc)novc[don'"'"'t use virtual circuit]' \
+  '(noignoretc)ignoretc[ignore packet truncation errors]' \
+  '(ignoretc)noignoretc[don'"'"'t ignore packet truncation errors]' \
+  'class[change query class]:query class:((in\:Internet\ class chaos\:CHAOS\ class hesiod\:MIT\ Athena\ Hesiod\ class any\:wildcard\ \(any\ of\ the\ above\)))'
+  'domain[change default domain]:default domain:_hosts'
+  'srchlist[change default domain and search list]: :->srchlist'
+  'port[change name server port]:name server port:'
+  {query,}type'[change type of information query]:query information type:((a\:internet\ address cname\:canonical\ name\ for\ alias hinfo\:CPU\ and\ operating\ system\ type minfo\:mailbox\ or\ mail\ list\ information mx\:mail\ exchanger ns\:name\ server\ for\ zone ptr\:host\ name\ or\ other\ information soa\:domain\'"'"'s\ \`start-of-authority\'"'"'\ information txt\:text\ information uinfo\:user\ information wks\:supported\ well-known\ services))'
+  'retry[change number of retries]:number of retries:'
+  'root[change name of root server]:root server:_hosts'
+  'timeout[change initial timeout interval]:timeout (seconds):'
+)
+
+if [[ -n "$compcontext" ]]; then
+  if [[ CURRENT -eq 1 ]]; then
+
+    funcall ret _nslookup_command && return ret
+
+    _description expl 'command'
+    compadd "$expl[@]" - server lserver root finger ls view help set && ret=0
+    _hosts && ret=0
+    return ret
+  elif [[ "$compstate[context]" = redirect ]]; then
+
+    funcall ret _nslookup_redirect && return ret
+
+    if [[ "$words[1]" != (finger|ls) ]]; then
+      _message "redirection not allowed for command \`$words[1]'"
+      return 1
+    elif [[ "$compstate[redirect]" = '>' ]]; then
+      _description expl 'write to file'
+    elif [[ "$compstate[redirect]" = '>>' ]]; then
+      _description expl 'append to file'
+    else
+      _message "unknown redirection operator \`$compstate[redirect]'"
+      return 1
+    fi
+
+    _files "$expl[@]"
+    return
+  fi
+
+  if [[ "$words[1]" = [a-z]## ]]; then
+    funcall ret _nslookup_$words[1] && return ret
+  else
+    funcall ret _nslookup_host && return ret
+  fi
+
+  case "$words[1]" in
+  (|l)server)
+    _description expl 'new default server'
+    _hosts "$expl[@]"
+    return
+    ;;
+  root|exit|help|\?)
+    return 1
+    ;;
+  finger)
+    _message 'finger name'
+    return 1
+    ;;
+  ls)
+    _arguments -s \
+      '-t[records of given type]:query information type:((a\:internet\ address cname\:canonical\ name\ for\ alias hinfo\:CPU\ and\ operating\ system\ type minfo\:mailbox\ or\ mail\ list\ information mx\:mail\ exchanger ns\:name\ server\ for\ zone ptr\:host\ name\ or\ other\ information soa\:domain\'"'"'s\ \`start-of-authority\'"'"'\ information txt\:text\ information uinfo\:user\ information wks\:supported\ well-known\ services))' \
+     '-a[aliases of hosts in domain]' \
+     '-d[all records]' \
+     '-h[CPU and operating system information]' \
+     '-s[well-known services]' \
+     ':domain:_hosts'
+    return
+    ;;
+  view)
+    _description expl 'view file'
+    _files "$expl[@]"
+    return
+    ;;
+  set)
+    typeset -A values
+
+    _values 'state information' "$setopts[@]" && ret=0
+
+    [[ -z "$state" ]] && return ret
+    ;;
+  *)
+    _description expl 'server'
+    _hosts "$expl[@]"
+    return
+  esac
+fi
+
+# Now comes the command line option completion part.
+
+if [[ -z "$state" ]]; then
+  local line
+  typeset -A options
+
+  _arguments \
+    "-${(@)^${(@M)setopts:#*\]:*}/\[/=[}" \
+    "-${(@)^setopts:#(\(|*\]:)*}" \
+    "${(@)^${(@)${(@M)setopts:#\(*}/\)/)-}/\(/(-}" \
+    ':host to find:_hosts' \
+    ':server:_hosts' && ret=0
+fi
+
+# This is completion after `srchlist' for both types.
+
+if [[ -n "$state" ]]; then
+  if compset -P '*/'; then
+    _description expl 'search list entry'
+  else
+    _description expl 'default domain name and first search list entry'
+  fi
+  if [[ -n "$_vals_cache_multi" ]]; then
+    _hosts "$expl[@]" -qS/ -r "/\\- \\t\\n$_vals_cache_multi"
+  else
+    _hosts "$expl[@]" -qS/
+  fi
+  return
+fi
+
+return ret
--- Functions/Misc/nslookup.old	Mon Sep  6 10:48:19 1999
+++ Functions/Misc/nslookup	Mon Sep  6 10:47:39 1999
@@ -0,0 +1,34 @@
+# Simple wrapper function for `nslookup'. With completion if you are using
+# the function based completion system.
+
+setopt localoptions completealiases
+
+local char line compcontext=nslookup pid
+
+trap 'print -p exit;return' INT
+
+coproc command nslookup
+pid=$!
+
+while read -pk1 char; do
+  line="$line$char"
+  [[ "$line" = *'
+> ' ]] && break
+done
+print -nr - "$line"
+
+line=''
+while vared -p '> ' line; do
+  print -p "$line"
+  line=''
+  while read -pk1 char; do
+    line="$line$char"
+    [[ "$line" = *'
+> ' ]] && break
+  done
+  print -nr - "$line"
+  line=''
+done
+
+print -p exit
+wait $pid
 
--
Sven Wischnowsky                         wischnow@xxxxxxxxxxxxxxxxxxxxxxx



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