diff options
101 files changed, 1909 insertions, 1127 deletions
diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter index 3d663a777..d45e317be 100644 --- a/Completion/Base/_brace_parameter +++ b/Completion/Base/_brace_parameter @@ -1,4 +1,4 @@ -#defcomp -brace-parameter- +#compdef -brace-parameter- if [[ "$SUFFIX" = *\}* ]]; then ISUFFIX="${SUFFIX#*\}}$ISUFFIX" diff --git a/Completion/Base/_command_names b/Completion/Base/_command_names index f21af674c..8f75f1a77 100644 --- a/Completion/Base/_command_names +++ b/Completion/Base/_command_names @@ -1,4 +1,4 @@ -#defcomp -command- +#compdef -command- local nm=$compstate[nmatches] ret=1 diff --git a/Completion/Base/_condition b/Completion/Base/_condition index df1bf913e..c617d9d90 100644 --- a/Completion/Base/_condition +++ b/Completion/Base/_condition @@ -1,4 +1,4 @@ -#defcomp -condition- +#compdef -condition- local prev="$words[CURRENT-1]" diff --git a/Completion/Base/_default b/Completion/Base/_default index 9ea1a09db..b49ca7115 100644 --- a/Completion/Base/_default +++ b/Completion/Base/_default @@ -1,4 +1,4 @@ -#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. diff --git a/Completion/Base/_equal b/Completion/Base/_equal index f407014fe..0e2a11e46 100644 --- a/Completion/Base/_equal +++ b/Completion/Base/_equal @@ -1,3 +1,3 @@ -#defcomp -equal- +#compdef -equal- compgen -am diff --git a/Completion/Base/_long_options b/Completion/Base/_long_options index 97b11f2dc..6288fc3a0 100644 --- a/Completion/Base/_long_options +++ b/Completion/Base/_long_options @@ -32,12 +32,25 @@ # for option descriptions containing `=FILE' and paths for option # descriptions that contain `=DIR' or `=PATH'. These builtin patterns # can be overridden by patterns given as arguments, though. +# +# This function accepts following options: +# +# -t do completion only on words starting with two hyphens +# +# -i list of patterns. Options, matching these patterns, are ignored. +# The list may be given as array name or as literal list in braces. +# E.g. _long_options -i '(--(enable|disable)-FEATURE*)' will ignore +# --enable-FEATURE, that is listed in configure help output +# +# -s list of pattern/replacement pairs. The argument is the same as above. +# E.g. configure often lists only --enable but accepts both +# --enable and --disable options. +# _long_options -s '(#--enable- --disable)' will accept both forms. # # This function also accepts the `-X', `-J', and `-V' options which -# are given to `compadd'. Finally, it accepts the option `-t'. If this -# is given, completion is only done on words starting with two hyphens. +# are given to `compadd'. -local opt expl group test i name action ret=1 tmp suffix +local opt expl group test i name action ret=1 tmp suffix iopts sopts setopt extendedglob @@ -46,11 +59,23 @@ setopt extendedglob group=() expl=() if [[ $1 = -*~--* ]]; then - while getopts "J:V:X:t" opt; do + while getopts "J:V:X:ti:s:" opt; do case "$opt" in [JV]) group=("-$opt" "$OPTARG");; X) expl=(-X "$OPTARG");; t) test=yes;; + i) if [[ "$OPTARG[1]" = '(' ]]; then + iopts=( ${=OPTARG[2,-2]} ) + else + iopts=( ${(P)${OPTARG}} ) + fi + ;; + s) if [[ "$OPTARG[1]" = '(' ]]; then + sopts=( ${=OPTARG[2,-2]} ) + else + sopts=( ${(P)${OPTARG}} ) + fi + ;; esac done shift OPTIND-1 @@ -77,6 +102,7 @@ if [[ "$tmp" != $_lo_cache_cmd ]]; then (( $+_lo_cache_actions )) && unset "$_lo_cache_names[@]" _lo_cache_actions _lo_cache_names local opts pattern anum=1 tmpo str + typeset -U opts # Now get the long option names by calling the command with `--help'. # The parameter expansion trickery first gets the lines as separate @@ -87,11 +113,24 @@ if [[ "$tmp" != $_lo_cache_cmd ]]; then # 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. Finally all elements with option strings - # that contain uppercase letters are removed. + # option up to the end. + + opts=("--${(@)^${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$("$words[1]" --help)}:#[ ]#-*}//,/ +}}:#[ ]#--*}#*--}%%[, ]*}") - opts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$("$words[1]" --help)}:#[ ]#-*}//,/ -}}:#[ ]#--*}#*--}%%[, ]*}:#(*-[A-Z]*|)}") + # Now remove all ignored options ... + + while (($#iopts)) ; do + opts=( ${opts:#$~iopts[1]} ) + shift iopts + done + + # ... and add "same" options + + while (($#sopts)) ; do + opts=( $opts ${opts/$sopts[1]/$sopts[2]} ) + shift 2 sopts + done # The interpretation of the options is completely table driven. We # use the positional parameters we were given and a few standard @@ -173,7 +212,7 @@ if [[ "$tmp" != $_lo_cache_cmd ]]; then # Now filter out any option strings we don't like and stuff them # in an array, if there are still some. - tmp=("${(@)${(@)tmp%%\=*}//[^a-z0-9-]}") + tmp=("${(@)${(@)tmp%%\=*}//[^a-zA-Z0-9-]}") if (( $#tmp )); then _lo_cache_names[anum]="$name" _lo_cache_actions[anum]="$action" @@ -200,20 +239,6 @@ if [[ "$str" = *\=* ]]; then osuf="$SUFFIX" pre="${str%%\=*}" - IPREFIX="${IPREFIX}${pre}=" - PREFIX="${str#*\=}" - SUFFIX="" - - # We will check if the arrays contain an option matching what's on - # the line. To do this good, we build a pattern. - - [[ -n "$_comp_correct" && $#pre -le _comp_correct ]] && return 1 - - pat="${pre}*" - patflags='' - _match_pattern _long_options pat patflags - - [[ -n "$_comp_correct" ]] && patflags="$patflags(#a$_comp_correct)" # Then we walk through the array names. For each array we test if it # contains the option string. If so, we `invoke' the action stored @@ -223,6 +248,9 @@ if [[ "$str" = *\=* ]]; then for name in "$_lo_cache_names[@]"; do action="$_lo_cache_actions[anum]" if (( ${(@)${(@P)name}[(I)$pre]} )); then + IPREFIX="${oipre}${pre}=" + PREFIX="${str#*\=}" + SUFFIX="" if [[ "$action[1]" = (\[|\() ]]; then compadd - ${=action[2,-2]} elif (( $#action )); then @@ -240,7 +268,10 @@ if [[ "$str" = *\=* ]]; then # element from `_lo_actions' in `parta'. If we find more than one # such option or if we already had one, we set `parto' to `-'. - tmp=("${(@M)${(@P)name}:#${~pat}}") + PREFIX="${str%%\=*}" + SUFFIX="" + compadd -O tmp -M 'r:|-=* r:|=*' - "${(@P)name}" + if [[ $#tmp -eq 1 ]]; then if [[ -z "$parto" ]]; then parto="$tmp[1]" @@ -258,8 +289,9 @@ if [[ "$str" = *\=* ]]; then # try to complete the string after the `='. if [[ -n "$parto" && "$parto" != - ]]; then - IPREFIX="${parto}=" - + IPREFIX="${oipre}${parto}=" + PREFIX="${str#*\=}" + SUFFIX="" if (( $#parta )); then if [[ "$parta[1]" = (\[|\() ]]; then compadd - ${=parta[2,-2]} @@ -281,7 +313,7 @@ fi # The string on the line did not contain a `=', or we couldn't # complete the option string since there were more than one matching -# what's on the line. So we just ad the option string as possible +# what's on the line. So we just add the option strings as possible # matches, giving the string from the `=' on as a suffix. if [[ "$str" = *\=* ]]; then diff --git a/Completion/Base/_math b/Completion/Base/_math index f7f4c360f..90a2c5f7b 100644 --- a/Completion/Base/_math +++ b/Completion/Base/_math @@ -1,4 +1,4 @@ -#defcomp -math- +#compdef -math- if [[ "$PREFIX" = *[^a-zA-Z0-9_]* ]]; then IPREFIX="$IPREFIX${PREFIX%%[a-zA-Z0-9_]#}" @@ -10,7 +10,7 @@ if [[ "$SUFFIX" = *[^a-zA-Z0-9_]* ]]; then fi compgen -v -#defcomp -math- +#compdef -math- IPREFIX="$IPREFIX${PREFIX%[a-zA-Z0-9_]*}" PREFIX="${PREFIX##*[^a-zA-Z0-9_]}" diff --git a/Completion/Base/_parameter b/Completion/Base/_parameter index 2bd66ec93..7200ae440 100644 --- a/Completion/Base/_parameter +++ b/Completion/Base/_parameter @@ -1,3 +1,3 @@ -#defcomp -parameter- +#compdef -parameter- compgen -v diff --git a/Completion/Base/_precommand b/Completion/Base/_precommand index c13cd7465..bcda9fc8b 100644 --- a/Completion/Base/_precommand +++ b/Completion/Base/_precommand @@ -1,4 +1,4 @@ -#defcomp - nohup nice eval time rusage noglob nocorrect exec +#compdef - nohup nice eval time rusage noglob nocorrect exec shift words (( CURRENT-- )) diff --git a/Completion/Base/_redirect b/Completion/Base/_redirect index 32113ad7c..6e02636da 100644 --- a/Completion/Base/_redirect +++ b/Completion/Base/_redirect @@ -1,3 +1,3 @@ -#defcomp -redirect- +#compdef -redirect- _files diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript index e783d04e7..abaabec8c 100644 --- a/Completion/Base/_subscript +++ b/Completion/Base/_subscript @@ -1,4 +1,4 @@ -#defcomp -subscript- +#compdef -subscript- if [[ ${(Pt)${compstate[parameter]}} = assoc* ]]; then compgen -S ']' -k "( ${(kP)${compstate[parameter]}} )" diff --git a/Completion/Base/_tilde b/Completion/Base/_tilde index c5ebb66d5..c24c65701 100644 --- a/Completion/Base/_tilde +++ b/Completion/Base/_tilde @@ -1,4 +1,4 @@ -#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 diff --git a/Completion/Base/_vars b/Completion/Base/_vars index 497a9316f..f06562694 100644 --- a/Completion/Base/_vars +++ b/Completion/Base/_vars @@ -1,3 +1,3 @@ -#defcomp getopts read unset vared +#compdef getopts read unset vared compgen -v diff --git a/Completion/Builtins/_aliases b/Completion/Builtins/_aliases index 2ccf18439..94c8a0f7c 100644 --- a/Completion/Builtins/_aliases +++ b/Completion/Builtins/_aliases @@ -1,3 +1,3 @@ -#defcomp unalias +#compdef unalias compgen -a diff --git a/Completion/Builtins/_arrays b/Completion/Builtins/_arrays index 94401f28a..b79bf711c 100644 --- a/Completion/Builtins/_arrays +++ b/Completion/Builtins/_arrays @@ -1,3 +1,3 @@ -#defcomp shift +#compdef shift compgen -A diff --git a/Completion/Builtins/_autoload b/Completion/Builtins/_autoload index d1462e09a..d1c255b73 100644 --- a/Completion/Builtins/_autoload +++ b/Completion/Builtins/_autoload @@ -1,3 +1,3 @@ -#defcomp autoload +#compdef autoload compgen -s '${^fpath}/*(N:t)' diff --git a/Completion/Builtins/_bg_jobs b/Completion/Builtins/_bg_jobs index 4abee070c..ef1cd4d3c 100644 --- a/Completion/Builtins/_bg_jobs +++ b/Completion/Builtins/_bg_jobs @@ -1,3 +1,3 @@ -#defcomp bg +#compdef bg compgen -z -P '%' diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey index 6fca200ba..57b3d8a85 100644 --- a/Completion/Builtins/_bindkey +++ b/Completion/Builtins/_bindkey @@ -1,4 +1,4 @@ -#defcomp bindkey +#compdef bindkey # Normally, this completes names of zle widgets, whether the builtin ones # or ones defined by the user. Note that a - allows a wildcard before it, diff --git a/Completion/Builtins/_builtin b/Completion/Builtins/_builtin index bee67fe8f..0e6d613be 100644 --- a/Completion/Builtins/_builtin +++ b/Completion/Builtins/_builtin @@ -1,4 +1,4 @@ -#defcomp builtin +#compdef builtin if (( $CURRENT > 2 )); then shift words diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd index a4fd56327..61abd2068 100644 --- a/Completion/Builtins/_cd +++ b/Completion/Builtins/_cd @@ -1,4 +1,4 @@ -#defcomp cd pushd +#compdef cd pushd # Handling of cd. # - Normally just completes directories. Uses cdpath if that's set @@ -55,7 +55,7 @@ elif [[ $words[1] = pu* && $PREFIX = [-+]* ]]; then list=(${list%%[ ]*}) compgen -y '$lines' -Q -k list && ret=0 [[ -z $compstate[list] ]] && compstate[list]=list && ret=0 - [[ -n $compstate[insert] ]] && compstat[insert]=menu && ret=0 + [[ -n $compstate[insert] ]] && compstate[insert]=menu && ret=0 return ret elif [[ $PREFIX != (\~|/|./|../)* && $#cdpath -ne 0 ]]; then diff --git a/Completion/Builtins/_command b/Completion/Builtins/_command index 4facceb12..6103ee81f 100644 --- a/Completion/Builtins/_command +++ b/Completion/Builtins/_command @@ -1,4 +1,4 @@ -#defcomp command +#compdef command if [[ CURRENT -ge 3 ]]; then compset -n 2 diff --git a/Completion/Builtins/_dirs b/Completion/Builtins/_dirs index bc426e322..04c4b75d9 100644 --- a/Completion/Builtins/_dirs +++ b/Completion/Builtins/_dirs @@ -1,3 +1,3 @@ -#defcomp rmdir df du dircmp +#compdef rmdir df du dircmp _files -/ diff --git a/Completion/Builtins/_disable b/Completion/Builtins/_disable index 27db5c18f..5572fbcf3 100644 --- a/Completion/Builtins/_disable +++ b/Completion/Builtins/_disable @@ -1,4 +1,4 @@ -#defcomp disable +#compdef disable local prev="$words[CURRENT-1]" ret=1 diff --git a/Completion/Builtins/_echotc b/Completion/Builtins/_echotc index 135cabada..ce282437d 100644 --- a/Completion/Builtins/_echotc +++ b/Completion/Builtins/_echotc @@ -1,3 +1,3 @@ -#defcomp echotc +#compdef echotc compgen -k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)' diff --git a/Completion/Builtins/_enable b/Completion/Builtins/_enable index 1baa09ed6..483661efe 100644 --- a/Completion/Builtins/_enable +++ b/Completion/Builtins/_enable @@ -1,4 +1,4 @@ -#defcomp enable +#compdef enable local prev="$words[CURRENT-1]" ret=1 diff --git a/Completion/Builtins/_fc b/Completion/Builtins/_fc index f8cf4dde8..5b493dc3b 100644 --- a/Completion/Builtins/_fc +++ b/Completion/Builtins/_fc @@ -1,4 +1,4 @@ -#defcomp fc +#compdef fc local prev="$words[CURRENT-1]" diff --git a/Completion/Builtins/_functions b/Completion/Builtins/_functions index 7e3174af5..052dfca00 100644 --- a/Completion/Builtins/_functions +++ b/Completion/Builtins/_functions @@ -1,3 +1,3 @@ -#defcomp unfunction +#compdef unfunction compgen -F diff --git a/Completion/Builtins/_hash b/Completion/Builtins/_hash index 4d4ba4244..b03cf7fb9 100644 --- a/Completion/Builtins/_hash +++ b/Completion/Builtins/_hash @@ -1,4 +1,4 @@ -#defcomp hash +#compdef hash if [[ "$words[2]" = -*d* ]]; then if compset -P 1 '*\='; then diff --git a/Completion/Builtins/_jobs b/Completion/Builtins/_jobs index 8f6991de0..bce005b4e 100644 --- a/Completion/Builtins/_jobs +++ b/Completion/Builtins/_jobs @@ -1,3 +1,3 @@ -#defcomp fg jobs +#compdef fg jobs compgen -j -P '%' diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill index 0b0f5c188..979c87804 100644 --- a/Completion/Builtins/_kill +++ b/Completion/Builtins/_kill @@ -1,4 +1,4 @@ -#defcomp kill +#compdef kill local list diff --git a/Completion/Builtins/_limits b/Completion/Builtins/_limits index 8b0b41c71..be5c35593 100644 --- a/Completion/Builtins/_limits +++ b/Completion/Builtins/_limits @@ -1,3 +1,3 @@ -#defcomp limit unlimit +#compdef limit unlimit compgen -k "(${(j: :)${(f)$(limit)}%% *})" diff --git a/Completion/Builtins/_sched b/Completion/Builtins/_sched index 5fba71eac..581438b0e 100644 --- a/Completion/Builtins/_sched +++ b/Completion/Builtins/_sched @@ -1,3 +1,3 @@ -#defcomp sched +#compdef sched compset -n 3 && _normal diff --git a/Completion/Builtins/_set b/Completion/Builtins/_set index 959dac75d..59a38b654 100644 --- a/Completion/Builtins/_set +++ b/Completion/Builtins/_set @@ -1,4 +1,4 @@ -#defcomp set +#compdef set local prev="$words[CURRENT-1]" diff --git a/Completion/Builtins/_setopt b/Completion/Builtins/_setopt index e5a6c054f..17ecafb20 100644 --- a/Completion/Builtins/_setopt +++ b/Completion/Builtins/_setopt @@ -1,4 +1,4 @@ -#defcomp setopt +#compdef setopt # If you first want to complete only unset options, un-comment the lines # setting the _unset_options array and then use: diff --git a/Completion/Builtins/_source b/Completion/Builtins/_source index efac4f32b..a232aa458 100644 --- a/Completion/Builtins/_source +++ b/Completion/Builtins/_source @@ -1,4 +1,4 @@ -#defcomp source +#compdef source if [[ CURRENT -ge 3 ]]; then compset -n 2 diff --git a/Completion/Builtins/_trap b/Completion/Builtins/_trap index 36ab1f1a8..bb0630638 100644 --- a/Completion/Builtins/_trap +++ b/Completion/Builtins/_trap @@ -1,4 +1,4 @@ -#defcomp trap +#compdef trap if [[ CURRENT -eq 2 ]]; then compgen -c diff --git a/Completion/Builtins/_unhash b/Completion/Builtins/_unhash index a9050cb49..825c130b2 100644 --- a/Completion/Builtins/_unhash +++ b/Completion/Builtins/_unhash @@ -1,4 +1,4 @@ -#defcomp unhash +#compdef unhash local fl="$words[2]" ret=1 diff --git a/Completion/Builtins/_unsetopt b/Completion/Builtins/_unsetopt index cdc2ab9f3..d862aa0e1 100644 --- a/Completion/Builtins/_unsetopt +++ b/Completion/Builtins/_unsetopt @@ -1,4 +1,4 @@ -#defcomp unsetopt +#compdef unsetopt # If you first want to complete only unset options, uncomment the lines # setting the _set_options array and then use: diff --git a/Completion/Builtins/_vars_eq b/Completion/Builtins/_vars_eq index 9488cb7f9..892c1f57e 100644 --- a/Completion/Builtins/_vars_eq +++ b/Completion/Builtins/_vars_eq @@ -1,3 +1,3 @@ -#defcomp declare export integer local readonly typeset +#compdef declare export integer local readonly typeset compgen -v -q -S '=' diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait index 9281a5cc2..0fadc087b 100644 --- a/Completion/Builtins/_wait +++ b/Completion/Builtins/_wait @@ -1,4 +1,4 @@ -#defcomp wait +#compdef wait local list ret=1 diff --git a/Completion/Builtins/_which b/Completion/Builtins/_which index 9248f9c9f..db5271653 100644 --- a/Completion/Builtins/_which +++ b/Completion/Builtins/_which @@ -1,3 +1,3 @@ -#defcomp which whence where type +#compdef which whence where type compgen -caF diff --git a/Completion/Builtins/_zftp b/Completion/Builtins/_zftp index 178d9d9e3..71404fb2b 100644 --- a/Completion/Builtins/_zftp +++ b/Completion/Builtins/_zftp @@ -1,4 +1,4 @@ -#defpatcomp zf* +#compdef -p zf* # Don't try any more completion after this. _compskip=1 @@ -10,7 +10,7 @@ _compskip=1 local subcom if [[ $words[1] = zftp ]]; then - if [[ $CURRENT -eq 1 ]]; then + if [[ $CURRENT -eq 2 ]]; then compadd open params user login type ascii binary mode put \ putat get getat append appendat ls dir local remote mkdir rmdir return diff --git a/Completion/Builtins/_zle b/Completion/Builtins/_zle index 0a9ad0a9e..f0860c5e3 100644 --- a/Completion/Builtins/_zle +++ b/Completion/Builtins/_zle @@ -1,4 +1,4 @@ -#defcomp zle +#compdef zle if [[ "$words[2]" = -N && CURRENT -eq 3 ]]; then compgen -F diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload index 9247ba0ce..4b0e91442 100644 --- a/Completion/Builtins/_zmodload +++ b/Completion/Builtins/_zmodload @@ -1,4 +1,4 @@ -#defcomp zmodload +#compdef zmodload local fl="$words[2]" diff --git a/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename index 72bac5f1b..53ed6d113 100644 --- a/Completion/Commands/_correct_filename +++ b/Completion/Commands/_correct_filename @@ -1,4 +1,4 @@ -#defkeycomp complete-word \C-xc +#compdef -k complete-word \C-xc # Function to correct a filename. Can be used as a completion widget, # or as a function in its own right, in which case it will print the diff --git a/Completion/Commands/_correct_word b/Completion/Commands/_correct_word new file mode 100644 index 000000000..db3023860 --- /dev/null +++ b/Completion/Commands/_correct_word @@ -0,0 +1,12 @@ +#compdef -k complete-word \C-xc + +# Simple completion front-end implementing spelling correction. +# The maximum number of errors is set quite high, and +# the numeric prefix can be used to specify a different value. + +local oca="$compconfig[correct_accept]" +compconfig[correct_accept]=6n + +_main_complete _correct + +compconfig[correct_accept]=$oca diff --git a/Completion/Commands/_most_recent_file b/Completion/Commands/_most_recent_file index 5bd737fd2..c571483ca 100644 --- a/Completion/Commands/_most_recent_file +++ b/Completion/Commands/_most_recent_file @@ -1,4 +1,4 @@ -#defkeycomp complete-word \C-xm +#compdef -k complete-word \C-xm # Complete the most recent file matching the pattern on the line so # far: globbing is active, i.e. *.txt will be expanded to the most recent diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso index 52fb08f05..61649901e 100644 --- a/Completion/Core/_compalso +++ b/Completion/Core/_compalso @@ -1,15 +1,17 @@ #autoload -# This searches $1 in the array for normal completions and calls the result. +# This searches $* in the array for normal completions and calls the result. # 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. -local tmp ret=1 +local i tmp -tmp="$_comps[$1]" -[[ -z "$tmp" ]] || "$tmp" && ret=0 +for i; do + tmp="$_comps[$1]" + [[ -z "$tmp" ]] || "$tmp" && return 0 +done -return ret +return 1 diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts index ab9438494..b49c41e22 100644 --- a/Completion/Core/_multi_parts +++ b/Completion/Core/_multi_parts @@ -7,15 +7,8 @@ # 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 menu origflags mflags - -_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] +local sep matches pref npref i tmp1 group expl menu pre suf +typeset -U tmp2 # Get the options. @@ -41,193 +34,159 @@ else matches=( "${(@P)2}" ) fi -# Now build the pattern from what we have on the line. We also save -# the original string in `orig'. +# In `pre' and `suf' we will hold the prefix and the suffix from the +# line while we walk through them. The original string are used +# temporarily for matching. -if [[ $#compstate[pattern_match] -ne 0 ]]; then - if [[ "${compstate[pattern_match]-*}" = \** ]]; then - str="${PREFIX}*${SUFFIX}*" - else - str="${PREFIX}${SUFFIX}" - fi -else - patstr="${PREFIX:q}*${SUFFIX:q}*" -fi -orig="${PREFIX}${SUFFIX}" +pre="$PREFIX" +suf="$SUFFIX" +orig="$PREFIX$SUFFIX" -[[ $compstate[insert] = *menu || -n "$_comp_correct" || +# Special handling for menucompletion? + +[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" || ( $#compstate[pattern_match] -ne 0 && "$orig" != "${orig:q}" ) ]] && menu=yes -matchflags="" -_match_pattern _path_files patstr matchflags -origflags="$matchflags" -[[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - -patstr="${${patstr//$sep/*$sep}//\*##/*}" - -# 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. +# In `pref' we collect the unambiguous prefix path. 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'. +# If the string from the line matches at least one of the strings, +# we use only the matching strings. - if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then - mflags="$origflags" - else - mflags="$matchflags" - fi - - pat="${${${patstr#*${sep}}%${sep}*}//\*/[^${sep}]#}" - tmp1=( "${(@M)matches:#${~mflags}${orig%%${sep}*}${sep}${~pat}}" ) +compadd -O tmp1 -M "r:|${sep}=* r:|=*" - "$matches[@]" - # If there are no words matching the exact substring, stop. +(( $#tmp1 )) && matches=( "$tmp1[@]" ) - (( $#tmp1 )) || break +while true; do - # Otherwise add the part to the prefix, remove it from the matches - # (and also remove all words not matching the string at all), and - # set `patstr' and `orig' to the next component. + # Get the prefix and suffix for matching. - tmp1="${orig%%${sep}*}${sep}" - pref="$pref$tmp1" - matches=("${(@)${(@)${(@M)matches:#${tmp1}*}#$tmp1}:#}") - 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 -if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then - mflags="$origflags" -else - mflags="$matchflags" -fi -tmp1=( "${(@M)matches:#${~mflags}${~pat}}" ) - -if (( $#tmp1 )); then - - # There are words that are matched, put them into `matches' and then - # move all unambiguous components from the beginning into `pref'. + if [[ "$pre" = *${sep}* ]]; then + PREFIX="${pre%%${sep}*}" + SUFFIX="" + else + PREFIX="${pre}" + SUFFIX="${suf%%${sep}*}" + fi - matches=( "$tmp1[@]" ) - while [[ "$matches[1]" = *${sep}* ]]; do + # Check if the component for some of the possible matches is equal + # to the string from the line. If there are such strings, we directly + # use the stuff from the line. This avoids having `foo' complete to + # both `foo' and `foobar'. - # 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=( "${(@M)matches:#${PREFIX}${SUFFIX}${sep}*}" ) - tmp1="${matches[1]%%${sep}*}${sep}" - tmp2=( "${(@)matches:#${tmp1}*}" ) - (( $#tmp2 )) && break + if (( $#tmp1 )); then + npref="${PREFIX}${SUFFIX}${sep}" + else + # No exact match, see how many strings match what's on the line. - # All matches have the same prefix, put it into `pref' and remove - # it from the matches. + tmp2=( "${(@)matches%%${sep}*}" ) + compadd -O tmp1 - "$tmp2[@]" - pref="$pref$tmp1" - matches=( "${(@)${(@)matches#$tmp1}:#}" ) + if [[ $#tmp1 -eq 1 ]]; then - if [[ "$orig" = *${sep}* ]]; then - orig="${orig#*${sep}}" - else - orig='' - fi - done + # Only one match. If there are still separators from the line + # we just accept this component. Otherwise we insert what we + # have collected, probably giving it a separator character + # as a suffix. - # 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" -I "$ISUFFIX" -S '' - \ - "${pref}${orig}" - elif [[ -n "$menu" ]]; then - if [[ "$orig" = *${sep}* ]]; then - orig="${sep}${orig#*${sep}}" - else - orig='' - fi - for i in "$matches[@]" ; do - if [[ "$i" = *${sep}* ]]; then - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" -s "$orig" - "${i%%${sep}*}${sep}" + if [[ "$pre$suf" = *${sep}* ]]; then + npref="${tmp1[1]}${sep}" else - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" -s "$orig" - "${i%%${sep}*}" + matches=( "${(@M)matches:#${tmp1[1]}*}" ) + tmp2=( "${(@M)matches:#${tmp1[1]}${sep}*}" ) + + if (( $#tmp2 )); then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" -qS "$sep" - "$tmp1[1]" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" - "$tmp1[1]" + fi + return 1 fi - done - else - for i in "$matches[@]" ; do - if [[ "$i" = *${sep}* ]]; then - compadd -U -i "$IPREFIX" -I "$ISUFFIX" -p "$pref" -s "${i#*${sep}}" \ - "$group[@]" "$expl[@]" -M "r:|${sep:q}=*" - "${i%%${sep}*}${sep}" + elif (( $#tmp1 )); then + + # More than one match. First we get all strings that match the + # rest from the line. + + PREFIX="$pre" + SUFFIX="$suf" + compadd -O matches -M "r:|${sep}=* r:|=*" - "$matches[@]" + + if [[ -n "$menu" ]]; then + # With menucompletion we just add matches for the matching + # components with the prefix we collected and the rest from the + # line as a suffix. + + tmp2="$pre$suf" + if [[ "$tmp2" = *${sep}* ]]; then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" -s "${sep}${tmp2#*${sep}}" - "$tmp1[@]" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -p "$pref" - "$tmp1[@]" + fi else - compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" - "$i" + # With normal completion we add all matches one-by-one with + # the unmatched part as a suffix. This will insert the longest + # unambiguous string for all matching strings. + + for i in "${(@M)matches:#(${(j:|:)~tmp1})*}"; do + if [[ "$i" = *${sep}* ]]; then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' -p "$pref" -s "${i#*${sep}}" - "${i%%${sep}*}${sep}" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' -p "$pref" - "$i" + fi + done 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. - - if [[ -n "$_comp_correct" && "${#orig%%${sep}*}" -le _comp_correct ]]; then - mflags="$origflags" + return 0 else - mflags="$matchflags" - fi - tmp1=( "${(@M)matches:#${~mflags}${~patstr%%${sep}*}${sep}*}" ) - (( $#tmp1 )) || break - - # Then we check if there are words that have a different prefix. + # We are here if no string matched what's on the line. In this + # case we insert the expanded prefix we collected if it differs + # from the original string from the line. - tmp2=( "${(@)tmp1:#${tmp1[1]%%${sep}*}${sep}*}" ) - if (( $#tmp2 )); then + [[ "$orig" = "$pref$pre$suf" ]] && return 1 - # 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" -I "$ISUFFIX" \ - -p "$pref" -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}" + if [[ -n "$suf" ]]; then + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -s "$suf" - "$pref$pre" + else + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' - "$pref$pre$suf" + fi return 0 fi + fi - # All words have the same prefix, so add it to `pref' again and - # try the next component. + # We just accepted and/or expanded a component from the line. We + # remove it from the matches (using only those that have a least + # the skipped string) and ad it the `pref'. - pref="$pref${tmp1[1]%%${sep}*}${sep}" - matches=( "${(@)matches#${tmp1[1]%%${sep}*}${sep}}" ) - orig="${orig#*${sep}}" - patstr="${patstr#*${sep}}" - done + matches=( "${(@)${(@)${(@M)matches:#${npref}*}#*${sep}}:#}" ) + pref="$pref$npref" - # Finally, add the unambiguous prefix and the rest of the string - # from the line. + # Now we set `pre' and `suf' to their new values. - compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$pref" - "$orig" -fi + if [[ "$pre" = *${sep}* ]]; then + pre="${pre#*${sep}}" + elif [[ "$suf" = *${sep}* ]]; then + pre="${suf#*${sep}}" + suf="" + else + # The string from the line is fully handled. If we collected an + # unambiguous prefix and that differs from the original string, + # we insert it. -# This sets the return value to indicate that we added matches (or not). + [[ -n "$pref" && "$orig" != "$pref" ]] && + compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + -S '' - "$pref" -[[ nm -ne compstate[nmatches] ]] + return + fi +done diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files index 54b04a368..21837ade2 100644 --- a/Completion/Core/_path_files +++ b/Completion/Core/_path_files @@ -7,10 +7,9 @@ # the last have the same syntax and meaning as for `compgen' or # `compadd', respectively. 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 are treated -# like files with one of the suffixes in the `fignore' array in normal -# completion. +# literally by giving them in a string surrounded by parentheses. Files +# with one of the suffixes thus given are treated like files with one +# of the suffixes in the `fignore' array in normal completion. # # This function supports one configuration key: # @@ -18,22 +17,13 @@ # If this is set to a non-empty string, the partially typed path # from the line will be expanded as far as possible even if trailing # pathname components can not be completed. -# -# -# This function uses the helper functions `_match_test' and `_match_pattern'. - -# First see if we should generate matches for the global matcher in use. - -_match_test _path_files || return 1 -# Yes, so... +local linepath realpath donepath prepath testpath exppath +local tmp1 tmp2 tmp3 tmp4 i orig pre suf tpre tsuf +local pats ignore group expl addpfx addsfx remsfx +local nm=$compstate[nmatches] menu -local nm 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 orig ostr nm=$compstate[nmatches] menu remsfx patlast -local origflags mflags tmp3 tmp4 exppaths - -typeset -U prepaths +typeset -U prepaths exppaths setopt localoptions nullglob rcexpandparam extendedglob unsetopt markdirs globsubst shwordsplit nounset @@ -109,35 +99,27 @@ if [[ "$sopt" = - ]]; then fi fi -# str holds the whole string from the command line with a `*' between -# the prefix and the suffix. Then we see if we will do menucompletion. +# We get the prefix and the suffix from the line and save the whole +# original string. Then we see if we will do menucompletion. -if [[ $#compstate[pattern_match] -ne 0 ]]; then - if [[ "${compstate[pattern_match]-*}" = \** ]]; then - str="${PREFIX}*${SUFFIX}" - else - str="${PREFIX}${SUFFIX}" - fi -else - str="${PREFIX:q}*${SUFFIX:q}" - [[ "$str" = \\\~* ]] && str="$str[2,-1]" -fi +pre="$PREFIX" +suf="$SUFFIX" orig="${PREFIX}${SUFFIX}" -[[ $compstate[insert] = *menu || -n "$_comp_correct" || +[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" || ( $#compstate[pattern_match] -ne 0 && "${orig#\~}" != "${${orig#\~}:q}" ) ]] && menu=yes - # We will first try normal completion called with `compgen', but only if we -# weren't given a `-F', `-r', or `-R' option. +# weren't given a `-F', `-r', or `-R' option or we are in the string. -if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then +if [[ -z "$suf" && $#ignore -eq 0 && $#remsfx -eq 0 && + -z "$_comp_correct" ]]; then # First build an array containing the `-W' option, if there is any and we # want to use it. We don't want to use it if the string from the command line # is a absolute path or relative to the current directory. - if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then + if [[ -z "$prepaths[1]" || "$pre[1]" = [~/] || "$pre" = (.|..)/* ]]; then tmp1=() else tmp1=(-W "( $prepaths )") @@ -145,7 +127,6 @@ if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then # Now call compgen. - nm=$compstate[nmatches] if [[ -z "$gopt" ]]; then compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" "$tmp1[@]" $sopt else @@ -157,13 +138,13 @@ if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then [[ compstate[nmatches] -eq nm ]] || return 0 fi -# No `-F' option, so we want to use `fignore'. +# If given no `-F' option, we want to use `fignore'. (( $#ignore )) || ignore=(-F fignore) # Now let's have a closer look at the string to complete. -if [[ "$str[1]" = \~ ]]; then +if [[ "$pre[1]" = \~ ]]; then # It begins with `~', so remember anything before the first slash to be able # to report it to the completion code. Also get an expanded version of it # (in `realpath'), so that we can generate the matches. Then remove that @@ -171,10 +152,10 @@ if [[ "$str[1]" = \~ ]]; then # paths and make sure that the loop below is run only once with an empty # prefix path by setting `prepaths'. - linepath="${str%%/*}/" + linepath="${pre%%/*}/" eval realpath\=$linepath [[ "$realpath" = "$linepath" ]] && return 1 - str="${str#*/}" + pre="${pre#*/}" orig="${orig#*/}" donepath='' prepaths=( '' ) @@ -185,12 +166,12 @@ else linepath='' realpath='' - if [[ "$str[1]" = / ]]; then + if [[ "$pre[1]" = / ]]; then # If it is a absolut path name, we remove the first slash and put it in # `donepath' meaning that we treat it as the path that was already handled. # Also, we don't use the paths from `-W'. - str="$str[2,-1]" + pre="$pre[2,-1]" orig="$orig[2,-1]" donepath='/' prepaths=( '' ) @@ -198,278 +179,204 @@ else # The common case, we just use the string as it is, unless it begins with # `./' or `../' in which case we don't use the paths from `-W'. - [[ "$str" = (.|..)/* ]] && prepaths=( '' ) + [[ "$pre" = (.|..)/* ]] && prepaths=( '' ) donepath='' fi fi -# Now build the glob pattern by calling `_match_pattern'. -patstr="$str" -matchflags="" -_match_pattern _path_files patstr matchflags -origflags="$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-.*/-./-" - -# We take the last pathname component from the pattern and store it in -# `patlast', replacing `*'s in it with patterns that match any character -# but not slashes. Later we will generate matches using `patstr' with the -# patterns we were given (like `*.c') appended to it, producing all matching -# files. These filenames are then compared to `patlast' and all names not -# matching that will be removed. All this is needed to be able to correctly -# support `completeinword' as otherwise we would have something like `a*x' -# from the line (the `*' was inserted above) and appending the `-g' pattern -# `*.tex' would yield `a*x*.tex' which is not what we want. - -if [[ "$patstr" = */* ]]; then - if [[ -n "$_comp_correct" && "${#orig##*/}" -le _comp_correct ]]; then - patlast="*/${origflags}${${${patstr##*/}//\*/[^/]#}:gs.\[^/]#.\\\*.}" - else - patlast="*/${matchflags}${${${patstr##*/}//\*/[^/]#}:gs.\[^/]#.\\\*.}" - fi - patstr="${patstr%/*}/" -else - if [[ -n "$_comp_correct" && "$#orig" -le _comp_correct ]]; then - patlast="${origflags}${${patstr//\*/[^/]#}:gs.\[^/]#.\\\*.}" - else - patlast="${matchflags}${${patstr//\*/[^/]#}:gs.\[^/]#.\\\*.}" - fi - patstr="" -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 `orig' and added -# to `donepath'. +# Now we generate the matches. First we loop over all prefix paths given +# with the `-W' option. -while [[ "$orig" = */* ]] do - tmp1=( $realpath$donepath${orig%%/*}/${~matchflags}${~patstr#*/}${^~pats} ) - tmp1=("${(@M)tmp1:#$~patlast}") - [[ $#tmp1 -gt 0 && -e "$realpath$donepath${orig%%/*}" ]] || break - donepath="$donepath${orig%%/*}/" - orig="${orig#*/}" - patstr="${patstr#*/}" -done +for prepath in "$prepaths[@]"; do -# 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' -# and `testpath' is a modifyable version of `donepath'. + # Get local copies of the prefix, suffix, and the prefix path to use + # in the following loop, which walks through the pathname components + # in the string from the line. -for prepath in "$prepaths[@]"; do - str="$patstr" + tpre="$pre" + tsuf="$suf" testpath="$donepath" - ostr="$orig" - [[ -z "$prepath" || "$prepath[-1]" = / ]] || prepath="${prepath}/" + tmp1=( "$prepath$realpath$donepath" ) - # The second loop tests the components of the path in `str' to get the - # possible matches. + while true; do - while [[ "$str" = */* ]] do - # `rest' is the pathname after the first slash that is left. In `tmp1' - # we get the globbing matches for the pathname component currently - # handled. + # Skip over `./' and `../'. + + if [[ "$tpre" = (.|..)/* ]]; then + tmp1=( ${^tmp1}${tpre%%/*}/ ) + tpre="${tpre#*/}" + continue + fi - if [[ -n "$_comp_correct" && "${#ostr%%/*}" -le _comp_correct ]]; then - mflags="$origflags" + # Get the prefix and suffix for matching. + + if [[ "$tpre" = */* ]]; then + PREFIX="${tpre%%/*}" + SUFFIX="" else - mflags="$matchflags" + PREFIX="${tpre}" + SUFFIX="${tsuf%%/*}" fi - rest="${str#*/}" - tmp1="${prepath}${realpath}${testpath}${~mflags}${str%%/*}(-/)" - tmp1=( $~tmp1 ) - if [[ $#tmp1 -eq 0 ]]; then - # If this didn't produce any matches, we don't need to test this path - # any further, so continue with the next `-W' path, if any. + # Get the matching files by globbing. - continue 2 - elif [[ $#tmp1 -gt 1 ]]; then - # If it produced more than one match, we want to remove those which - # don't have possible following pathname components matching the - # rest of the string we are completing. (The case with only one - # match is handled below.) - # In `collect' we will collect those of the produced pathnames that - # have a matching possible path-suffix. In `suffixes' we build an - # array containing strings build from the rest of the string to - # complete and the glob patterns we were given as arguments. - - collect=() - suffixes=( $rest$^pats ) - suffixes=( "${(@)suffixes:gs.**.*.}" ) - - if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then - mflags="$origflags" - else - mflags="$matchflags" - fi + if [[ "$tpre$tsuf" = */* ]]; then + tmp1=( ${^tmp1}*(D/) ) + else + tmp1=( ${^tmp1}${^~pats} ) + fi + + if [[ -n "$PREFIX$SUFFIX" ]]; then + # See which of them match what's on the line. - # In the loop the prefixes from the `tmp1' array produced above and - # the suffixes we just built are used to produce possible matches - # via globbing. - - for i in "$tmp1[@]" ; do - tmp2=( ${~i}/${~mflags}${~suffixes} ) - tmp2=("${(@M)tmp2:#$~patlast}") - [[ $#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 add the - # matches found in `tmp1' and otherwise give up and continue with the - # next `-W' path. - - if [[ $#collect -eq 0 ]]; then - # Before giving, we first try to get the longest expandable path- - # prefix, though. The result is stored in `exppaths' - - tmp2=() - tmp3="$rest" - tmp4="${ostr##*/}" - ostr="${ostr%/*}" - while [[ "$tmp3" = */* ]]; do - tmp2=( ${^tmp1}/${~mflags}${~tmp3} ) - if [[ $#tmp2 -eq 1 ]]; then - exppaths=( "$exppaths[@]" "${tmp2[1]}${tmp4}" ) - exppaths=( "${(@)exppaths#${prepath}${realpath}}" ) - break; - fi - tmp3="${tmp3%/*}" - tmp4="${ostr##*/}/${tmp4}" - ostr="${ostr%/*}" - done + compadd -O tmp2 "$ignore[@]" - "${(@)tmp1##*/}" + + # If no file matches, save the expanded path and continue with + # the outer loop. + + if [[ $#tmp2 -eq 0 ]]; then + [[ "$tmp1[1]" = */* ]] && + exppaths=( "$exppaths[@]" ${^tmp1%/*}/${tpre}${tsuf} ) continue 2 - elif [[ $#collect -ne 1 ]]; then - # If we have more than one possible match, this means that the - # pathname component currently handled is ambiguous, so we give - # it to the completion code. - # First we build the full path prefix in `tmp1'. + fi - tmp1="$prepath$realpath$testpath" + # Remove all files that weren't matched. - # Now produce all matching pathnames in `collect'. + if [[ "$tmp1[1]" = */* ]]; then + tmp1=( "${(@M)tmp1:#*/(${(j:|:)~${(@)tmp2:q}})}" ) + else + tmp1=( "${(@M)tmp1:#(${(j:|:)~${(@)tmp2:q}})}" ) + fi + elif (( ! $#tmp1 )); then + [[ "$tmp1[1]" = */* ]] && + exppaths=( "$exppaths[@]" ${^tmp1%/*}/${tpre}${tsuf} ) + continue 2 + fi - collect=( ${~collect}/${~matchflags}${~suffixes} ) - collect=("${(@M)collect:#$~patlast}") + # Step over to the next component, if any. - # And then remove the common path prefix from all these matches. + if [[ "$tpre" = */* ]]; then + tpre="${tpre#*/}" + elif [[ "$tsuf" = */* ]]; then + tpre="${tsuf#*/}" + tsuf="" + else + break + fi - collect=( ${collect#$tmp1} ) + # There are more components, so add a slash to the files we are + # collecting. - # Finally, we add all these matches with the common (unexpanded) - # pathprefix (the `-p' option), the path-prefix (the `-W' option) - # to allow the completion code to test file type, and the path- - # suffix (the `-s' option). We also tell the completion code that - # these are file names and that `fignore' should be used as usual - # (the `-f' and `-F' options). + tmp1=( ${^tmp1}/ ) + done - if [[ -n "$menu" ]]; then - compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" -p "$linepath${testpath:q}" \ - -s "/${ostr#*/}" \ - -W "$tmp1" -f "$ignore[@]" - "${(@)${(@)collect%%/*}:q}" - else - for i in $collect; do - compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \ - -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${${i%%/*}:q}" - done - fi + # The next loop searches the first ambiguous component. - # We have just finished handling all the matches from above, so we - # can continue with the next `-W' path. + tmp3="$pre$suf" + tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" ) - continue 2 - fi + while true; do - # We reach this point if only one of the path prefixes in `tmp1' - # has a existing path-suffix matching the string from the line. - # In this case we accept this match and continue with the next - # path-name component. + # First we check if some of the files match the original string + # for this component. If there are some we remove all other + # names. This avoid having `foo' complete to `foo' and `foobar'. - tmp1=( "$collect[1]" ) - elif [[ -n "$_comp_correct" && "$mflags" = "$matchflags" ]]; then + if [[ "$tmp3" = */* ]]; then + tmp4=( "${(@M)tmp1:#${tmp3%%/*}/*}" ) + if (( $#tmp4 )); then + tmp1=( "$tmp4[@]" ) + fi + fi - # If we got only one match with auto-correction and if we get none - # without correction, stop now. + # Next we see if this component is ambiguous. - tmp2="${prepath}${realpath}${testpath}${~origflags}${str%%/*}(-/)" - tmp2=( $~tmp2 ) + if [[ "$tmp3" = */* ]]; then + tmp4=( "${(@)tmp1:#${tmp1[1]%%/*}/*}" ) + else + tmp4=( "${(@)tmp1:#${tmp1[1]}}" ) + fi - if [[ $#tmp1 -ne $#tmp2 ]]; then - compadd -QU "$addpfx[@]" -S '' "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$linepath${testpath:q}" -s "/${ostr#*/}" \ - - "${${tmp1#${prepath}${realpath}${testpath}}:q}" - continue 2 + if (( $#tmp4 )); then + + # It is. For menucompletion we now add the possible completions + # for this component with the unambigous prefix we have built + # and the rest of the string from the line as the suffix. + # For normal completion we add the rests of the filenames + # collected as the suffixes to make the completion code expand + # it as far as possible. + + if [[ -n $menu ]]; then + if [[ "$tmp3" = */* ]]; then + compadd -Uf -p "$linepath$testpath" -s "/${tmp3#*/}" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "${(@)tmp1%%/*}" + else + compadd -Uf -p "$linepath$testpath" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "$tmp1[@]" + fi + else + if [[ "$tmp3" = */* ]]; then + for i in "$tmp1[@]"; do + compadd -Uf -p "$linepath$testpath" -s "/${i#*/}" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "${i%%/*}" + done + else + compadd -Uf -p "$linepath$testpath" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "$tmp1[@]" + fi fi + tmp4=- + break fi - # This is also reached if the first globbing produced only one match - # in this case we just continue with the next pathname component, too. - tmp1="$tmp1[1]" - testpath="$testpath${tmp1##*/}/" - str="$rest" - ostr="${ostr#*/}" - done + # If we have checked all components, we stop now and add the + # strings collected after the loop. - # We are here if all pathname components except the last one (which is still - # not tested) are unambiguous. So we add matches with the full path prefix, - # no path suffix, the `-W' we are currently handling, all the matches we - # can produce in this directory, if any. + if [[ "$tmp3" != */* ]]; then + tmp4="" + break + fi - if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then - mflags="$origflags" - else - mflags="$matchflags" - fi - tmp1="$prepath$realpath$testpath" - suffixes=( $str$^pats ) - suffixes=( "${(@)suffixes:gs.**.*.}" ) - tmp2=( ${~tmp1}${~matchflags}${~suffixes} ) - if [[ "$tmp1" = */* && "$patlast" != \*/* ]]; then - tmp2=("${(@M)tmp2:#*${~patlast}}") - else - tmp2=("${(@M)tmp2:#$~patlast}") - fi - if [[ $#tmp2 -eq 0 ]]; then - # No match, insert the expanded path and add the original tail. + # Otherwise we add the unambiguous component to `testpath' and + # take it from the filenames. - [[ "$testpath[-1]" = / ]] && testpath="$testpath[1,-2]" - [[ -z "$testpath" && "$linepath[-1]" = / ]] && linepath="$linepath[1,-2]" - [[ -n "$ostr" && -n "$linepath$testpath" ]] && ostr="/$ostr" + testpath="${testpath}${tmp1[1]%%/*}/" + tmp1=( "${(@)tmp1#*/}" ) - # But only if something changed. - [[ "${PREFIX}${SUFFIX}" = $linepath$testpath$ostr(|/) ]] && return 1 + tmp3="${tmp3#*/}" + done - compadd -QU -S '' "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" -f - "$linepath${testpath:q}$ostr" - else - compadd -QU "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" "$group[@]" "$expl[@]" \ - -i "$IPREFIX" -I "$ISUFFIX" \ - -p "$linepath${testpath:q}" -f "$ignore[@]" \ - -W "$prepath$realpath$testpath" - "${(@)${(@)tmp2#$tmp1}:q}" + if [[ -z "$tmp4" ]]; then + compadd -Uf -p "$linepath$testpath" \ + -W "$prepath$realpath$testpath" "$ignore[@]" \ + "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ + "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \ + - "$tmp1[@]" fi done -# If no matches were found but we have expanded paths which are different -# from the original string, use them. +# If we are configured to expand paths as far as possible and we collected +# expanded paths that are different from the string on the line, we add +# them as possible matches. exppaths=( "${(@)exppaths:#$orig}" ) if [[ -n "$compconfig[path_expand]" && - nm -eq compstate[nmatches] && $#exppaths -ne 0 ]]; then - compadd -UQ -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" - \ - "${linepath}${(@)^exppaths}" + $#exppaths -ne 0 && nm -eq compstate[nmatches] ]]; then + compadd -U -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" - \ + "${(@)exppaths}" fi -# This sets the return value to indicate that we added matches (or not). - -[[ nm -ne compstate[nmatches] ]] +[[ nm -eq compstate[nmatches] ]] diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts index fc927163b..b906a3042 100644 --- a/Completion/Core/_sep_parts +++ b/Completion/Core/_sep_parts @@ -9,24 +9,17 @@ # # _sep_parts '(foo bar)' @ hosts # -# This will make this function complete the strings in the array -# `friends'. If the string on the line contains a `@', the substring -# after it will be completed from the array `hosts'. Of course more -# arrays may be given, each preceded by another separator string. +# This will make this function complete the strings `foo' and `bar' +# If the string on the line contains a `@', the substring after it +# will be completed from the array `hosts'. Of course more arrays +# may be given, each preceded by another separator string. # # This function understands the `-J group', `-V group', and # `-X explanation' options. -# -# This function does part of the matching itself and calls the functions -# `_match_test' and `_match_pattern' for this. local str arr sep test testarr tmparr prefix suffixes matchers autosuffix local matchflags opt group expl nm=$compstate[nmatches] -# Test if we should use this function for the global matcher in use. - -_match_test _sep_parts || return 1 - # Get the options. group=() @@ -42,7 +35,7 @@ shift OPTIND-1 # Get the string from the line. str="$PREFIX$SUFFIX" -[[ $#compstate[pattern_match] -ne 0 ]] || str="$str:q" +SUFFIX="" prefix="" # Walk through the arguments to find the longest unambiguous prefix. @@ -56,61 +49,49 @@ while [[ $# -gt 1 ]]; do tmparr=( ${=arr[2,-2]} ) arr=tmparr fi - # Is the separator on the line? - [[ "$str" != *${sep}* ]] && break - # Build a pattern matching the possible matches and get all these - # matches in an array. + # Is the separator on the line? - test="${str%%${sep}*}" - [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 + [[ "$str" != *${sep}* ]] && break - matchflags="" - _match_pattern _sep_parts test matchflags - [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" + # Get the matching array elements. - test="${matchflags}${test}" - testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) - testarr=( "${(@)testarr:#}" ) + PREFIX="${str%%${sep}*}" + compadd -O testarr - "${(@P)arr}" # If there are no matches we give up. If there is more than one # match, this is the part we will complete. + (( $#testarr )) || return 1 [[ $#testarr -gt 1 ]] && break # Only one match, add it to the prefix and skip over it in `str', # continuing with the next array and separator. + prefix="${prefix}${testarr[1]}${sep}" str="${str#*${sep}}" shift 2 done # Get the array to work upon. + arr="$1" if [[ "$arr[1]" == '(' ]]; then tmparr=( ${=arr[2,-2]} ) arr=tmparr fi + if [[ $# -le 1 || "$str" != *${2}* ]]; then # No more separators, build the matches. - test="$str" - [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 - - matchflags="" - _match_pattern _sep_parts test matchflags - [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - - [[ "${compstate[pattern_match]-*}" != \** ]] && test="$test:gs/*//" - - test="${matchflags}${test}" - testarr=( "${(@M)${(@P)arr}:#${~test}*}" ) - testarr=( "${(@)testarr:#}" ) + PREFIX="$str" + compadd -O testarr - "${(@P)arr}" fi [[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1 # Now we build the suffixes to give to the completion code. + shift matchers=() suffixes=("") @@ -118,26 +99,18 @@ autosuffix=() while [[ $# -gt 0 && "$str" == *${1}* ]]; do # Remove anything up to the the suffix. + str="${str#*${1}}" # Again, we get the string from the line up to the next separator # and build a pattern from it. + if [[ $# -gt 2 ]]; then - test="${str%%${3}*}" + PREFIX="${str%%${3}*}" else - test="$str" + PREFIX="$str" fi - [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1 - - matchflags="" - _match_pattern _sep_parts test matchflags - [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)" - - [[ "${compstate[pattern_match]-*}" != \** ]] && test="$test:gs/*//" - - test="${matchflags}${test}" - # We incrementally add suffixes by appending to them the seperators # and the strings from the next array that match the pattern we built. @@ -146,26 +119,31 @@ while [[ $# -gt 0 && "$str" == *${1}* ]]; do tmparr=( ${=arr[2,-2]} ) arr=tmparr fi - tmparr=( "${(@M)${(@P)arr}:#${~test}*}" ) - tmparr=( "${(@)tmparr:#}" ) + + compadd -O tmparr - "${(@P)arr}" + 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 # word matching before the separators on the fly. + matchers=("$matchers[@]" "r:|${1:q}=*") shift 2 done # If we were given at least one more separator we make the completion # code offer it by appending it as a autoremovable suffix. + (( $# )) && autosuffix=(-qS "$1") # If we have collected matching specifications, we build an array # from it that can be used as arguments to `compadd'. + [[ $#matchers -gt 0 ]] && matchers=(-M "$matchers") # Add the matches for each of the suffixes. + for i in "$suffixes[@]"; do compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \ -i "$IPREFIX" -I "$ISUFFIX" -p "$prefix" -s "$i" - "$testarr[@]" diff --git a/Completion/Core/compinit b/Completion/Core/compinit index 19a0fd1ad..20b1262ec 100644 --- a/Completion/Core/compinit +++ b/Completion/Core/compinit @@ -1,25 +1,23 @@ # Initialisation for new style completion. This mainly contains some helper -# function and aliases. Everything else is split into different files in this -# directory that will automatically be made autoloaded (see the end of this -# file). +# functions and aliases. Everything else is split into different files that +# will automatically be made autoloaded (see the end of this file). # The names of the files that will be considered for autoloading have to -# start with a underscores (like `_setopt). +# start with an underscores (like `_setopt). # The first line of these files will be read and has to say what should be # done with its contents: # -# `#defcomp <names ...>' -# if the first line looks like this, the file is -# autoloaded as a function and that function will -# be called to generate the matches when completing -# for one of the commands whose <name> is given +# `#compdef <names ...>' +# If the first line looks like this, the file is autoloaded as a +# function and that function will be called to generate the matches +# when completing for one of the commands whose <names> are given. # -# `#defpatcomp <pattern>' -# this defines a function that should be called to generate -# matches for commands whose name matches <pattern>; note -# that only one pattern may be given +# `#compdef -p <pattern>' +# This defines a function that should be called to generate matches +# for commands whose name matches <pattern>. Note that only one pattern +# may be given. # -# `#defkeycomp <style> [ <key-sequence> ... ] -# this is used to bind special completions to all the given +# `#compdef -k <style> [ <key-sequence> ... ] +# This is used to bind special completions to all the given # <key-sequence>(s). The <style> is the name of one of the built-in # completion widgets (complete-word, delete-char-or-list, # expand-or-complete, expand-or-complete-prefix, list-choices, @@ -41,11 +39,6 @@ # were able to add matches and non-zero otherwise. # # See the file `compdump' for how to speed up initialisation. -# -# If you are using global matching specifications with `compctl -M ...' -# have a look at the files `_match_test' and `_match_pattern'. To make -# all the example functions use matching as specified with `-M' these -# need some editing. # If we got the `-d'-flag, we will automatically dump the new state (at # the end). @@ -220,17 +213,34 @@ compdef() { # and sets the according values in `compconfig'. # Arguments may be `foo=bar' to set key `foo' to `bar' or `baz' to # set key `baz' to the empty string. +# If no arguments are given, all configurations keys set are displayed. +# With the option `-l' as the first argument, the other arguments are +# taken to be key names and the values for theses keys are printed, one +# per line. compconf() { local i - for i; do - if [[ "$i" = *\=* ]]; then - compconfig[${i%%\=*}]="${i#*\=}" + if (( $# )); then + if [[ "$1" = -l ]]; then + shift + for i; do + print $compconfig[$i] + done else - compconfig[$i]='' + for i; do + if [[ "$i" = *\=* ]]; then + compconfig[${i%%\=*}]="${i#*\=}" + else + compconfig[$i]='' + fi + done fi - done + else + for i in ${(k)compconfig}; do + print ${(r:25:)i} $compconfig[$i] + done + fi } # Now we automatically make the definition files autoloaded. @@ -262,12 +272,12 @@ if [[ -z "$_i_done" ]]; then read -rA _i_line < $_i_file _i_tag=$_i_line[1] shift _i_line - if [[ $_i_tag = '#defcomp' ]]; then - compdef -na "${_i_file:t}" "${_i_line[@]}" - elif [[ $_i_tag = '#defpatcomp' ]]; then - compdef -pa "${_i_file:t}" "${_i_line[@]}" - elif [[ $_i_tag = '#defkeycomp' ]]; then - compdef -ka "${_i_file:t}" "${_i_line[@]}" + if [[ $_i_tag = '#compdef' ]]; then + if [[ $_i_line[1] = -[pk] ]]; then + compdef ${_i_line[1]}a "${_i_file:t}" "${(@)_i_line[2,-1]}" + else + compdef -na "${_i_file:t}" "${_i_line[@]}" + fi elif [[ $_i_tag = '#autoload' ]]; then autoload ${_i_file:t} fi diff --git a/Completion/README b/Completion/README index bf677066a..dbfb50534 100644 --- a/Completion/README +++ b/Completion/README @@ -1,356 +1,3 @@ -The subdirectories contain code for the new function-based completion -system. Broadly speaking, this uses shell functions defined for each -command to determine how the arguments of a command should be completed. - -You should copy all the files you need or want to a directory of your own, -which should be included in your autoload path as defined by $fpath. Then -in your .zshrc you should source the file which appears here in -Core/compinit. It is recommnded that you use the -d option, which outputs -a file containing the necessary variables, bindkeys etc., making later -loading much faster. For example, - [[ -f ~/completion/compinit ]] && . ~/completion/compinit -d -The name of the file to use may be given as an extra argument. - -This will rebind any keys which do completion to use the new system. -For more detailed instructions, including how to add new completions, see -the top of Core/compinit. For information about how to configure the code, -see the section below. - -The subdirectories contain: - -Core: - The basic functions and files to be sourced. You will certainly need - these, and will most likely not want to alter them --- if you do, it - would probably help to give your version a different name. The files - are: - compinit - As already described, this is not a function, but is sourced once - (with the `source' or `.' commands) to set up the completion system. - compdump - This dumps the completions status for faster initialisation. The - easiest way of doing this is to use the -d option to compinit rather - than calling compdump directly. - _approximate - A completer function that does correcting completion. - _compalso - Utility for calling a function to add additional completions to an - already existing set. - _complete - The main completer function that generates the completions by calling - the context and command specific functions. - _correct - A completer function that attempts correction on the word from the - line. Unlike _approximate this does only correction, not completion. - _expand - A completer function for expanding the word on the line. - _files - A frontend to _path_files which will default to any old file if the - specified file was not found. - _list - A completer function that allows showing only a list on the first - TAB and insert completions on the second one. - _match - A completer function that temporarily swicthes on pattern matching - when comparing the string from the line with possible completions. - _main_complete - The main entry point called by the key bindings which compinit sets - up (the main `completion widget' in zsh jargon). This mainly calls - completer functions like _complete, either those given as arguments - or (if it is called without arguments) those from the completer - configuration key (see below). - _multi_parts - Utility for completion parts of words given a separator character and - a list of words. - _normal - The function called by _main_complete to handle the most common - cases, such as completing a command name or its arguments. This - function dispatches to the various other functions for individual - commands. (Actually, the system is fairly context-sensitive, so - it is wider than just command+argument.) - _options - Utility to complete option names, allowing the optional `no' prefix - and correctly handling upper case letters and underscores. - _parameters - This can be used to complete parameter names if you need some of the - options of compadd not supported by compgen. - _path_files - The function usually called to complete filenames and directories. It - replaces the standard -f, -g and -/ options for the basic completion - commands: it can do various extra tricks, such as expanding a whole - path at once, e.g. F/C/C/_p<TAB> -> Functions/Completion/Core/_path_files - _sep_parts - Utility used for completing words with multiple separate parts, such as - `<user>@<host>' - _set_options - _unset_options - These can be used to complete only set or unset options. For this to - work, you'll have to un-comment a few lines in _main_complete. -Base: - You will almost certainly want these files, too, which handle standard - tasks like completing files. However, you may want to edit them for - your own particular setup. Files are: - _command_names - This handles completion of the command word, i.e. the first thing - on the command line. You may want to alter this, for example, - to complete parameters to assign to. - _condition - This handles completing inside [[ ... ]] . - _default - This handles completion of command arguments when no special function - exists. Usually this means completing files, but you can modify this - as you wish. - _long_options - This handles options beginning with `--', as in many GNU commands. - The command must accept the --help option to list the possible options. - __long_options can also take arguments to help it decide what to - complete as the value of the option. Note that this is potentially - dangerous because the command from the line will be called with the - --help option and hence could cause damage if used with a command - that does not support it. - _match_pattern - _match_test - These are used by Base/_path_files (and hence also Base/_files) - and Base/_sep_parts for file completion with control over - matching (whether to complete case-insensitively, or to allow - insertion before `.', etc.) See _match_test for instructions. - Note _path_files expects these files to be present. - _precommand - Allows completion when the first word on the line has to be ignored, - for example `noglob ...' should ignore the noglob and just complete - as if it wasn't there. Add other such commands to the top line. - _redirect - Completes after `<' or `>': this version calls _files. - _subscript - For completion in subscripts of parameters, e.g $foo[...]. - _vars - Completion for commands which need variables (so this could also be in - the Builtins directory), but also in math environments such as ((...)). - _tilde - Completion after `~', defaults to user names and named directories. - _equal - Completion after `=', normally command and alias names are used. - _parameter - _brace_parameter - For completion inside parameter expansions ($... and ${...). -Builtins: - Define completions for various shell builtins. The top line of each file - says which builtins they apply to; in many cases you can guess from the - name. Note in particular that _zftp defines completions for all commands - beginning `zf', not just for the module command zftp. This is only - really useful if you use zftp with the zf* function suite (zfopen, zfget, - ...). -User: - This contains a pot pourri of completions for various external commands. - Not all will work unmodified on your system. -Commands: - These functions define separate completion commands which do not use - the usual context information, and hence have to be bound separately - to keys. As they appear, they have bindings which you can change or - delete by altering the top line of the file. To bind a function - (strictly speaking, the corresponding completion widget) yourself - after completion is loaded, use `bindkey '<key-string>' <_function_name>'. - The files are: - _correct_filename, bound to \C-xc - Correct the word under the cursor as a filename. This is significantly - more powerful than the standard \e$ (spell-word) binding. - _most_recent_file, bound to \C-xm - Insert the name of the most recent file matching the pattern - so far on the command line. - - -Configuration - -You can configure several aspects of the completion functions and the -overall completion behavior using the compconf shell function defined -in compinit. This function gets any number of arguments of the form -`key=value', where `key' is one of the keys described below and `value' -is any string. Most configuration keys are defined by the completer -functions. - -The completion widget function _main_complete currently understands -one configuration key: - - completer - This should be set to the names of the functions to generate the - matches separated by colons. E.g. with - - compconf completer=_complete:_correct:_approximate - - the code will first try normal completion. If that doesn't yield - any matches, correction is tried and if that doesn't yield - anything either, correcting completion is attempted. - -The keys understood by the _approximate completer function are: - - approximate_accept - This 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 this - key will be used. E.g. with `compconf approximate_accept=2n' two - errors will be accepted, but if the user gives another number - with the numeric argument, this will be prefered. Also, with - `compconf approximate_accept=0n', normally no correction will be - tried, but if a numeric argument is given, automatic correction - will be used. On the other hand, if the string contains an `!' - and a `n' or `N', correction is not attempted if a numeric - argument is given. 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. Independent of the - number of errors the user wants to accept, the code will allow - only fewer errors than there are characters in the string from - the line. - - approximate_original - This value is used to determine if the original string should - be included in the list (and thus be presented to the user when - cycling through the corrections). If it is set to any non-empty - value, the original string will be offered. If it contains the - sub-string `last', the original string will appear as the last - string when cycling through the corrections, otherwise it will - appear as the first one (so that the command line does not - change immediately). Also, if the value contains the sub-string - `always', the original string will always be included, whereas - normally it is included only if more than one possible - correction was generated. - - approximate_prompt - This can be set to a string that should be printed before the - list of corrected strings when cycling through them. This string - may contain the control sequences `%n', `%B', etc. known from - the `-X' option of `compctl'. Also, the sequence `%e' will be - replaced by the number of errors accepted to generate the - corrected strings. - - approximate_insert - If this is set to a string starting with `unambig', the code - will try to insert a usable unambiguous string in the command - line instead of always cycling through the corrected strings. - If such a unambiguous string could be found, the original - string is not used, independent of the setting of - `approximate_original'. If no sensible string could be found, - one can cycle through the corrected strings as usual. - -If any of these keys is not set, but the the same key with the -prefix `correct' instead of `approximate' is set, that value will -be used. The forms beginning with `correct' are also used by the -_correct completer function, and this function uses only them, not -the ones starting with `approximate'. This allows one to give -different value to be used with correction and correcting -completion. For example, with: - - compconf completer=_complete:_correct:_approximate - compconf correct_accept='2n!' approximate_accept=3n - -correction will accept up to two errors. If a numeric argument is -given, correction will not be used, but correcting completion will -and it will accept as many errors as given by the numeric argument. -Without a numeric argument first correction and then correcting -completion will be tried, with the first one accepting two errors -and the second one accepting three errors. - -The _match completer function, which allows to give patterns on the -command line and complete all strings matching it from the set of -possible completions, understands the following key: - - match_original - If this is set to a `only', pattern matching will only be tried - with the string from the line. If it is set to any other non-empty - string, the original pattern will be tried first and if that yields - no completions, matching will be tried again with a `*' inserted - at the cursor position. If this key is not set or set to an empty - string, matching will only be attempted with the `*' inserted. - -The _expand completer allows one to do expansion on the word from the -line. Note that you may either want to use the configuration keys or -not use this completer at all if you also use the _match completer -because the _expand completer will otherwise expand patterns before -they are seen by the _match completer. -Configuration keys supported are: - - expand_substitute - If this is unset or set to the empty string, the code will first - try to expand all substitutions in the string (such as $(...) and - ${...}). If this is set to an non-empty string it should be - an expression usable inside a $[...] arithmetical expression. - In this case, expansion of substitutions will be done if the - expression evaluates to `1'. For example, with - - compconf expand_substitute='NUMERIC != 1' - - substitution will be performed only if given an explicit numeric - argument other than `1', as by typing ESC 2 TAB. - - expand_glob - If this is unset or set to an empty string, globbing will be - attempted on the word resulting from substitution or the - original string. The values accepted for this key are the same - as for expand_substitute. - - expand_menu - If this is unset or set to the empty string, the words resulting - from expansion (if any) will simply be inserted in the ommand line, - replacing the original string. However, if this key is set to an - non-empty string, the user can cycle through the expansion as in - a menucompletion. Unless the value contains the sub-string `only', - the user will still be offered all expansions at once as one of - the strings to insert in the command line. Also, if the value - contains the sub-string `last', the string with all expansion will - be offered first, whereas normally it is offered as the last string - to insert. Finally, if the value contains the sub-string `sort', - the expansions will be sorted alphabetically, normally they are - kept in the order the expansion produced them in. - - expand_original - If this is set to an non-empty string, the original string from the - line will be included in the list of strings the user can cycle - through as in a menucompletion. If the value contains the sub-string - `last', the original string will appear as the last string, with - other values it is inserted as the first one (so that the command - line does not change immediatly). - - expand_prompt - This may be set to a string that should be displayed before the - possible expansions. This is given to the -X option and thus may - contain the control sequences `%n', `%B', etc. Also, the sequence - `%o' in this string will be replaced by the original string. - -The _list completer allows one to delay the insertion of matches until -completion is attempted a second time without the word on the line -being changed. On the first attempt, only the list of matches will -be shown. Configuration keys understood are: - - list_condition - If this key is unset or set to the empty string, this completer - will delay the insertion of matches unconditionally. However, - if this value is set, it should be set to an expression usable - inside a $[...] arithmetical expression. In this case, delaying - will be done if the expression evaluates to `1'. - For example, with - - compconf list_condition='NUMERIC != 1' - - delaying will be done only if given an explicit numeric argument - other than `1'. - - list_word - To find out if only listing should be done, the code normally - compares the contents of the line with the contents the line - had at the time of the last invocation. If this key is set to - an non-empty string comparison is done using only the current - word. So if it is set, attempting completion on a word equal - to the one completion was called on the last time will not - delay the generation of matches. - -For more information about what the completers do, see the files -containing them (_complete, _correct, _approximate, _match, and -_expand in the Core directory). - -The _path_files function used to complete filenames has one -configuration key: - - path_expand - If this is set to a non-empty string, the partially typed path - from the line will be expanded as far as possible even if trailing - pathname components can not be completed. +The subdirectories contain code for a shell-based system of command-line +completion. This is considerably more powerful than the old `compctl' +method. For details, see the compsys(1) manual page (`man zshcompsys'). diff --git a/Completion/User/_a2ps b/Completion/User/_a2ps index 9923ae20d..26f4d200e 100644 --- a/Completion/User/_a2ps +++ b/Completion/User/_a2ps @@ -1,4 +1,4 @@ -#defcomp a2ps +#compdef a2ps # This is for the GNU version of a2ps. diff --git a/Completion/User/_compress b/Completion/User/_compress index 860aeb5b0..84ed42fb2 100644 --- a/Completion/User/_compress +++ b/Completion/User/_compress @@ -1,3 +1,3 @@ -#defcomp compress +#compdef compress _files -g '*~*.Z' diff --git a/Completion/User/_configure b/Completion/User/_configure index 7559852e8..01c12dd62 100644 --- a/Completion/User/_configure +++ b/Completion/User/_configure @@ -1,4 +1,9 @@ -#defcomp configure +#compdef configure -_long_options '*=(E|)PREFIX*' '_files -/' \ +_long_options -i '(--(disable|enable)-FEATURE* --(with|without)-PACKAGE*)' \ + -s '(#--disable- --enable- + #--enable- --disable- + #--with- --without- + #--without- --with-)' \ + '*=(E|)PREFIX*' '_files -/' \ '*=PROGRAM*' '_command_names' diff --git a/Completion/User/_dd b/Completion/User/_dd index 6b07a382c..6094d60b7 100644 --- a/Completion/User/_dd +++ b/Completion/User/_dd @@ -1,4 +1,4 @@ -#defcomp dd +#compdef dd if compset -P 1 'conv\='; then # If there's a comma present, ignore up to the last one. The diff --git a/Completion/User/_dvi b/Completion/User/_dvi index bb2fc293e..d49840ae9 100644 --- a/Completion/User/_dvi +++ b/Completion/User/_dvi @@ -1,3 +1,3 @@ -#defcomp xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype +#compdef xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype _files -g '*.(dvi|DVI)' diff --git a/Completion/User/_find b/Completion/User/_find index 13d1d797e..116ee6f6b 100644 --- a/Completion/User/_find +++ b/Completion/User/_find @@ -1,4 +1,4 @@ -#defcomp find +#compdef find local prev="$words[CURRENT-1]" diff --git a/Completion/User/_gunzip b/Completion/User/_gunzip index 35a27e774..7868e0c24 100644 --- a/Completion/User/_gunzip +++ b/Completion/User/_gunzip @@ -1,3 +1,3 @@ -#defcomp gunzip zcat +#compdef gunzip zcat _files -g '*.[gG][z]' diff --git a/Completion/User/_gzip b/Completion/User/_gzip index 3cda1e4ed..59e74a441 100644 --- a/Completion/User/_gzip +++ b/Completion/User/_gzip @@ -1,3 +1,3 @@ -#defcomp gzip +#compdef gzip _files -g '*~*.[gG][zZ]' diff --git a/Completion/User/_hosts b/Completion/User/_hosts index eb418c5b3..911cea03b 100644 --- a/Completion/User/_hosts +++ b/Completion/User/_hosts @@ -1,3 +1,3 @@ -#defcomp ftp ncftp ping rwho rup xping traceroute nslookup +#compdef ftp ncftp ping rwho rup xping traceroute nslookup compgen -k hosts diff --git a/Completion/User/_make b/Completion/User/_make index 24d2cf3f5..f950ad754 100644 --- a/Completion/User/_make +++ b/Completion/User/_make @@ -1,3 +1,3 @@ -#defcomp make gmake pmake +#compdef make gmake pmake -compgen -s "\$(awk '/^[a-zA-Z0-9][^/ ]+:/ {print \$1}' FS=: [mM]akefile)" +compgen -s "\$(awk '/^[a-zA-Z0-9][^\/ ]+:/ {print \$1}' FS=: [mM]akefile)" diff --git a/Completion/User/_man b/Completion/User/_man index cd1badc4d..1a175506c 100644 --- a/Completion/User/_man +++ b/Completion/User/_man @@ -1,4 +1,4 @@ -#defcomp man +#compdef man setopt localoptions rcexpandparam diff --git a/Completion/User/_mh b/Completion/User/_mh index ab78468d8..c2801b896 100644 --- a/Completion/User/_mh +++ b/Completion/User/_mh @@ -1,4 +1,4 @@ -#defcomp folder folders comp inc mark refile repl scan show next prev rmm pick whom mhn mhpath +#compdef folder folders comp inc mark refile repl scan show next prev rmm pick whom mhn mhpath # Completion for all possible MH commands. # Alter the following two to your own mh directory and the directory diff --git a/Completion/User/_pdf b/Completion/User/_pdf index 11abc216b..b8759864e 100644 --- a/Completion/User/_pdf +++ b/Completion/User/_pdf @@ -1,3 +1,3 @@ -#defcomp acroread +#compdef acroread _files -g '*.(pdf|PDF)' diff --git a/Completion/User/_ps b/Completion/User/_ps index 6bc0643b2..6c0d91a23 100644 --- a/Completion/User/_ps +++ b/Completion/User/_ps @@ -1,3 +1,3 @@ -#defcomp gs ghostview gview psnup psselect pswrap pstops pstruct lpr +#compdef gs ghostview gview psnup psselect pswrap pstops pstruct lpr _files -g '*([pP][sS]|eps)' diff --git a/Completion/User/_rcs b/Completion/User/_rcs index 5e53cb4da..af66739cb 100644 --- a/Completion/User/_rcs +++ b/Completion/User/_rcs @@ -1,4 +1,4 @@ -#defcomp co ci rcs +#compdef co ci rcs local nm=$compstate[nmatches] diff --git a/Completion/User/_rlogin b/Completion/User/_rlogin index 5d6daef9b..9f5f3d628 100644 --- a/Completion/User/_rlogin +++ b/Completion/User/_rlogin @@ -1,4 +1,4 @@ -#defcomp rlogin rsh ssh +#compdef rlogin rsh ssh if [[ CURRENT -eq 2 ]]; then compgen -k hosts diff --git a/Completion/User/_strip b/Completion/User/_strip index 541c901ad..d9f44cc5e 100644 --- a/Completion/User/_strip +++ b/Completion/User/_strip @@ -1,3 +1,3 @@ -#defcomp strip +#compdef strip _files -g '*(*)' diff --git a/Completion/User/_stty b/Completion/User/_stty index 73c860d54..32355be30 100644 --- a/Completion/User/_stty +++ b/Completion/User/_stty @@ -1,4 +1,4 @@ -#defcomp stty +#compdef stty if [[ "$words[CURRENT-1]" = \ (*erase|discard|status|dsusp|intr|kill|lnext|quit|reprint|start|s*p) ]] diff --git a/Completion/User/_tar b/Completion/User/_tar index d11ee76c8..f443fefb7 100644 --- a/Completion/User/_tar +++ b/Completion/User/_tar @@ -1,4 +1,4 @@ -#defcomp tar +#compdef tar # Tar completion. Features: # - Tries to collect tar commands from second position, single letter diff --git a/Completion/User/_tex b/Completion/User/_tex index 1f645e2a2..ac926cfbe 100644 --- a/Completion/User/_tex +++ b/Completion/User/_tex @@ -1,3 +1,3 @@ -#defcomp tex latex slitex +#compdef tex latex slitex _files -g '*.(tex|TEX|texinfo|texi)' diff --git a/Completion/User/_uncompress b/Completion/User/_uncompress index e25805d50..0aa1e073f 100644 --- a/Completion/User/_uncompress +++ b/Completion/User/_uncompress @@ -1,3 +1,3 @@ -#defcomp uncompress zmore +#compdef uncompress zmore _files -g '*.Z' diff --git a/Completion/User/_x_options b/Completion/User/_x_options index 3190ee377..cd45c87f6 100644 --- a/Completion/User/_x_options +++ b/Completion/User/_x_options @@ -1,4 +1,4 @@ -#defpatcomp */X11/* +#compdef -p */X11/* # A simple pattern completion, just as an example. diff --git a/Completion/User/_xfig b/Completion/User/_xfig index fcd2bba9f..a3b30bb81 100644 --- a/Completion/User/_xfig +++ b/Completion/User/_xfig @@ -1,3 +1,3 @@ -#defcomp xfig +#compdef xfig _files -g '*.fig' diff --git a/Config/version.mk b/Config/version.mk index 747fa76a7..6bf4d3e9b 100644 --- a/Config/version.mk +++ b/Config/version.mk @@ -27,5 +27,5 @@ # This must also serve as a shell script, so do not add spaces around the # `=' signs. -VERSION=3.1.5-pws-14 -VERSION_DATE='March 26, 1999' +VERSION=3.1.5-pws-15 +VERSION_DATE='April 13, 1999' diff --git a/Doc/Makefile.in b/Doc/Makefile.in index d1fcf162a..df16ef19a 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -38,7 +38,7 @@ TEXI2HTML = texi2html -expandinfo -split_chapter .SUFFIXES: .yo .1 # man pages to install -MAN = zsh.1 zshbuiltins.1 zshcompctl.1 zshcompwid.1 \ +MAN = zsh.1 zshbuiltins.1 zshcompctl.1 zshcompwid.1 zshcompsys.1 \ zshexpn.1 zshmisc.1 zshmodules.1 \ zshoptions.1 zshparam.1 zshzle.1 zshall.1 @@ -48,7 +48,7 @@ YODLSRC = zmacros.yo zman.yo ztexi.yo Zsh/arith.yo Zsh/builtins.yo \ Zsh/compat.yo Zsh/compctl.yo Zsh/cond.yo Zsh/exec.yo Zsh/expn.yo \ Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/guide.yo \ Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \ -Zsh/modules.yo Zsh/mod_cap.yo Zsh/compwid.yo \ +Zsh/modules.yo Zsh/mod_cap.yo Zsh/compwid.yo Zsh/compsys.yo \ Zsh/mod_clone.yo Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_deltochar.yo \ Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_stat.yo \ Zsh/mod_zle.yo Zsh/options.yo \ @@ -115,6 +115,8 @@ zshcompctl.1: Zsh/compctl.yo zshcompwid.1: Zsh/compwid.yo +zshcompsys.1: Zsh/compsys.yo + zshexpn.1: Zsh/expn.yo zshmisc.1: Zsh/grammar.yo Zsh/redirect.yo Zsh/exec.yo Zsh/func.yo \ diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo index f494ec5f9..7b672dd68 100644 --- a/Doc/Zsh/compctl.yo +++ b/Doc/Zsh/compctl.yo @@ -449,7 +449,8 @@ be shown if there was at least one match added for the explanation string. The sequences tt(%B), tt(%b), tt(%S), tt(%s), tt(%U), and tt(%u) specify -output attributes (bold, standout, and underline) as in prompts. +output attributes (bold, standout, and underline) and tt(%{...%}) can +be used to include literal escape sequences as in prompts. ) item(tt(-Y) var(explanation))( Identical to tt(-X), except that the var(explanation) first undergoes diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo new file mode 100644 index 000000000..632cb3195 --- /dev/null +++ b/Doc/Zsh/compsys.yo @@ -0,0 +1,691 @@ +texinode(Completion System)()(Zsh Modules)(Top) +chapter(Completion System) +cindex(completion, system) +cindex(completion, programmable) +cindex(completion, controlling) +sect(Description) + +This describes the shell code for the new completion system. It consists +of two scripts and a few other files that define shell functions. +The shell functions which implement completion behaviour and which may +be bound to keystrokes, are referred to as `widgets'. All are contained +in the following subdirectories of the tt(Completion) directory of the main +distribution directory. + +startitem() +item(tt(Core))( +The core scripts and functions. You will certainly need these, though will +probably not need to alter them. The contents of this directory is +described in more detail below. +) +item(tt(Base))( +Other functions you will almost certainly want if you are going to use +any of the standard completion functions. You may want to edit some of +these files. +) +item(tt(Builtins))( +Functions for completing arguments of shell builtin commands. +) +item(tt(User))( +Functions for completing arguments of external commands and suites of +commands. They may need modifying for your system. +) +item(tt(Commands))( +Functions which implement special types of completion to be bound to +keystrokes rather than called by context. +) +enditem() + +You should decide which files you will be using and copy them to a +directory (or multiple directories) of your own which should appear in your +tt($fpath) variable so that the functions can be autoloaded. + +startmenu() +menu(Initialisation) +menu(Control Functions) +menu(Completion Functions) +endmenu() + +texinode(Initialisation)(Control Functions)()(Completion System) +sect(Initialisation) + +To initialise the system, the script tt(compinit) should be sourced with +`tt(source )var(<path>)tt(/compinit)' or +`tt(. )var(<path>)tt(/compinit)'. This will define a few utility functions, +arrange for all the necessary shell functions to be autoloaded, and will +then re-bind all keys that do completion to use the new system. + +subsect(Arguments) + +To speed up the running of tt(compinit), it can be made to produce a dumped +configuration which will be read in on future invocations. The easiest way +to do this is by adding the option tt(-d) whenever tt(compinit) is sourced. +In this case the dumped file will have the same name as the sourced file, +but with tt(.dump) appended to the end; alternatively, an explicit file +name can be given following the tt(-d). On the next call to tt(compinit +-d), the dumped file will be read instead. + +If the number of completion files changes, tt(compinit) will recognise this +and produce a new dump file. However, if the name of a function or the +arguments in the first line of a tt(#compdef) funcion (as described below) +change, it is easiest to delete the dump file by hand so that the next time +tt(compinit) will re-create it. + +The dumping is actually done by another script, tt(compdump), but you will +only need to source this yourself if you change the configuration +(e.g. using tt(compdef)) and then want to dump the new one. The name of +the old dumped file will be remembered for this. + +subsect(Autoloaded files) + +The convention for autoloaded functions used in completion is that they +start with an underscore; as already mentioned, the tt(fpath/FPATH) +parameter must contain the directory in which they are stored. When +tt(compinit) is sourced, it searches all such files accessible via +tt(fpath/FPATH) and reads the first line of each of them. This line should +contain one of the tags described below. Files whose first line does not +start with one of these tags are not considered to be part of the +completion system and will not be treated specially. + +The tags are: + +startitem() +item(tt(#compdef) var(names...))( +The file will be made autoloadable and the function defined +in it will be called when completing var(names), each of which is +either the name of a command whose arguments are to be completed or one of +a number of special contexts in the form tt(-)var(context)tt(-) described +below for the tt(_complete) function. +) +item(tt(#compdef -p) var(pattern))( +The file will be made autoloadable and the function defined in it will be +called when completing for a command whose name matches the given +var(pattern) (a standard globbing pattern). Note that only one +var(pattern) may be given. +) +item(tt(#compdef -k) var(style key-sequences...))( +This can be used bind special completion functions to the +var(key-sequences). It creates a widget behaving like the builtin widget +var(style), which must be one of those that perform completion, namely +tt(complete-word), tt(delete-char-or-list), tt(expand-or-complete), +tt(expand-or-complete-prefix), tt(list-choices), tt(menu-complete), +tt(menu-expand-or-complete), or tt(reverse-menu-complete). + +The widget is then bound to all the var(key-sequences) given, if any: when +one of the var(key-sequences) is typed, the function in the file will +be invoked to generate the matches. The widget created has the same +name as the file and can also be bound to other keys using tt(bindkey) +as usual. +) +item(tt(#autoload))( +This is used for files defining utility function that are not to be +called directly as completion functions but should be loaded automatically +when invoked. Typically they are to be called from within one of the +completion functions. +) +enditem() + +Note that the tt(#) is part of the tag name and no white space is allowed +after it. The tt(#compdef) tags use the tt(compdef) function defined +below; the main difference is that the name of the function is supplied +implicitly. + +subsect(Functions) + +The tt(compinit) file defines the following functions, which may +also be called directly by the user. + +startitem() +xitem(tt(compdef) [ tt(-an) ] var(function names...)) +xitem(tt(compdef -d) var(names...)) +xitem(tt(compdef -p) [ tt(-a) ] var(function pattern)) +item(tt(compdef -k) [ tt(-a) ] var(function style key-sequences...))( +The first form tells the completion system to call the given +var(function) when completing for the contexts or commands +whose var(names) are given: this is like the tt(#compdef) tag. If the +tt(-n) option is given, any existing completion behaviour for particular +contexts or commands will not be altered. These definitions can be deleted +by giving the tt(-d) option as in the second form. + +The third form is similar to the first, but var(function) will be called +for all commands whose name matches the var(pattern); this is like the +tt(#compdef -p) function tag. + +The fourth form defines a widget with the same name as the var(function) +which will be called for each of the var(key-sequences); this is like the +tt(#compdef -k) tag. The function should generate the completions needed +and will otherwise behave like the builtin widget whose name is given as +the var(style) argument. The widgets usable for this are: +tt(complete-word), tt(delete-char-or-list), tt(expand-or-complete), +tt(expand-or-complete-prefix), tt(list-choices), tt(menu-complete), +tt(menu-expand-or-complete), and tt(reverse-menu-complete). + +In each of the forms supporting it the tt(-a) option makes the +var(function) autoloadable (exactly equivalent to +tt(autoload )var(function)). +) +xitem(tt(compconf) var(definitions...)) +xitem(tt(compconf)) +item(tt(compconf) [ tt(-l) ] var(keys...))( +Several aspects of the completion system can be configured by the +user. The configuration values are stored under the keys described +below in the associative array `tt(compconfig)'. After sourcing +tt(compinit), configuration values can either be set directly as in +`tt(compconfig[completer]=_complete)' or by calling this utility function. + +Each var(definition) may be either a simple `var(key)', which sets this +key in the tt(compconfig) array to an empty string, or of the form +`var(key=value)' which stores the `var(value)' under key `var(key)'. + +Since the completion system also uses the array for internal purposes, +you should not set all values at once by doing `tt(compconfig=(...))'. + +In the second form (without arguments), this function lists all keys +and their values. If given the tt(-l) option as its first argument, as +in the last form, the other arguments are taken as names of keys and +the values of these keys are printed one per line. +) +enditem() + +texinode(Control Functions)(Completion Functions)(Initialisation)(Completion System) +sect(Control Functions) + +The initialisation script tt(compinit) re-binds all the keys which perform +completion to newly created widgets that all call the supplied widget +function tt(_main_complete). This function acts as a wrapper calling +the so-called `completer' functions that generate matches. If +tt(_main_complete) is +called with arguments, these are taken as the names of completer +functions to be called in the order given. If no arguments are given, the +set of functions to try is taken from the colon-separated list in the +configuration key tt(completer). For example, to use normal +completion and correction if that doesn't generate any matches: + +indent( +nofill(tt(compconf completer=_complete:_correct)) +) + +after sourcing tt(compinit). The default value for this configuration key +set up in tt(compinit) is `tt(_complete)', i.e. normally only ordinary +completion is tried. The tt(_main_complete) function uses the return value +of the completer functions to decide if other completers should be +called. If the return value is zero, no other completers are tried and the +tt(_main_complete) function returns. + +The following completer functions are contained in the distribution (users +may write their own): + +startitem() +item(tt(_complete))( +This completer generates all possible completions in a context-sensitive +manner, i.e. using the tt(compdef) function +explained above and the current settings of all special parameters. + +To complete arguments of commands, tt(_complete) uses the utility function +tt(_normal), which is in turn responsible for finding the particular +function; it is described below. Various contexts of the form +tt(-)var(context)tt(-), as mentioned above for the tt(#compdef) tag, are +handled specially. These are: + +startitem() +item(tt(-equal-))( +for completion after an equal sign, other than one occurring in a +shell-variable assignment. +) +item(tt(-tilde-))( +for completion after a tilde (`tt(~)') character, but before a slash. +) +item(tt(-redirect-))( +for completion after a redirection operator. +) +item(tt(-math-))( +for completion inside mathematical contexts, such as +`tt(LPAR()LPAR())...tt(RPAR()RPAR())'. +) +item(tt(-subscript-))( +for completion inside subscripts. +) +item(tt(-value-))( +for completion on the right hand side of an assignment. +) +item(tt(-array-value-))( +for completion on the right hand side of an array-assignment +(`tt(foo=LPAR()...RPAR())'). +) +item(tt(-condition-))( +for completion inside conditions (`tt([[...]])'). +) +item(tt(-parameter-))( +for completing the name of a parameter expansion (`tt($...)'). +) +item(tt(-brace-parameter-))( +for completing the name of a parameter expansion within braces +(`tt(${...})'). +) +item(tt(-first-))( +for adding completions before any other other completion functions are +tried (similar to the `tt(-T)' flag of tt(compctl)); if this +function sets the tt(_compskip) parameter to any value, the completion +system will not call any other function to generate matches. +) +item(tt(-default-))( +for generating completions when no special completion function is used +(similar to the `tt(-D)' option of tt(compctl)). +) +item(tt(-command-))( +for completing in a command position (as with the `tt(-C)' option of +tt(compctl)). +) +enditem() + +Default implementations are supplied for each of these +contexts, in most cases named after the context itself +(e.g. completion for the `tt(-tilde-)' context is done by the function +named `tt(_tilde)'). +) +item(tt(_approximate))( +This completer function uses the tt(_complete) completer to generate +a list of strings for the context the cursor is currently in, allowing +you to specify a maximum number of errors: see the description of +approximate matching in +ifzman(\ +zmanref(zshexpn) +)\ +ifnzman(\ +noderef(Filename Generation) +)\ +for how errors are +counted. The resulting list of corrected and completed strings is then +presented to the user. The intended use of this completer function is to +try after the normal tt(_complete) completer by setting: + +indent( +nofill(tt(compconf completer=_complete:_approximate)) +) + +This will give correcting completion if and only if +normal completion doesn't yield any possible completions. When +corrected completions are found, the completer will normally start +menucompletion allowing you to cycle through these strings. + +The exact behavior of this completer can be changed by using the +following configuration keys: + +startitem() +item(tt(approximate_accept))( +This should be set to the number of errors the correction code should +accept. The completer will try to generate completions by first allowing +one error, then two errors, and so on, until either a match +was found or the maximum number of errors given by this key has +been reached. + +If the value for this key contains a lower- or upper-case `tt(n)', the +completer function will take any numeric argument as the +maximum number of errors allowed. For example, with + +indent( +nofill(tt(compconf approximate_accept=2n)) +) + +two errors will be allowed if no numeric argument is given. However, +with a numeric argument of six (as in `tt(ESC-6 TAB)'), up to six +errors are accepted. Hence with a value of `tt(0n)', no correcting +completion will be attempted unless a numeric argument is given. + +If the value contains `tt(n)' or `tt(N)' and a exclamation mark +(`tt(!)'), tt(_approximate) will var(not) try to generate corrected +completions when given a numeric argument, so in this case the number given +should be greater than zero. For example, `tt(2n!)' specifies that +correcting completion with two errors will usually be performed, but if a +numeric argument is given, correcting completion will not be performed. +) +item(tt(approximate_original))( +This key is used to specify whether the original string on which correcting +completion was attempted is to be included in the list of possible +corrections. If it is set to any non-empty string, the original string +will be offered when cycling through the completions. Normally it will +appear as the first string, so that the command line does not change +immediately; consecutive completion attempts will cycle through the +corrected strings. If the value for this key contains the substring +`tt(last)', the original string will be the last one in the list, so +that it appears just before wrapping around to the first corrected +string again. Also, if the value contains the substring `tt(always)', +the original string will always be included; normally it is +included only if more than one possible correction was generated. +) +item(tt(approximate_prompt))( +This can be set to a string to be displayed on top of the +corrected strings generated when cycling through them. This string +may contain the control sequences `tt(%n)', `tt(%B)', etc. known from +the `tt(-X)' option of tt(compctl). Also, the sequence `tt(%e)' will +be replaced by the number of errors accepted to generate the corrected +strings. +) +item(tt(approximate_insert))( +If this is set to a string starting with `tt(unambig)', the code will try +to insert a usable unambiguous string in the command line instead of +always cycling through the corrected strings. If such a unambiguous +string could be found, the original string is not used, independent of +the setting of tt(approximate_original). If no sensible string could be +found, one can cycle through the corrected strings as usual. +) +enditem() + +If any of these keys is not set, but the the same key with the prefix +`tt(correct)' instead of `tt(approximate)' is set, that value will be +used. The forms beginning with `tt(correct)' are also used by the +tt(_correct) completer function. + +The keys with the `tt(approximate)' prefix have no default values, but +tt(compinit) defines default values for tt(correct_accept) (which +is set to `tt(2n)'), and tt(correct_prompt). +) +item(tt(_correct))( +Generate corrections (but not completions) for the current word; this is +similar to spell-checking. This calls tt(_approximate), but only the +configuration parameters beginning tt(correct_) are used. + +For example, with: + +indent(tt( +nofill(compconf completer=_complete:_correct:_approximate) +nofill(compconf correct_accept='2n!' approximate_accept=3n)) +) + +correction will accept up to two errors. If a numeric argument is +given, correction will not be performed, but correcting completion will be, +and will accept as many errors as given by the numeric argument. +Without a numeric argument, first correction and then correcting +completion will be tried, with the first one accepting two errors +and the second one accepting three errors. + +This completer function is intended to be used without the +tt(_approximate) completer or, as in the example, just before +it. Using it after the tt(_approximate) completer is useless since +tt(_approximate) will at least generate the corrected strings +generated by the tt(_correct) completer -- and probably more. +) +item(tt(_match))( +This completer is intended to be used after the tt(_complete) +completer. It allows one to give patterns on the command line and +to complete all strings metching these patterns from the set of possible +completions for the context the cursor is in, without having to set +the tt(GLOB_COMPLETE) option. + +Normally this will be done by taking the pattern from the line, +inserting a `tt(*)' at the cursor position and comparing the resulting +pattern with the possible completions generated. However, if the +configuration key tt(match_original) has a value of `tt(only)', no +`tt(*)' will be inserted. If tt(match_original) has any other non-empty +string as its value, this completer will first try to generate matches +without, then with a `tt(*)' inserted at the cursor position. +) +item(tt(_expand))( +This completer function does not really do completion, but instead +checks if the word on the command line is eligible for expansion and, +if it is, gives detailed control over how this expansion is done. When +using this, one should not use the tt(expand-or-complete) widget, but +instead use tt(complete-word), as otherwise tt(expand-or-complete) +will expand the string on the line before the completion widget is +called. Also, this completer should be called before the tt(_complete) +completer function. + +Control over how the expanded string will be treated is possible with the +following configuration keys: + +startitem() +item(tt(expand_substitute))( +If this is unset or set to the empty string, the code will first try +to expand all substitutions in the string (such as +`tt($LPAR()...RPAR())' and `tt(${...})'). If this is set to an +non-empty string it should be an expression usable inside a `tt($((...)))' +arithmetical expression. In this case, expansion of substitutions will +be done if the expression evaluates to `tt(1)'. For example, with + +indent( +nofill(tt(compconf expand_substitute='NUMERIC != 1')) +) + +substitution will be performed only if given an explicit numeric +argument other than `tt(1)', as by typing `tt(ESC 2 TAB)'. +) +item(tt(expand_glob))( +If this is unset or set to an empty string, globbing will be attempted +on the word resulting from substitution or the original string. The +values accepted for this key are the same as for tt(expand_substitute). +) +item(tt(expand_menu))( +If this is unset or set to the empty string, the words resulting from +expansion (if any) will simply be inserted in the command line, +replacing the original string. However, if this key is set to a +non-empty string, the user can cycle through the expansion as in +menucompletion. Unless the value contains the substring `tt(only)', +the user will still be offered all expansions at once as one of the +strings to insert in the command line; normally, this possibility is +offered first, but if the value contains the +substring `tt(last)', it is offered last. Finally, if the value contains +the substring `tt(sort)', the expansions will be sorted alphabetically, +normally they are kept in the order the expansion produced them in. +) +item(tt(expand_original))( +If this is set to an non-empty string, the original string from the +line will be included in the list of strings the user can cycle +through as in a menucompletion. If the value contains the substring +`tt(last)', the original string will appear as the last string, with +other values it is inserted as the first one (so that the command line +does not change immediately). +) +item(tt(expand_prompt))( +This may be set to a string that should be displayed before the +possible expansions. This is passed to the `tt(-X)' option of +tt(compadd) and thus may contain the control sequences `tt(%n)', +`tt(%B)', etc. Also, the sequence `tt(%o)' in this string will be +replaced by the original string. +) +enditem() + +None of these configuration keys has a default value. +) +item(tt(_list))( +This completer allows one to delay the insertion of matches until +completion is attempted a second time without the word on the line +being changed. On the first attempt, only the list of matches will be +shown. Configuration keys understood are: + +startitem() +item(tt(list_condition))( +If this key is unset or set to the empty string, the insertion of +matches will be delayed unconditionally. If this value is set, it +should be set to an expression usable inside a `tt($((...)))' +arithmetical expression. In this case, delaying will be done if the +expression evaluates to `tt(1)'. For example, with + +indent( +nofill(tt(compconf list_condition='NUMERIC != 1')) +) + +delaying will be done only if given an explicit numeric argument +other than `tt(1)'. +) +item(tt(list_word))( +To find out if listing should be performed on its own, the code normally +compares the contents of the line with the contents the line had at the +time of the last invocation. If this key is set to an non-empty string, +comparison is done using only the current word. So if it is set, +attempting completion on a word equal to the one when completion was called +the last time will not delay the generation of matches. +) +enditem() +) +item(tt(_menu))( +This completer is a simple example function implemented to show how +menucompletion can be done in shell code. It should be used as the +first completer and has the effect of making the code perform +menucompletion. Note that this is independent of the setting of the +tt(MENU_COMPLETE) option and does not work with the other +menucompletion widgets such as tt(reverse-menu-complete), or +tt(accept-and-menu-complete). +) +enditem() + +texinode(Completion Functions)()(Control Functions)(Completion System) +sect(Utility Functions) + +Descriptions follow for utility functions that may be +useful when writing completion functions. Most of these reside in the +tt(Core) subdirectory except where noted. Like the example +functions for commands in the distribution, the utility functions +generating matches all follow the convention of returning zero if they +generated completions and non-zero if no matching completions could be +added. + +startitem() +item(tt(_compalso))( +This function looks up the definitions for the context and command +names given as arguments and calls the handler functions for them if +there is a definition (given with the tt(compdef) function). For +example, the function completing inside subscripts might use +`tt(_compalso -math-)' to include the completions generated for +mathematical environments. +) +item(tt(_normal))( +This function is used for normal command completion. If +completion is attempted on the first word, command names are +completed. Otherwise, the arguments are completed by calling the +functions defined for this command, including those functions defined +for patterns matching the command name. This function can also be +called by other completion functions if they have to complete a range +of words as a separate command. For example, the function to complete after +the pre-command specifiers such as tt(nohup) removes the first word from +the tt(words) array, decrements the tt(CURRENT) parameter, then calls this +function. + +When calling a function defined for a pattern, this function also +checks if the parameter tt(_compskip) is set. If it was set by the +function called, no further completion functions are called. With this +one can write a pattern completion function that keeps other functions +from being tried simply by setting this parameter to any value. +) +item(tt(_multi_parts))( +This functions gets two arguments: a separator character and an +array. As usual, the array may be either the +name of an array parameter or a literal array in the form +`tt(LPAR()foo bar)tt(RPAR())' (i.e. a list of words separated by white +space in parentheses). With these arguments, this function will +complete to strings from the array where the parts separated by the +separator character are completed independently. For example, the +tt(_tar) function from the distribution caches the pathnames from the +tar file in an array and then calls this function to complete these +names in the way normal filenames are completed by the +tt(_path_files) function. + +Like other utility functions, this function accepts the `tt(-V)', +`tt(-J)', and `tt(-X)' options with an argument and passes them to the +tt(compadd) builtin. +) +item(tt(_sep_parts))( +This function gets as arguments alternating arrays and separators. +The arrays specify completions for parts of strings to be separated by the +separators. The arrays may be the names of array parameters or +a quoted list of words in parentheses. For example, with the array +`tt(hosts=(ftp news))' the call `tt(_sep_parts '(foo bar)' @ hosts)' will +complete the string `tt(f)' to `tt(foo)' and the string `tt(b@n)' to +`tt(bar@news)'. + +This function passes the `tt(-V)', `tt(-J)', and `tt(-X)' options and +their arguments to the tt(compadd) builtin used to add the matches. +) +item(tt(_path_files) and tt(_files))( +The function tt(_path_files) is used throughout the shell code +to complete filenames. The advantage over the builtin +completion functions is that it allows completion of partial paths. For +example, the string `tt(/u/i/s/sig)' may be completed to +`tt(/usr/include/sys/signal.h)'. The options `tt(-/)', `tt(-f)', `tt(-g)', +and `tt(-W)' are available as for the tt(compctl) +and tt(compgen) builtins; tt(-f) is the default. Additionally, the `tt(-F)' +option from the tt(compadd) builtin is supported, giving direct control +over which filenames should be ignored as done by the tt(fignore) +parameter in normal completion. + +The function tt(_files) calls tt(_path_files) with all the arguments +it was passed and, if that generated no matches, call tt(_path_files) again +without any tt(-g) or tt(-/) option, thus generating all filenames. + +These functions also accept the `tt(-J)', `tt(-V)', `tt(-X)', `tt(-P)', +`tt(-S)', `tt(-q)', `tt(-r)', and `tt(-R)' options from the +tt(compadd) builtin. + +Finally, the tt(_path_files) function supports one configuration key: +tt(path_expand). If this is set to any non-empty string, the partially +typed path from the line will be expanded as far as possible even if +trailing pathname components can not be completed. +) +item(tt(_parameters))( +This should be used to complete parameter names if you need some of the +extra options of tt(compadd). It first tries to complete only non-local +parameters. All arguments are passed unchanged to the tt(compadd) builtin. +) +item(tt(_options))( +This can be used to complete option names. The difference to the +`tt(-o)' option of tt(compgen) is that this function uses a matching +specification that ignores a leading `tt(no)', ignores underscores and +allows the user to type upper-case letters, making them match their +lower-case counterparts. All arguments passed to this function are +propagated unchanged to the tt(compgen) builtin. +) +item(tt(_set_options) and tt(_unset_options))( +These functions complete only set or unset options, with the same +matching specification used in the tt(_options) function. + +Note that you need to uncomment a few lines in the tt(_main_complete) +function for these functions to work properly. The lines in question +are used to store the option settings in effect before the completion +widget locally sets the options it needs. +) +item(tt(_long_options))( +This function resides in the tt(Base) subdirectory of the example +completion system because it is not used by the core system. + +This function is used to complete long options for commands that +support the `tt(--help)' option as, for example, most of the GNU +commands do. For this it invokes the command from the line with the +`tt(--help)' option and then parses the output to find possible option +names. Note that this means that you should be careful to make sure +that this function is not called for a command that does not support +this option. + +For options that get an argument after a `tt(=)', the function also +automatically tries to find out what should be completed as the argument. +The possible completions for option-arguments can be described with +the arguments to this function. This is done by giving pairs of +patterns and actions as consecutive arguments. The actions specify +what should be done to complete arguments of those options whose +description match the pattern. The action may be a list of words in +brackets or in parentheses, separated by spaces. A list in square brackets +denotes possible values for an optional argument, a list in parentheses +gives words to complete for mandatory arguments. If the action does +not start with a square bracket or parenthesis, it should be the name of a +command (probably with arguments) that should be invoked to complete +after the equal sign. Example: + +indent( +nofill(tt(_long_options '*\*' '(yes no)' \)) +nofill(tt( '*=FILE*' '_files' \)) +nofill(tt( '*=DIR*' '_files -/')) +) + +Here, `tt(yes)' and `tt(no)' will be completed as the argument of +options whose description ends in a star, file names for options that +contain the substring `tt(=FILE)' in the description, and paths for +options whose description contains `tt(=DIR)'. In fact, the last two +patterns are not needed since this function always completes files +for option descriptions containing `tt(=FILE)' and paths for option +descriptions that contain `tt(=DIR)' or `tt(=PATH)'. These builtin +patterns can be overridden by patterns given as arguments, however. + +This function also accepts the `tt(-X)', `tt(-J)', and `tt(-V)' +options which are passed unchanged to `tt(compadd)'. Finally, it +accepts the option `tt(-t)'; if this is given, completion is only done +on words starting with two hyphens. +) +enditem() diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 7d7e688b0..0a626f4e1 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -186,6 +186,18 @@ normally be shown only if there are at least two matches in the list. Setting tt(force_list) to an non-empty string makes the list be shown even if there is only one match. ) +item(tt(list_max))( +Initially this is set to the value of the tt(LISTMAX) parameter. +Completion widgets may set it to any other numeric value and the value +stored at when the widget finishes will be used in the same way the +value of tt(LISTMAX) is used. +) +item(tt(last_prompt))( +If this is set to an non-empty string, the completion code will move +the cursor back to the previous prompt after the list of completions +has been displayed. Initially this is set depending on the setting of +the tt(ALWAYS_LAST_PROMPT) option. +) item(tt(insert))( This will be unset by the completon code if the contents of the command line will not be changed. It is set to tt(unambiguous), @@ -193,6 +205,62 @@ tt(menu), or tt(automenu) if a common unambiguous string will be inserted or if the first match will be inserted and menu completion will be started (due to tt(MENU_COMPLETE) or tt(AUTO_MENU) being set), respectively. + +On exit it may be set to any of the values above with the obvious +result or to a number or a string of the form +`var(group):var(match)'. If it is set to a number the match whose +number is given will be inserted in the command line. A string like +`tt(2:4)' specifies which match from which group should be +inserted. In this example the fourth match of the second group is +inserted. All groups and matches are number from one upwards. In the +value of this key, negative numbers count backward from the last match +or group (with `tt(-1)' selecting the last match or group) and values +out of the range from one to the number of matches or groups are +wrapped around (so that a value of zero selects the last match and a +value equal to the number of matches or groups plus one selects the +first one). +) +item(tt(to_end))( +On entry to the completion widget this is set to tt(single) if the +cursor would be moved to the end of the word only if completion +generated only one match and that is inserted into the line. Depending +on the original position of the cursor and the setting of the option +tt(ALWAYS_TO_END) this may also be set to the string tt(match) if the +cursor would be moved to the end if a whole match would be inserted +(either if there is only one match or if menucompletion is used). + +The value of this key after the completion widget exits will be used +to determin when the cursor will be moved to the end of the string +inserted into the line. If it is unset or set to the empty string, the +cursor will never be moved to the end. If it is set to tt(single), it +will be moved to the end only if completion generated only one +match. A value of tt(always) says to move the cursor always to the end +(even with normal completion when an unambiguous string is inserted), +and any other value says to move the cursor to the end when a full +match is inserted (a single match or the first match when using +menucompletion). +) +item(tt(old_list))( +This is set to tt(yes) if there is still a valid list of completions +from a previous completion at the time the widget is invoked. Such a +list exists if it was generated by the previous key press. If the list +is also shown on the screen, the value of this key is tt(shown). + +After the widget has exited the value of this key is only used if it +was set to tt(keep). In this case, the completion code will continue +to use this old list. If the widget generated new matches, they will +not be used. +) +item(tt(old_insert))( +On entry to the widget this will be set to the number of the match of +an old list of completions that is currently inserted in the command +line. If no match has been inserted, this is unset. + +As with tt(old_list), the value of this key will only be used if it is +the string tt(keep). If it was set to this value by the widget and +there was an old match inserted in the line, this match will be kept +and if the value of the tt(insert) key says that another match should +be inserted, this will be inserted after the old one. ) item(tt(exact))( This is set to tt(accept) if an exact match would be accepted by the @@ -268,7 +336,8 @@ xitem([ tt(-i) var(ignored-prefix) ] [ tt(-I) var(ignored-suffix) ]) xitem([ tt(-W) var(file-prefix) ]) xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ]) xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ]) -item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])( +xitem([ tt(-M) var(match-spec) ] [ tt(-O) var(array) ] [ tt(-A) var(array) ]) +item([ tt(--) ] [ var(words) ... ])( This builtin command can be used to add matches and directly control all the information the completion code stores with each possible @@ -437,6 +506,23 @@ Note that with tt(compadd) this option does not automatically turn on menu completion if tt(AUTO_LIST) is set as the same options for the tt(compctl) and tt(compgen) builtin command do. ) +item(tt(-O) var(array))( +If this option is given, the var(words) are em(not) added to the set of +possible completions. Instead, matching is done as usual and all +var(words) given as arguments that are matched will be stored in the +array parameter whose name is given as var(array). +) +item(tt(-A) var(array))( +Like the tt(-O) option this keeps the var(words) from being stored as +possible completions. The matching words are instead stored in the array +parameter given as var(array). In difference to the tt(-O) option this +does not store the unchanged var(words) given as arguments, but instead +the strings the completion code generated while matching. For example, +with a matching specification of `tt(-M "L:|no=")', the string `tt(nof)' +on the line and the string `tt(foo)' as one of the var(words), this option +will make the string `tt(nofoo)' be stored in the array, whereas the tt(-O) +option stores the `tt(foo)' originally given. +) item(tt(-), tt(--))( This flag ends the list of flags and options. All arguments after it will be taken as the words to use as matches even if they begin with @@ -596,3 +682,9 @@ tt(complete-history LPAR()RPAR() { compgen -H 0 '' }))) In this the function will complete words from the history matching the current word. + +For a description of the example completion system from the +distributions, see +ifzman(zmanref(zshcompsys))\ +ifnzman(noderef(Completion System))\ +. diff --git a/Doc/Zsh/guide.yo b/Doc/Zsh/guide.yo index 96e12fc58..81347da1f 100644 --- a/Doc/Zsh/guide.yo +++ b/Doc/Zsh/guide.yo @@ -31,6 +31,7 @@ menu(Zsh Line Editor) menu(Programmable Completion) menu(Completion Widgets) menu(Zsh Modules) +menu(Completion System) --- Indices --- diff --git a/Doc/Zsh/intro.yo b/Doc/Zsh/intro.yo index f619f82fe..b3d4887b1 100644 --- a/Doc/Zsh/intro.yo +++ b/Doc/Zsh/intro.yo @@ -23,6 +23,7 @@ list(em(zshzle) Zsh command line editing) list(em(zshcompctl) Zsh completion control) list(em(zshcompwid) Zsh completion widgets) list(em(zshmodules) Zsh loadable modules) +list(em(zshcompsys) Zsh completion system) ifzshone(\ list(em(zshall) Meta-man page containing all of the above) )\ diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo index 97c420eed..63894076b 100644 --- a/Doc/Zsh/modules.yo +++ b/Doc/Zsh/modules.yo @@ -1,4 +1,4 @@ -texinode(Zsh Modules)()(Completion Widgets)(Top) +texinode(Zsh Modules)(Completion System)(Completion Widgets)(Top) chapter(Zsh Modules) cindex(modules) sect(Description) diff --git a/Doc/Zsh/seealso.yo b/Doc/Zsh/seealso.yo index 5231e8828..924663651 100644 --- a/Doc/Zsh/seealso.yo +++ b/Doc/Zsh/seealso.yo @@ -11,6 +11,7 @@ ifzshone(\ zmanref(zshbuiltins), zmanref(zshcompctl), zmanref(zshcompwid), +zmanref(zshcompsys), zmanref(zshexpn), zmanref(zshmisc), zmanref(zshmodules), diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index bd91f0efa..c4809b02d 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -139,6 +139,12 @@ The part of the buffer that lies to the right of the cursor position. If it is assigned to, only that part of the buffer is replaced, and the cursor remains between the old tt($LBUFFER) and the new tt($RBUFFER). ) +vindex(PREBUFFER) +item(tt(PREBUFFER) (scalar))( +In a multi-line input at the secondary prompt, this read-only parameter +contains the contents of the lines before the one the cursor is +currently in. +) vindex(WIDGET) item(tt(WIDGET) (scalar))( The name of the widget currently being executed. diff --git a/Doc/zsh.yo b/Doc/zsh.yo index e6cef6a32..cb768bb3a 100644 --- a/Doc/zsh.yo +++ b/Doc/zsh.yo @@ -66,6 +66,7 @@ ifnzman(includefile(Zsh/zle.yo)) ifnzman(includefile(Zsh/compctl.yo)) ifnzman(includefile(Zsh/compwid.yo)) ifnzman(includefile(Zsh/modules.yo)) +ifnzman(includefile(Zsh/compsys.yo)) ifzshall(\ def(source)(1)(NOTRANS(.so )man1/ARG1NOTRANS(.)1)\ CMT() @@ -78,6 +79,7 @@ source(zshzle) source(zshcompctl) source(zshcompwid) source(zshmodules) +source(zshcompsys) manpage(ZSHALL)(1)(date())(zsh version()) )\ ifzman(includefile(Zsh/filelist.yo)) diff --git a/Doc/zshcompsys.yo b/Doc/zshcompsys.yo new file mode 100644 index 000000000..d640606b6 --- /dev/null +++ b/Doc/zshcompsys.yo @@ -0,0 +1,3 @@ +manpage(ZSHCOMPSYS)(1)(date())(zsh version()) +manpagename(zshcompsys)(zsh completion system) +includefile(Zsh/compsys.yo) diff --git a/Misc/compctl-examples b/Misc/compctl-examples index e84a37fbb..3aa9b15f4 100644 --- a/Misc/compctl-examples +++ b/Misc/compctl-examples @@ -11,15 +11,15 @@ # page. # #------------------------------------------------------------------------------ -hosts=("${${(s: :)${(s: :)${${(f)$(</etc/hosts)}%%\#*}#*[ ]*}}:#}") -ports=( "${${${(f)$(</etc/services)}:#\#*}%%[ ]*}" ) +hosts=("${(@)${(@s: :)${(@s: :)${(@)${(@f)$(</etc/hosts)}%%\#*}#*[ ]*}}:#}") +ports=( "${(@)${(@)${(@f)$(</etc/services)}:#\#*}%%[ ]*}" ) # groups=( $(cut -d: -f1 /etc/group) ) # groups=( $(ypcat group.byname | cut -d: -f1) ) # if you use NIS # It can be done without forking, but it used too much memory in old zsh's: -groups=( "${${(f)$(</etc/group)}%%:*}" ) -#groups=( "${${(f)$(ypcat groups)}%%:*}" ) # if you use NIS +groups=( "${(@)${(@f)$(</etc/group)}%%:*}" ) +#groups=( "${(@)${(@f)$(ypcat groups)}%%:*}" ) # if you use NIS # Completion for zsh builtins. compctl -z -P '%' bg @@ -251,7 +251,7 @@ compctl -k ftphosts ftp # Note that 'r[-exec,;]' must come first if [[ -r /proc/filesystems ]]; then # Linux - filesystems='"${${(f)$(</proc/filesystems)}#* }"' + filesystems='"${(@)${(@f)$(</proc/filesystems)}#* }"' else filesystems='ufs 4.2 4.3 nfs tmp mfs S51K S52K' fi @@ -563,7 +563,7 @@ cvsprefix() { cvsentries() { setopt localoptions nullglob unset if [[ -f ${pref}CVS/Entries ]]; then - reply=( "${pref}${^${${${(f@)$(<${pref}CVS/Entries)}:#D*}#/}%%/*}" ) + reply=( "${pref}${^${(@)${(@)${(f@)$(<${pref}CVS/Entries)}:#D*}#/}%%/*}" ) fi } @@ -574,7 +574,7 @@ cvstargets() { } cvsrevisions() { - reply=( "${${${(M)${(f)$(cvs -q status -vl .)}:# *}##[ ]##}%%[ ]*}" ) + reply=( "${(@)${(@)${(@M)${(@f)$(cvs -q status -vl .)}:# *}##[ ]##}%%[ ]*}" ) } cvsrepositories() { @@ -582,7 +582,7 @@ cvsrepositories() { [[ -f CVS/Root ]] && root=$(<CVS/Root) reply=( $root/^CVSROOT(:t) - "${${(M)${(f)$(<$root/CVSROOT/modules)}:#[^#]*}%%[ ]*}" + "${(@)${(@M)${(@f)$(<$root/CVSROOT/modules)}:#[^#]*}%%[ ]*}" ) } @@ -622,7 +622,7 @@ compctl -s '$(rpm -qa)' \ 'c[-1,--rcfile]' -f - \ 'p[1] s[-b]' -k '(p l c i b a)' - \ 'c[-1,--queryformat] N[-1,{]' \ - -s '"${${(f)$(rpm --querytags)}#RPMTAG_}"' -S '}' - \ + -s '"${(@)${(@f)$(rpm --querytags)}#RPMTAG_}"' -S '}' - \ 'W[1,-q*] C[-1,-([^-]*|)f*]' -f - \ 'W[1,-i*], W[1,-q*] C[-1,-([^-]*|)p*]' \ -/g '*.rpm' + -f -- rpm @@ -635,7 +635,7 @@ compctl -K talkmatch talk ytalk ytalk3 function talkmatch { local u reply=($(users)) - for u in "${${(f)$(rwho 2>/dev/null)}%%:*}"; do + for u in "${(@)${(@f)$(rwho 2>/dev/null)}%%:*}"; do reply=($reply ${u%% *}@${u##* }) done } diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 42a647199..c9d6aa859 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -190,6 +190,7 @@ struct cmgroup { LinkList lmatches; /* list of matches */ LinkList lfmatches; /* list of matches without fignore */ LinkList lallccs; /* list of used compctls */ + int num; /* number of this group */ }; @@ -213,6 +214,8 @@ struct cmatch { int brsl; /* ...and the suffix */ char *rems; /* when to remove the suffix */ char *remf; /* shell function to call for suffix-removal */ + int rnum; /* group relative number */ + int gnum; /* global number */ }; #define CMF_FILE 1 /* this is a file */ @@ -298,7 +301,12 @@ struct cpattern { #define CP_PATINSERT (1 << 23) #define CP_UNAMBIG (1 << 24) #define CP_UNAMBIGC (1 << 25) +#define CP_LISTMAX (1 << 26) +#define CP_LASTPROMPT (1 << 27) +#define CP_TOEND (1 << 28) +#define CP_OLDLIST (1 << 29) +#define CP_OLDINS (1 << 30) -#define CP_NUM 26 +#define CP_NUM 31 -#define CP_ALLMASK ((1 << CP_NUM) - 1) +#define CP_ALLMASK ((int) ((((unsigned int) 1) << CP_NUM) - 1)) diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c index 904f66305..fe21b1dfc 100644 --- a/Src/Zle/comp1.c +++ b/Src/Zle/comp1.c @@ -52,7 +52,7 @@ void (*comp_setunsetptr) _((int, int)); /* pointers to functions required by compctl and defined by zle */ /**/ -int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **)); +int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char *, char *, char **)); /**/ char *(*comp_strptr) _((int *, int *, int)); @@ -103,7 +103,8 @@ int incompfunc; long compcurrent, compnmatches, compmatcher, - compmatchertot; + compmatchertot, + complistmax; /**/ char **compwords, @@ -124,7 +125,11 @@ char **compwords, *compexact, *compexactstr, *comppatmatch, - *comppatinsert; + *comppatinsert, + *complastprompt, + *comptoend, + *compoldlist, + *compoldins; /**/ Param *comppms; @@ -439,7 +444,8 @@ setup_comp1(Module m) compcontext = compparameter = compredirect = compquote = compquoting = comprestore = complist = compinsert = compexact = compexactstr = comppatmatch = comppatinsert = - compforcelist = NULL; + compforcelist = complastprompt = comptoend = + compoldlist = compoldins = NULL; makecompparamsptr = NULL; comp_setunsetptr = NULL; return 0; @@ -487,6 +493,10 @@ finish_comp1(Module m) zsfree(compexactstr); zsfree(comppatmatch); zsfree(comppatinsert); + zsfree(complastprompt); + zsfree(comptoend); + zsfree(compoldlist); + zsfree(compoldins); return 0; } diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export index 1be9b6492..4f9fb143d 100644 --- a/Src/Zle/comp1.export +++ b/Src/Zle/comp1.export @@ -19,11 +19,15 @@ compforcelist compinsert compiprefix compisuffix +complastprompt complist +complistmax compmatcher compmatcherstr compmatchertot compnmatches +compoldlist +compoldins compparameter comppatinsert comppatmatch @@ -36,6 +40,7 @@ comprestore comp_setunsetptr comp_strptr compsuffix +comptoend compwords freecmatcher freecmlist diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 2ebe2d4a4..28c7cb7b7 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1694,7 +1694,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) char *p, **sp, *e; char *ipre = NULL, *isuf = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL; char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL; - char *ign = NULL, *rf = NULL, *expl = NULL; + char *ign = NULL, *rf = NULL, *expl = NULL, *apar = NULL, *opar = NULL; int f = 0, a = CAF_MATCH, dm; Cmatcher match = NULL; @@ -1791,6 +1791,14 @@ bin_compadd(char *name, char **argv, char *ops, int func) sp = &rf; e = "function name expected after -%c"; break; + case 'A': + sp = &apar; + e = "parameter name expected after -%c"; + break; + case 'O': + sp = ⦷ + e = "parameter name expected after -%c"; + break; case '-': argv++; goto ca_args; @@ -1799,15 +1807,13 @@ bin_compadd(char *name, char **argv, char *ops, int func) return 1; } if (sp) { - if (*sp) { - zerrnam(name, "doubled option: -%c", NULL, *p); - return 1; - } if (p[1]) { - *sp = p + 1; + if (!*sp) + *sp = p + 1; p = "" - 1; } else if (argv[1]) { - *sp = *++argv; + if (!*sp) + *sp = *++argv; p = "" - 1; } else { zerrnam(name, e, NULL, *p); @@ -1826,7 +1832,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) match = cpcmatcher(match); a = addmatchesptr(ipre, isuf, ppre, psuf, prpre, pre, suf, group, - rs, rf, ign, f, a, match, expl, argv); + rs, rf, ign, f, a, match, expl, apar, opar, argv); freecmatcher(match); return a; @@ -2152,7 +2158,13 @@ static struct compparam { { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL }, { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL }, { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) }, - { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_unambig_curs) }, + { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL, + VAL(get_unambig_curs) }, + { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL }, + { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL }, + { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL }, + { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL }, + { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL }, { NULL, 0, NULL, NULL, NULL } }; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 479994249..7a5d0d7db 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -883,7 +883,7 @@ trashzle(void) moveto(nlnct, 0); if (clearflag && tccan(TCCLEAREOD)) { tcout(TCCLEAREOD); - clearflag = 0; + clearflag = listshown = 0; } if (postedit) fprintf(shout, "%s", postedit); @@ -927,6 +927,7 @@ setup_zle(Module m) /* miscellaneous initialisations */ stackhist = stackcs = -1; kungetbuf = (char *) zalloc(kungetsz = 32); + hasperm = 0; /* initialise the keymap system */ init_keymaps(); diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index adde116f2..d9fefe659 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -61,6 +61,8 @@ static struct zleparam { zleunsetfn, NULL }, { "RBUFFER", PM_SCALAR, FN(set_rbuffer), FN(get_rbuffer), zleunsetfn, NULL }, + { "PREBUFFER", PM_SCALAR | PM_READONLY, NULL, FN(get_prebuffer), + zleunsetfn, NULL }, { "WIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_widget), zleunsetfn, NULL }, { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget), @@ -212,6 +214,16 @@ get_rbuffer(Param pm) /**/ static char * +get_prebuffer(Param pm) +{ + if (chline) + return dupstrpfx(chline, hptr - chline); + else + return dupstring(""); +} + +/**/ +static char * get_widget(Param pm) { return bindk->nam; diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index b9f39b70b..1f1063b8d 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -53,6 +53,11 @@ int nlnct; /**/ int showinglist; +/* Non-zero if a completion list was displayed. */ + +/**/ +int listshown; + /* Non-zero if ALWAYS_LAST_PROMPT has been used, meaning that the * * screen below the buffer display should not be cleared by * * zrefresh(), but should be by trashzle(). */ @@ -253,13 +258,23 @@ zrefresh(void) if (inlist) return; - if (clearlist) { - invalidatelist(); - moveto(0, 0); - clearflag = 0; - resetneeded = 1; - clearlist = 0; + if (clearlist && listshown) { + if (tccan(TCCLEAREOD)) { + int ovln = vln, ovcs = vcs; + + moveto(nlnct, 0); + tcout(TCCLEAREOD); + moveto(ovln, ovcs); + } else { + invalidatelist(); + moveto(0, 0); + clearflag = 0; + resetneeded = 1; + } + listshown = 0; } + clearlist = 0; + #ifdef HAVE_SELECT cost = 0; /* reset */ #endif @@ -287,6 +302,7 @@ zrefresh(void) moveto(0, 0); t0 = olnct; /* this is to clear extra lines even when */ winchanged = 0; /* the terminal cannot TCCLEAREOD */ + listshown = 0; } #endif resetvideo(); @@ -303,6 +319,7 @@ zrefresh(void) tcout(TCCLEAREOD); else cleareol = 1; /* request: clear to end of line */ + listshown = 0; } if (t0 > -1) olnct = t0; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 4ae7495c3..1f13d55b4 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -72,12 +72,27 @@ static int wb, we; static int offs; -/* These control the type of completion that will be done. They are * - * affected by the choice of ZLE command and by relevant shell options. * - * usemenu is set to 2 if we have to start automenu. */ +/* These control the type of completion that will be done. They are * + * affected by the choice of ZLE command and by relevant shell options. * + * usemenu is set to 2 if we have to start automenu and 3 if we have to * + * insert a match as if for menucompletion but without really stating it. */ static int usemenu, useglob, useexact, useline, uselist; +/* Non-zero if we should keep an old list. */ + +static int oldlist, oldins; + +/* The match and group number to insert when starting menucompletion. */ + +static int insmnum, insgnum, insgroup; + +/* This is used to decide when the cursor should be moved to the end of * + * the inserted word: 0 - never, 1 - only when a single match is inserted, * + * 2 - when a full match is inserted (single or menu), 3 - always. */ + +static int movetoend; + /* != 0 if we are in the middle of a menu completion */ static int menucmp; @@ -99,12 +114,12 @@ static int menupos, menulen, menuend, menuwe, menuinsc; /* This is for completion inside a brace expansion. brbeg and brend hold * * strings that were temporarily removed from the string to complete. * - * brpl and brsl, brbsl hold the offset of these strings. * + * brpl and brsl, hold the offset of these strings. * * brpcs and brscs hold the positions of the re-inserted string in the * * line. */ static char *brbeg = NULL, *brend = NULL; -static int brpl, brsl, brbsl, brpcs, brscs; +static int brpl, brsl, brpcs, brscs, qbrpl, qbrsl, hasunqu; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ @@ -114,7 +129,16 @@ static LinkList matches, fmatches; /* This holds the list of matches-groups. lmatches is a pointer to the * * last element in this list. */ -static Cmgroup amatches, lmatches; +static Cmgroup pmatches, amatches, lmatches; + +/* Non-zero if we have permanently allocated matches. */ + +/**/ +int hasperm; + +/* Number of permanently allocated matches and groups. */ + +static int permmnum, permgnum; /* The total number of matches and the number of matches to be listed. */ @@ -155,6 +179,7 @@ static char *ppre, *psuf, *lppre, *lpsuf, *prpre; static char *fpre, *fsuf; static char *ipre, *ripre; static char *isuf; +static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf; static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl; static int noreal; @@ -485,14 +510,9 @@ reversemenucomplete(void) * with the next completions. This gives you a way to * * accept several selections from the list of matches. */ -/**/ -void -acceptandmenucomplete(void) +static void +acceptlast(void) { - if (!menucmp) { - feep(); - return; - } if (brbeg && *brbeg) { int l; @@ -514,6 +534,17 @@ acceptandmenucomplete(void) menupos = cs; menuwe = 1; } +} + +/**/ +void +acceptandmenucomplete(void) +{ + if (!menucmp) { + feep(); + return; + } + acceptlast(); menucomplete(); } @@ -650,7 +681,13 @@ check_param(char *s, int set) int n = 0, br = 1; if (*b == Inbrace) { - /* If this is a ${...}, ignore the possible (...) flags. */ + char *tb = b; + + /* If this is a ${...}, see if we are before the '}'. */ + if (!skipparens(Inbrace, Outbrace, &tb)) + return NULL; + + /* Ignore the possible (...) flags. */ b++, br++; n = skipparens(Inpar, Outpar, &b); } @@ -1494,11 +1531,13 @@ get_comp_string(void) if (tt && tt < s + myoffs) { /* Braces are go: delete opening brace */ - char *com = NULL; + char *com = NULL, *tmp; int pl, sl; brbeg = dupstring(tt); brpl = tt - s; + tmp = dupstrpfx(s, tt - s); + qbrpl = strlen(quotename(tmp, NULL)); pl = 1; sl = 0; chuck(tt); @@ -1531,8 +1570,8 @@ get_comp_string(void) if (*p == Outbrace) chuck(p); brsl = strlen(s) - (p - s); - brbsl = p - s; brend[sl] = '\0'; + qbrsl = strlen(quotename(p, NULL)); } /* we are still waiting for an outbrace and maybe commas */ if (brbeg) @@ -2040,7 +2079,7 @@ static int match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test) { int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw; - int il = 0, iw = 0, t, ind, add, bc; + int il = 0, iw = 0, t, ind, add, bc = (bp ? *bp : 0); VARARR(unsigned char, ea, ll + 1); char *ow; Cmlist ms; @@ -2054,9 +2093,9 @@ match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test) if (sfx) { l += ll; w += lw; - ind = -1; add = -1; bc = brsl; + ind = -1; add = -1; } else { - ind = 0; add = 1; bc = brpl; + ind = 0; add = 1; } /* ow will always point to the beginning (or end) of that sub-string * in w that wasn't put in the match-variables yet. */ @@ -2406,17 +2445,19 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp, wl = strlen(w); *clp = bld_parts(w, wl, wl, NULL); *exact = 0; + *bpl = (qu ? qbrpl : brpl); + *bsl = (qu ? qbrsl : brsl); } else { Cline pli, plil; int mpl, rpl, wl; - if (qu) - w = quotename(w, NULL); + w = (qu ? quotename(w, NULL) : dupstring(w)); wl = strlen(w); /* Always try to match the prefix. */ + *bpl = (qu ? qbrpl : brpl); if ((mpl = match_str(pfx, w, bpl, &rpl, 0, 0)) < 0) return NULL; @@ -2441,6 +2482,7 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp, plil = matchlastpart; /* The try to match the suffix. */ + *bsl = (qu ? qbrsl : brsl); if ((msl = match_str(sfx, w + mpl, bsl, &rsl, 1, 0)) < 0) { free_cline(pli); @@ -2479,6 +2521,7 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp, pli = matchparts; } r = dupstring(matchbuf); + *clp = pli; /* Test if the string built is equal to the one from the line. */ @@ -2489,6 +2532,9 @@ comp_match(char *pfx, char *sfx, char *w, Comp cp, } else *exact = !strcmp(pfx, w); } + if (!qu) + hasunqu = 1; + return r; } @@ -3398,6 +3444,22 @@ add_match_data(int alt, char *str, Cline line, return cm; } +/* This stores the strings from the list in an array. */ + +static void +set_param(char *name, LinkList l) +{ + char **a, **p; + LinkNode n; + + a = (char **) zalloc((countlinknodes(l) + 1) * sizeof(char *)); + for (p = a, n = firstnode(l); n; incnode(n)) + *p++ = ztrdup((char *) getdata(n)); + *p = NULL; + + setaparam(name, a); +} + /* This is used by compadd to add a couple of matches. The arguments are * the strings given via options. The last argument is the array with * the matches. */ @@ -3407,22 +3469,29 @@ int addmatches(char *ipre, char *isuf, char *ppre, char *psuf, char *prpre, char *pre, char *suf, char *group, char *rems, char *remf, char *ign, - int flags, int aflags, Cmatcher match, char *exp, char **argv) + int flags, int aflags, Cmatcher match, char *exp, + char *apar, char *opar, char **argv) { char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL; char **aign = NULL; int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum; - int oisalt = 0, isalt, isexact; + int oisalt = 0, isalt, isexact, doadd; Cline lc = NULL; Cmatch cm; struct cmlist mst; Cmlist oms = mstack; Comp cp = NULL; + LinkList aparl = NULL, oparl = NULL; /* Switch back to the heap that was used when the completion widget * was invoked. */ SWITCHHEAPS(compheap) { HEAPALLOC { + doadd = (!apar && !opar); + if (apar) + aparl = newlinklist(); + if (opar) + oparl = newlinklist(); if (exp) { expl = (Cexpl) zhalloc(sizeof(struct cexpl)); expl->count = expl->fcount = 0; @@ -3548,9 +3617,17 @@ addmatches(char *ipre, char *isuf, rems = NULL; } else if (rems) rems = dupstring(rems); + + /* Probably quote the prefix and suffix for testing. */ + if (!cp && (aflags & CAF_MATCH) && !(aflags & CAF_QUOTE)) { + lpre = quotename(lpre, NULL); + lsuf = quotename(lsuf, NULL); + llpl = strlen(lpre); + llsl = strlen(lsuf); + } } /* Walk through the matches given. */ - for (; (s = dupstring(*argv)); argv++) { + for (; (s = *argv); argv++) { sl = strlen(s); bpl = brpl; bsl = brsl; @@ -3565,25 +3642,40 @@ addmatches(char *ipre, char *isuf, if ((filell = strlen(*pt)) < sl && !strcmp(*pt, s + sl - filell)) isalt = 1; + + if (isalt && !doadd) + continue; } if (!(aflags & CAF_MATCH)) { - ms = s; - lc = bld_parts(s, sl, -1, NULL); + ms = dupstring(s); + lc = bld_parts(ms, sl, -1, NULL); isexact = 0; } else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc, - (aflags & CAF_QUOTE), + !(aflags & CAF_QUOTE), &bpl, &bsl, &isexact))) continue; - cm = add_match_data(isalt, ms, lc, ipre, ipre, isuf, pre, prpre, - ppre, psuf, suf, bpl, bsl, - flags, isexact); - cm->rems = rems; - cm->remf = remf; + if (doadd) { + cm = add_match_data(isalt, ms, lc, ipre, ipre, isuf, pre, + prpre, ppre, psuf, suf, bpl, bsl, + flags, isexact); + cm->rems = rems; + cm->remf = remf; + } else { + if (apar) + addlinknode(aparl, ms); + if (opar) + addlinknode(oparl, s); + free_cline(lc); + } } compnmatches = mnum; if (exp) addexpl(); + if (apar) + set_param(apar, aparl); + if (opar) + set_param(opar, oparl); } LASTALLOC; } SWITCHBACKHEAPS; @@ -3629,9 +3721,6 @@ addmatch(char *s, char *t) hn = (HashNode) t; pm = (Param) t; - if (incompfunc) - s = dupstring(s); - if (addwhat == -1 || addwhat == -5 || addwhat == -6 || addwhat == CC_FILES || addwhat == -7 || addwhat == -8) { if ((addwhat == CC_FILES || @@ -3645,10 +3734,13 @@ addmatch(char *s, char *t) !strcmp(*pt, s + sl - filell)) isalt = 1; } - if (!(ms = comp_match(fpre, fsuf, s, filecomp, &lc, - (addwhat == CC_FILES || addwhat == -6 || - addwhat == -5 || addwhat == -8), - &bpl, &bsl, &isexact))) + ms = ((addwhat == CC_FILES || addwhat == -6 || + addwhat == -5 || addwhat == -8) ? + comp_match(qfpre, qfsuf, s, filecomp, &lc, 1, + &bpl, &bsl, &isexact) : + comp_match(fpre, fsuf, s, filecomp, &lc, 0, + &bpl, &bsl, &isexact)); + if (!ms) return; if (addwhat == -7 && !findcmd(s, 0)) @@ -3678,10 +3770,19 @@ addmatch(char *s, char *t) (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) || ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) || ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) { - if (!(ms = comp_match(rpre, rsuf, s, patcomp, &lc, + char *p1, *s1, *p2, *s2; + + if (addwhat == CC_QUOTEFLAG) { + p1 = qrpre; s1 = qrsuf; + p2 = rpre; s2 = rsuf; + } else { + p1 = qlpre; s1 = qlsuf; + p2 = lpre; s2 = lsuf; + } + if (!(ms = comp_match(p1, s1, s, patcomp, &lc, (addwhat == CC_QUOTEFLAG), &bpl, &bsl, &isexact)) && - !(ms = comp_match(lpre, lsuf, s, NULL, &lc, + !(ms = comp_match(p2, s2, s, NULL, &lc, (addwhat == CC_QUOTEFLAG), &bpl, &bsl, &isexact))) return; @@ -4002,6 +4103,7 @@ docompletion(char *s, int lst, int incmd) ainfo = fainfo = NULL; matchers = newlinklist(); + hasunqu = 0; useline = (lst != COMP_LIST_COMPLETE); useexact = (isset(RECEXACT) && usemenu != 1); uselist = (useline ? @@ -4014,6 +4116,12 @@ docompletion(char *s, int lst, int incmd) zsfree(compforcelist); compforcelist = ztrdup(""); haspattern = 0; + complistmax = getiparam("LISTMAX"); + zsfree(complastprompt); + complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || + (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? + "yes" : ""); + movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1); /* Make sure we have the completion list and compctl. */ if (makecomplist(s, incmd, lst)) { @@ -4031,7 +4139,7 @@ docompletion(char *s, int lst, int incmd) /* We have matches. */ if (nmatches > 1) /* There is more than one match. */ - do_ambiguous(); + do_ambiguous(); else if (nmatches == 1) { /* Only one match. */ @@ -4039,6 +4147,7 @@ docompletion(char *s, int lst, int incmd) while (!m->mcount) m = m->next; + menucur = NULL; do_single(m->matches[0]); invalidatelist(); } @@ -4069,9 +4178,8 @@ docompletion(char *s, int lst, int incmd) g = g->next; } if (!tr) { - clearflag = ((isset(USEZLE) && !termflags && - (isset(ALWAYSLASTPROMPT) && zmult == 1)) || - (unset(ALWAYSLASTPROMPT) && zmult != 1)); + clearflag = (isset(USEZLE) && !termflags && + complastprompt && *complastprompt); if (clearflag && up + nlnct < lines) tcmultout(TCUP, TCMULTUP, up + nlnct); @@ -4099,7 +4207,8 @@ callcompfunc(char *s, char *fn) { List list; int lv = lastval; - + char buf[20]; + if ((list = getshfunc(fn)) != &dummy_list) { char **p, *tmp; int set, aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext; @@ -4108,7 +4217,10 @@ callcompfunc(char *s, char *fn) comppms = (Param *) zalloc(CP_NUM * sizeof(Param)); set = -1 & ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING | - CP_EXACTSTR | CP_FORCELIST | (useglob ? 0 : CP_PATMATCH)); + CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS | + (useglob ? 0 : CP_PATMATCH)); + if (!*complastprompt) + set &= ~CP_LASTPROMPT; zsfree(compcontext); zsfree(compparameter); zsfree(compredirect); @@ -4205,20 +4317,17 @@ callcompfunc(char *s, char *fn) zsfree(compprefix); zsfree(compsuffix); if (unset(COMPLETEINWORD)) { - if (linwhat == IN_MATH) - tmp = s; - else - tmp = quotename(s, NULL); + /* Maybe we'll have to do quoting here some time. */ + tmp = dupstring(s); untokenize(tmp); compprefix = ztrdup(tmp); compsuffix = ztrdup(""); } else { - char *ss = s + offs, sav; + char *ss, sav; - if (linwhat == IN_MATH) - tmp = s; - else - tmp = quotename(s, &ss); + tmp = dupstring(s); + ss = tmp + offs; + sav = *ss; *ss = '\0'; untokenize(tmp); @@ -4260,6 +4369,30 @@ callcompfunc(char *s, char *fn) compexact = ztrdup(""); set &= ~CP_EXACT; } + zsfree(comptoend); + if (movetoend == 1) + comptoend = ztrdup("single"); + else + comptoend = ztrdup("match"); + zsfree(compoldlist); + zsfree(compoldins); + if (hasperm && permmnum) { + if (listshown) + compoldlist = "shown"; + else + compoldlist = "yes"; + set |= CP_OLDLIST; + if (menucur) { + sprintf(buf, "%d", (*menucur)->gnum); + compoldins = buf; + set |= CP_OLDINS; + } else + compoldins = ""; + } else + compoldlist = compoldins = ""; + compoldlist = ztrdup(compoldlist); + compoldins = ztrdup(compoldins); + incompfunc = 1; startparamscope(); makecompparamsptr(); @@ -4294,10 +4427,32 @@ callcompfunc(char *s, char *fn) else if (!strcmp(compinsert, "auto") || !strcmp(compinsert, "automenu")) useline = 1, usemenu = 2; - else + else if (idigit(*compinsert)) { + char *m; + + useline = 1; usemenu = 3; + insmnum = atoi(compinsert); + if ((m = strchr(compinsert, ':'))) { + insgroup = 1; + insgnum = atoi(m + 1); + } + } else useline = usemenu = 0; useexact = (compexact && !strcmp(compexact, "accept")); + if (!comptoend || !*comptoend) + movetoend = 0; + else if (!strcmp(comptoend, "single")) + movetoend = 1; + else if (!strcmp(comptoend, "always")) + movetoend = 3; + else + movetoend = 2; + + oldlist = (hasperm && compoldlist && !strcmp(compoldlist, "keep")); + oldins = (hasperm && menucur && + compoldins && !strcmp(compoldins, "keep")); + zfree(comppms, CP_NUM * sizeof(Param)); comppms = ocpms; } @@ -4327,6 +4482,7 @@ makecomplist(char *s, int incmd, int lst) struct cmlist ms; Cmlist m; char *p, *os = s; + int onm = nmatches; /* Inside $... ? */ if (compfunc && (p = check_param(s, 0))) @@ -4382,10 +4538,12 @@ makecomplist(char *s, int incmd, int lst) if (!validlist) lastambig = 0; - amatches = 0; + amatches = NULL; mnum = 0; unambig_mnum = -1; isuf = NULL; + insmnum = insgnum = 1; + insgroup = oldlist = oldins = 0; begcmgroup("default", 0); ccused = newlinklist(); @@ -4399,7 +4557,7 @@ makecomplist(char *s, int incmd, int lst) endcmgroup(NULL); - if (amatches) + if (amatches && !oldlist) amatches->ccs = (Compctl *) makearray(ccused, 0, &(amatches->ccount), NULL); else { @@ -4408,7 +4566,13 @@ makecomplist(char *s, int incmd, int lst) for (n = firstnode(ccused); n; incnode(n)) freecompctl((Compctl) getdata(n)); } + if (oldlist) { + nmatches = onm; + validlist = 1; + amatches = pmatches; + return 0; + } PERMALLOC { permmatches(); } LASTALLOC; @@ -4996,9 +5160,9 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) delit = ispattern = 0; usemenu = um; patcomp = filecomp = NULL; - menucur = NULL; - rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = prpre = - fpre = fsuf = ipre = ripre = prpre = NULL; + rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = + fpre = fsuf = ipre = ripre = prpre = + qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL; curcc = cc; @@ -5106,8 +5270,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) lpre = zhalloc(lpl + 1); memcpy(lpre, s, lpl); lpre[lpl] = '\0'; + qlpre = quotename(lpre, NULL); lsuf = dupstring(s + offs); lsl = strlen(lsuf); + qlsuf = quotename(lsuf, NULL); /* First check for ~.../... */ if (ic == Tilde) { @@ -5126,9 +5292,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) rpre = (*p || *lpre == Tilde || *lpre == Equals) ? (noreal = 0, getreal(tt)) : dupstring(tt); + qrpre = quotename(rpre, NULL); for (p = lsuf; *p && *p != String && *p != Tick; p++); rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf); + qrsuf = quotename(rsuf, NULL); /* Check if word is a pattern. */ @@ -5204,7 +5372,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) lppre = dupstring((char *) (line + wb)); line[cs] = save; if (brbeg && *brbeg) - strcpy(lppre + brpl, lppre + brpl + strlen(brbeg)); + strcpy(lppre + qbrpl, lppre + qbrpl + strlen(brbeg)); if ((p = strrchr(lppre, '/'))) { p[1] = '\0'; lppl = strlen(lppre); @@ -5226,7 +5394,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) lpsuf = dupstring((char *) (line + cs)); line[we] = save; if (brend && *brend) { - char *p = lpsuf + brsl - (cs - wb); + char *p = lpsuf + qbrsl - (cs - wb); strcpy(p, p + strlen(brend)); } @@ -5241,8 +5409,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) /* And get the file prefix. */ fpre = dupstring(((s1 == s || s1 == rpre || ic) && (*s != '/' || cs == wb)) ? s1 : s1 + 1); + qfpre = quotename(fpre, NULL); /* And the suffix. */ fsuf = dupstrpfx(rsuf, s2 - rsuf); + qfsuf = quotename(fsuf, NULL); if (comppatmatch && *comppatmatch && (ispattern & 2)) { int t2; @@ -5506,6 +5676,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) * that things starting with these characters will be added. */ rpre = dyncat((ic == Tilde) ? "~" : "=", rpre); rpl++; + qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre); } if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) { /* If we have to complete commands, add alias names, * @@ -6137,7 +6308,10 @@ permmatches(void) Cmatch *p, *q; Cexpl *ep, *eq, e, o; Compctl *cp, *cq; - int nn, nl, fi = 0; + int nn, nl, fi = 0, gn = 1, mn = 1, rn; + + if (hasperm) + freematches(); amatches = lmatches = NULL; nmatches = smatches = 0; @@ -6179,13 +6353,17 @@ permmatches(void) n->next = amatches; amatches = n; n->prev = 0; + n->num = gn++; n->flags = g->flags; n->mcount = g->mcount; n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) * sizeof(Cmatch)); - for (q = g->matches; *q; q++, p++) + for (rn = 1, q = g->matches; *q; q++, p++, rn) { *p = dupmatch(*q); + (*p)->rnum = rn++; + (*p)->gnum = mn++; + } *p = NULL; n->lcount = g->lcount; @@ -6216,6 +6394,10 @@ permmatches(void) n->ccs = NULL; g = g->next; } + pmatches = amatches; + hasperm = 1; + permmnum = mn - 1; + permgnum = gn - 1; } /* This frees one match. */ @@ -6247,7 +6429,7 @@ freematch(Cmatch m) static void freematches(void) { - Cmgroup g = amatches, n; + Cmgroup g = pmatches, n; Cmatch *m; Cexpl *e; Compctl *c; @@ -6281,24 +6463,27 @@ freematches(void) g = n; } + hasperm = 0; } /* Insert the given string into the command line. If move is non-zero, * * the cursor position is changed and len is the length of the string * - * to insert (if it is -1, the length is calculated here). */ + * to insert (if it is -1, the length is calculated here). * + * The last argument says if we should quote the string. */ /**/ -static void +static int inststrlen(char *str, int move, int len) { if (!len || !str) - return; + return 0; if (len == -1) len = strlen(str); spaceinline(len); strncpy((char *)(line + cs), str, len); if (move) cs += len; + return len; } /* This builds the unambiguous string. If ins is non-zero, it is @@ -6319,10 +6504,11 @@ cline_str(Cline l, int ins, int *csp) * to re-insert. */ if (ins) { if ((hasp = (brbeg && *brbeg))) { - plen = strlen(brbeg); pl = brpl; + plen = strlen(brbeg); pl = (hasunqu ? brpl : qbrpl); } if ((hass = (brend && *brend))) { - slen = strlen(brend); sl = we - wb - brsl - plen - slen + 1; + slen = strlen(brend); + sl = we - wb - (hasunqu ? brsl : qbrsl) - plen - slen + 1; } if (!pl) { inststrlen(brbeg, 1, -1); @@ -6606,6 +6792,7 @@ do_ambiguous(void) * how REC_EXACT takes effect. We effectively turn the ambiguous * * completion into an unambiguous one. */ if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) { + menucur = NULL; do_single(ainfo->exactm); invalidatelist(); return; @@ -6628,6 +6815,8 @@ do_ambiguous(void) int atend = (cs == we), oll = ll, la; VARARR(char, oline, ll); + menucur = NULL; + /* Copy the line buffer to be able to easily test if it changed. */ memcpy(oline, line, ll); @@ -6653,6 +6842,10 @@ do_ambiguous(void) fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) | ((atend && cs != lastend) ? FC_INWORD : 0)); + /* Probably move the cursor to then end. */ + if (movetoend == 3) + cs = lastend; + /* If the LIST_AMBIGUOUS option (meaning roughly `show a list only * * if the completion is completely ambiguous') is set, and some * * prefix was inserted, return now, bypassing the list-displaying * @@ -6674,7 +6867,8 @@ do_ambiguous(void) * if it is needed. */ if (isset(LISTBEEP)) feep(); - if (uselist && usemenu != 2 && !showinglist && + if (uselist && usemenu != 2 && + (!showinglist || (usemenu == 3 && !oldlist)) && (smatches >= 2 || (compforcelist && *compforcelist))) showinglist = -2; } @@ -6721,7 +6915,7 @@ do_single(Cmatch m) /* We are currently not in a menu-completion, * * so set the position variables. */ menupos = wb; - menuwe = (cs == we) || isset(ALWAYSTOEND); + menuwe = (movetoend >= 2 || (movetoend == 1 && !menucmp)); menuend = we; } /* If we are already in a menu-completion or if we have done a * @@ -6770,14 +6964,20 @@ do_single(Cmatch m) * If it is, we append a slash. */ struct stat buf; char *p; + int t = 0; - /* Build the path name. */ - p = (char *) zhalloc(strlen(prpre) + strlen(str) + + if (m->ipre && m->ipre[0] == '~' && !m->ipre[1]) + t = 1; + else { + /* Build the path name. */ + p = (char *) zhalloc(strlen(prpre) + strlen(str) + strlen(psuf) + 3); - sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf); + sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf); - /* And do the stat. */ - if (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode)) { + /* And do the stat. */ + t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode)); + } + if (t) { /* It is a directory, so add the slash. */ havesuff = 1; inststrlen("/", 1, 1); @@ -6822,23 +7022,71 @@ do_single(Cmatch m) if (menuwe && m->ripre && isset(AUTOPARAMKEYS)) makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc); - if (menucmp && !menuwe) + if ((menucmp && !menuwe) || !movetoend) cs = menuend; } +/* This maps the value in v into the range [0,m-1], decrementing v + * if it is non-negative and making negative values cound backwards. */ + +static int +comp_mod(int v, int m) +{ + if (v >= 0) + v--; + if (v >= 0) + return v % m; + else { + while (v < 0) + v += m; + return v; + } +} + /* This handles the beginning of menu-completion. */ /**/ static void do_ambig_menu(void) { - menucmp = 1; - menucur = NULL; - menugrp = amatches; - while (!menugrp->mcount) - menugrp = menugrp->next; - do_single(menugrp->matches[0]); - menucur = menugrp->matches; + Cmatch *mc; + + if (usemenu != 3) { + menucmp = 1; + menucur = NULL; + } else { + if (oldlist) { + if (oldins) + acceptlast(); + } else + menucur = NULL; + } + if (insgroup) { + insgnum = comp_mod(insgnum, permgnum); + for (menugrp = amatches; + menugrp && menugrp->num != insgnum + 1; + menugrp = menugrp->next); + if (!menugrp || !menugrp->mcount) { + menucur = NULL; + return; + } + insmnum = comp_mod(insmnum, menugrp->mcount); + } else { + int c = 0; + + insmnum = comp_mod(insmnum, permmnum); + for (menugrp = amatches; + menugrp && (c += menugrp->mcount) <= insmnum; + menugrp = menugrp->next) + insmnum -= menugrp->mcount; + if (!menugrp) { + menucur = NULL; + return; + } + } + mc = menugrp->matches + insmnum; + do_single(*mc); + menucur = mc; } /* Return the length of the common prefix of s and t. */ @@ -6924,6 +7172,15 @@ printfmt(char *fmt, int n, int dopr) u = 0; m = 1; tcout(TCUNDERLINEEND); break; + case '{': + for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++) + if (dopr) + putc(*p, shout); + if (*p) + p++; + else + p--; + break; } if (m) { if (b) @@ -6971,7 +7228,6 @@ listmatches(void) Cexpl *e; int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0; int of = isset(LISTTYPES), opl = 0; - int listmax = getiparam("LISTMAX"); #ifdef DEBUG /* Sanity check */ @@ -6983,11 +7239,10 @@ listmatches(void) /* Set the cursor below the prompt. */ trashzle(); - showinglist = 0; + showinglist = listshown = 0; clearflag = (isset(USEZLE) && !termflags && - (isset(ALWAYSLASTPROMPT) && zmult == 1)) || - (unset(ALWAYSLASTPROMPT) && zmult != 1); + complastprompt && *complastprompt); for (g = amatches; g; g = g->next) { char **pp = g->ylist; @@ -7066,7 +7321,8 @@ listmatches(void) } /* Maybe we have to ask if the user wants to see the list. */ - if ((listmax && nlist > listmax) || (!listmax && nlines >= lines)) { + if ((complistmax && nlist > complistmax) || + (!complistmax && nlines >= lines)) { int qup; zsetterm(); qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1); @@ -7204,6 +7460,7 @@ listmatches(void) if ((nlines += nlnct - 1) < lines) { tcmultout(TCUP, TCMULTUP, nlines); showinglist = -1; + listshown = 1; } else clearflag = 0, putc('\n', shout); } else diff --git a/Src/glob.c b/Src/glob.c index f8fd2aa4a..0d6d4d778 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -397,6 +397,7 @@ scanner(Complist q) int pbcwdsav = pathbufcwd; int errssofar = errsfound; struct dirsav ds; + char *str; ds.ino = ds.dev = 0; ds.dirname = NULL; @@ -412,17 +413,18 @@ scanner(Complist q) scanner(q->next); } c = q->comp; + str = c->str; /* Now the actual matching for the current path section. */ - if (!(c->next || c->left) && !haswilds(c->str) + if (!(c->next || c->left) && !haswilds(str) && (!((c->stat & (C_LCMATCHUC|C_IGNCASE)) || c->errsmax) - || !strcmp(".", c->str) || !strcmp("..", c->str))) { + || !*str || !strcmp(".", str) || !strcmp("..", str))) { /* * We always need to match . and .. explicitly, even if we're * checking other strings for case-insensitive matches. * * It's a straight string to the end of the path section. */ - int l = strlen(c->str); + int l = strlen(str); if (l + !l + pathpos - pathbufcwd >= PATH_MAX) { int err; @@ -442,14 +444,14 @@ scanner(Complist q) /* Not the last path section. Just add it to the path. */ int oppos = pathpos; - if (!errflag && !(q->closure && !strcmp(c->str, "."))) { - addpath(c->str); + if (!errflag && !(q->closure && !strcmp(str, "."))) { + addpath(str); if (!closure || statfullpath("", NULL, 1)) scanner((q->closure) ? q : q->next); pathbuf[pathpos = oppos] = '\0'; } } else - insert(c->str, 0); + insert(str, 0); } else { /* Do pattern matching on current path section. */ char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : "."; @@ -2282,16 +2284,45 @@ int getmatch(char **sp, char *pat, int fl, int n, char *replstr) { Comp c; - char *s = *sp, *t, *start, sav; - int i, l = strlen(*sp), matched; MUSTUSEHEAP("getmatch"); /* presumably covered by prefork() test */ - repllist = NULL; + c = parsereg(pat); + if (!c) { + zerr("bad pattern: %s", pat, 0); + return 1; + } + return igetmatch(sp, c, fl, n, replstr); +} + +/**/ +void +getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr) +{ + char **arr = *ap, **pp; + Comp c; + + MUSTUSEHEAP("getmatch"); /* presumably covered by prefork() test */ + c = parsereg(pat); if (!c) { zerr("bad pattern: %s", pat, 0); - return 1; + return; } + *ap = pp = ncalloc(sizeof(char *) * (arrlen(arr) + 1)); + while ((*pp = *arr++)) + if (igetmatch(pp, c, fl, n, replstr)) + pp++; +} + +/**/ +static int +igetmatch(char **sp, Comp c, int fl, int n, char *replstr) +{ + char *s = *sp, *t, *start, sav; + int i, l = strlen(*sp), matched; + + repllist = NULL; + if (fl & SUB_ALL) { i = domatch(s, c, 0); *sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0); diff --git a/Src/subst.c b/Src/subst.c index c62fcce6b..e8e22f943 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1361,16 +1361,24 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub) NULL, s[-1]); return NULL; } - singsub(&s); + { + char t = s[-1]; + + singsub(&s); + if (t == '/' && (flags & SUB_SUBSTR)) { + if (*s == '#' || *s == '%') { + flags &= ~SUB_SUBSTR; + if (*s == '%') + flags |= SUB_END; + s++; + } else if (*s == '\\') { + s++; + } + } + } if (!vunset && isarr) { - char **ap = aval; - char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1)); - - while ((*pp = *ap++)) { - if (getmatch(pp, s, flags, flnum, replstr)) - pp++; - } + getmatcharr(&aval, s, flags, flnum, replstr); copied = 1; } else { if (vunset) diff --git a/Src/utils.c b/Src/utils.c index 12b7fc7eb..86679e90f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3177,10 +3177,7 @@ bslashquote(const char *s, char **e, int instring) *v++ = *u; } *v = '\0'; - if (strcmp(buf, s)) - tt = dupstring(buf); - else - tt = s; + tt = dupstring(buf); v += tt - buf; if (e && (sf & 1)) *e += tt - buf; diff --git a/patchlist.txt b/patchlist.txt index 9cefc77ae..c7e12ee06 100644 --- a/patchlist.txt +++ b/patchlist.txt @@ -852,6 +852,8 @@ options (code to use currently unset/set options remains in comments) pws: 5934: option GLOBAL_RCS_FIRST runs /etc/z* files before user's files. +Sven: 5936: replace modifying completion tests with compset + Sven: 5938, 5937: compset to replace modifying conditions Sven: 5940: move cursor @@ -861,3 +863,73 @@ Sven: 5942: spaces in file names with old completion Sven: 5947: completion functions fix pws: unposted: updated .distfiles under Completion + + pws-15 + +Sven: 5955: more compstate choices: list_max, last_prompt, to_end + +Bruce: 5958: _make + +Sven: 5959: quoting characters in completion + +Sven: 5960: $PREBUFFER: lines before current $BUFFER + +pws: 5965: _correct_word + +Sven: 5968: fix brace re-insertion problem in completion + +Sven: 5969: clear to end of display optimization (may need modifying for +some terminals) + +Sven: 5970: completion fix compilation; #defcomp is now #compdef + +Sven: 5971: shell code control over re-using existing completion lists + +Sven: 5972: compconf without arguments lists; _compalso takes extra +arguments + +Sven: 5981: bit masks in comp.h + +Sven: 5982: menu behaviour + +Sven: 5983: documentation for Completion/ system (compsys.1). + +Sven: 5986: compstate[insert] + +Sven: 5995: should fix 5969 + +Sven: 5996: compsys.yo, special contexts + +Sven: 5999: ~foo<TAB> completes with / + +Bart: 6002, 6003: in ${foo/#bar/thing}, the `#' can appear from +substitution and can be quoted + +Sven: 6005: Misc/compctl-examples altered for latest (more consistent) +nested parameter expansion rules + +Sven: 6008: %{ works in completion listings + +Sven: extracted from 6009: chunk for getmatcharr() + +Sven: 6010: _match_pattern and _match_test replaced by options to compadd +(and deleted from distribution) + +Sven: 6011: compadd uses first (not last) occurrence of an option + +Sven: 6013: pass ignored prefix and suffix in _path_files + +Andrej: 6017 (ex 6014): -i and -s options for _long_options + +pws: 6016: compinit and _zftp + +pws: 6018: (#l) and friends with a trailing / on the glob pattern always +failed + +Sven: 6021: _path_files expanding path fix + +Sven: 6026: _path_files slight rewrite for speed + +pws: 6030: compsys.yo + +Sven: 6031: defcomp -> compdef |