diff options
Diffstat (limited to 'Completion')
-rw-r--r-- | Completion/Base/_regex_arguments | 342 | ||||
-rw-r--r-- | Completion/Debian/_apt | 331 | ||||
-rw-r--r-- | Completion/User/_combination | 28 |
3 files changed, 320 insertions, 381 deletions
diff --git a/Completion/Base/_regex_arguments b/Completion/Base/_regex_arguments index 12f4d6d2d..bbf8a2e5b 100644 --- a/Completion/Base/_regex_arguments +++ b/Completion/Base/_regex_arguments @@ -1,9 +1,5 @@ #autoload -## todo - -# imprement `guard' to more generic branch selection. - ## usage: _regex_arguments funcname regex ## configuration key used: @@ -23,118 +19,33 @@ ## regex word definition: -# elt-pattern = "/" ( pattern | "[]" ) # cutoff -# | "%" pattern # non-cutoff -# lookahead = "@" pattern -# parse-action = "-" zsh-code-to-eval -# complete-action = "!" zsh-code-to-eval +# pattern = "/" ( glob | "[]" ) "/" [ "+" | "-" ] +# lookahead = "%" glob "%" +# guard = "-" zsh-code-to-eval +# action = ":" zsh-code-to-eval ## regex word sequence definition: -# element = elt-pattern [ lookahead ] [ parse-action ] [ complete-action ] +# element = pattern [ lookahead ] [ guard ] [ action ] # # regex = element # | "(" regex ")" # | regex "#" # | regex regex # | regex "|" regex -# | void -# | null -# -# NOTE: void and null has no explicit representation. However null can -# be represent with empty words such as \( \). - -# example: (in zsh quoted form) - -# $'[^\0]#\0' \# : zero or more words - -## auxiliary functions definition: - -# nullable : regex -> bool -# first : regex -> list of element -# match : string * list of element -> element + {bottom} -# right : string * element -> string -# left : string * element -> string -# next : regex * element -> regex + {bottom} -# trans : string * string * regex -> (string * string * regex) + {bottom} - -# nullable(void) = false -# nullable(null) = true -# nullable(e) = false -# nullable(r #) = true -# nullable(r1 r2) = nullable(r1) and nullable(r2) -# nullable(r1 | r2) = nullable(r1) or nullable(r2) - -# first(void) = {} -# first(null) = {} -# first(e) = [ e ] -# first(r #) = first(r) -# first(r1 r2) = nullable(r1) ? first(r1) ++ first(r2) : first(r1) -# first(r1 | r2) = first(r1) ++ first(r2) - -# match(s, []) = bottom -# match(s, [e1, e2, ...]) = e if [[ $s = $elt-pattern[e]$lookahead[e]* ]] -# | match(s, [e2, ...]) otherwise - -# right(s, e) = ${s##$elt-pattern[e]} -# left(s, e) = ${(M)s##$elt-pattern[e]} - -### XXX: It can treat lookaheads if zsh provide $1, $2, ... in perl. - -# next(void, e) = bottom -# next(null, e) = bottom -# next(e1, e0) = e1 eq e0 ? null : bottom # eq is test operator of identity equality. -# next(r #, e) = next(r, e) != bottom ? next(r, e) (r #) : bottom -# next(r1 r2, e) = next(r1, e) != bottom ? next(r1, e) r2 : next(r2, e) -# next(r1 | r2, e) = next(r1, e) != bottom ? next(r1, e) : next(r2, e) - -# trans( (t, s, r) ) = ( (cutoff(e) ? '' : t ++ left(s, e)), right(s, e), next(r, e) ) -# where e = match(s, first(r)) - -# NOTE: This `next' definition is slightly different to ordinaly one. -# This definition uses only one element of first(r) for transition -# instead of all elements of first(r). - -# If _regex_arguments takes the regex r0, the first state of the state -# machine is r0. The state of the state machine transit as follows. - -# ('', s0, r0) -> trans('', s0, r0) = (t1, s1, r1) -> trans(t1, s1, r1) -> ... - -# If the state is reached to bottom, the state transition is stopped. - -# ... -> (tN, sN, rN) -> bottom - -# For each transitions (tI, sI, rI) to trans(tI, sI, rI), the state -# machine evaluate parse-action bound to match(sI, first(rI)). - -# In parse-action bound to match(sI, first(rI)) = e, it can refer variables: -# _ra_left : tI+1 -# _ra_match : left(sI, e) -# _ra_right : sI+1 - -# If the state transition is stopped, the state machine evaluate -# complete-actions bound to first(rN) if tN and sN does not contain NUL. -# When complete-actions are evaluated, completion focus is restricted to -# tN ++ sN. (This is reason of tN and sN cannot contain NUL when -# completion.) -# Also, if there are last transitions that does not cut off the string -# (tJ ++ sJ = tJ+1 ++ sJ+1 = ... = tN-1 ++ sN-1 = tN ++ sN), -# complete-actions bound to them -# --- match(sJ, first(rJ)), ..., match(sN-1, first(rN-1)) --- are also -# evaluated before complete-actions bound to first(rN). # example: # compdef _tst tst -# _regex_arguments _tst /$'[^\0]#\0' /$'[^\0]#\0' '!compadd aaa' +# _regex_arguments _tst /$'[^\0]#\0'/ /$'[^\0]#\0'/ :'compadd aaa' # _tst complete `aaa' for first argument. # First $'[^\0]#\0' is required to match with command name. -# _regex_arguments _tst /$'[^\0]#\0' \( /$'[^\0]#\0' '!compadd aaa' /$'[^\0]#\0' !'compadd bbb' \) \# +# _regex_arguments _tst /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'compadd aaa' /$'[^\0]#\0'/ :'compadd bbb' \) \# # _tst complete `aaa' for (2i+1)th argument and `bbb' for (2i)th argument. -# _regex_arguments _tst /$'[^\0]#\0' \( /$'[^\0]#\0' '!compadd aaa' \| /$'[^\0]#\0' !'compadd bbb' \) \# +# _regex_arguments _tst /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'compadd aaa' \| /$'[^\0]#\0'/ :'compadd bbb' \) \# # _tst complete `aaa' or `bbb'. ## Recursive decent regex parser @@ -146,37 +57,42 @@ # 2 : fatal parse error _ra_parse_elt () { - : index=$index "[$regex[$index]]" - local state + local state act if (( $#regex < index )); then return 1 else case "$regex[index]" in - [/%]*) state=$index + /*/([-+]|)) state=$index first=($state) last=($state) nullable= - case "${regex[index][1]}" in - /) cutoff[$state]=yes ;; - %) cutoff[$state]= ;; + case "$regex[index]" in + */+) cutoff[$state]=+;; + */) cutoff[$state]=/;; + */-) cutoff[$state]=-;; esac - pattern[$state]="${regex[index++][2,-1]}" - [[ -n "$pattern[$state]" ]] && pattern[$state]="($pattern[$state])" - if [[ $index -le $#regex && $regex[index] = @* ]]; then - lookahead[$state]="${regex[index++][2,-1]}" - [[ -n "$lookahead[$state]" ]] && lookahead[$state]="($lookahead[$state])" + pattern[$state]="${${regex[index++]#/}%/([-+]|)}" + if [[ $pattern[$state] != "[]" ]]; then + pattern[$state]="(#b)((#B)$pattern[$state])" + fi + if [[ $index -le $#regex && $regex[index] = %*% ]]; then + lookahead[$state]="(#B)${regex[index++][2,-2]}" else lookahead[$state]="" fi if [[ $index -le $#regex && $regex[index] = -* ]]; then - parse_action[$state]="${regex[index++][2,-1]}" + guard[$state]="${regex[index++][2,-1]}" else - parse_action[$state]="" + guard[$state]="" fi - if [[ $index -le $#regex && $regex[index] = \!* ]]; then - complete_action[$state]="${regex[index++][2,-1]}" + if [[ $index -le $#regex && $regex[index] = :* ]]; then + act="${regex[index++][2,-1]}" + action[$state]="$act" + # `actions[$act]="${actions[$act]} $state"' is not work properly + # because $act on lhs is expanded twice. + : ${actions[$act]::="${actions[$act]} $state"} else - complete_action[$state]="" + action[$state]="" fi ;; \() (( index++ )) @@ -193,7 +109,6 @@ _ra_parse_elt () { } _ra_parse_clo () { - : index=$index "[$regex[$index]]" _ra_parse_elt || return $? if (( index <= $#regex )) && [[ "$regex[$index]" = \# ]]; then @@ -207,7 +122,6 @@ _ra_parse_clo () { } _ra_parse_seq () { - : index=$index "[$regex[$index]]" local last_seq local first_seq nullable_seq first_seq=() @@ -248,7 +162,6 @@ _ra_parse_seq () { } _ra_parse_alt () { - : index=$index "[$regex[$index]]" local last_alt local first_alt nullable_alt first_alt=() @@ -286,8 +199,9 @@ _ra_parse_alt () { _ra_gen_func () { local old new - local state next index - local start="${(j/:/)first}" + local state index + local test tmp + local start="0" old=() new=($start) @@ -295,8 +209,8 @@ _ra_gen_func () { print -lr - \ "$funcname () {" \ 'setopt localoptions extendedglob' \ - 'local _ra_state _ra_left _ra_match _ra_right _ra_actions _ra_tmp' \ - "_ra_state='$start'" \ + 'local _ra_state _ra_left _ra_right _ra_actions' \ + "_ra_state=$start" \ '_ra_left=' \ '_ra_right="${(pj:\0:)${(@)words[1,CURRENT - 1]:Q}}"$'\''\0'\''"$PREFIX"' \ '_ra_actions=()' \ @@ -307,99 +221,114 @@ _ra_gen_func () { state="$new[1]" shift new old=("$old[@]" "$state") - print -lr - \ - "$state)" \ - 'case "$_ra_right" in' - - for index in ${(s/:/)state}; do - if [[ "$pattern[$index]" != "([])" ]]; then - next="${(j/:/)${(@)=tbl[$index]}}" - print -lr - \ - "$pattern[$index]$lookahead[$index]*)" - if [[ -n "$pattern[$index]" ]]; then - if [[ -n "$cutoff[$index]" ]]; then - print -lr - \ - '_ra_match="${(M)_ra_right##'"$pattern[$index]"'}"' \ - '_ra_right="$_ra_right[$#_ra_match + 1, -1]"' \ - '_ra_left=' \ - 'if (( $#_ra_match )); then' \ - '_ra_actions=()' - if [[ -n "${complete_action[$index]:q}" ]]; then - print -lr - \ - 'else' \ - '_ra_actions=("$_ra_actions[@]" '"${complete_action[$index]:q}"')' - fi - print -lr - \ - 'fi' - else - print -lr - \ - '_ra_match="${(M)_ra_right##'"$pattern[$index]"'}"' \ - '_ra_right="$_ra_right[$#_ra_match + 1, -1]"' \ - '_ra_left="$_ra_left$_ra_match"' - if [[ -n "${complete_action[$index]:q}" ]]; then - print -lr - \ - '_ra_actions=("$_ra_actions[@]" '"${complete_action[$index]:q}"')' - fi - fi - else - print -lr - \ - '_ra_match=' \ - '_ra_actions=("$_ra_actions[@]" '"${complete_action[$index]:q}"')' - fi - print -lr - \ - "$parse_action[$index]" - if [[ -n $next ]]; then - print -lr - \ - "_ra_state=$next" - (( $old[(I)$next] || $new[(I)$next] )) || new=($next "$new[@]") - else - print -lr - \ - '_message "no arg"' \ - 'break' - fi - print -lr - \ - ';;' - fi - done - + "$state)" + _ra_gen_parse_state print -lr - \ - '*)' \ - 'if [[ "$_ra_left$_ra_right" = *$'\''\0'\''* ]]; then' \ - '_message "parse failed before current word"' \ - 'else' \ - 'compset -p $(( $#PREFIX - $#_ra_right - $#_ra_left ))' + ';;' + done - print -lr - \ - 'for _ra_tmp in $_ra_actions; do' \ - 'eval "$_ra_tmp"' \ - 'done' - for index in ${(s/:/)state}; do - print -lr - \ - "$complete_action[$index]" - done + print -lr - \ + 'esac' \ + 'done' \ + 'while (( $#_ra_actions )); do' \ + 'case "$_ra_actions[1]" in' - print -lr - \ - 'fi' \ - 'break' \ - ';;' \ - 'esac' \ - ';;' + for tmp in "${(@k)actions}"; do + #print -lr - "KEY:{$tmp}" "VAL:{$actions[$tmp]}" >&2 + print -lr - "${(j:);&:)${=actions[$tmp]}})" $tmp ';;' done print -lr - \ 'esac' \ + 'shift _ra_actions' \ 'done' \ '}' } +_ra_gen_parse_state () { + local actions i + test='if' + for index in $=tbl[$state]; do + if [[ "$pattern[$index]" != "[]" ]]; then + if [[ -z "$guard[$index]" ]]; then + print -lr - \ + "$test [[ \$_ra_right = $pattern[$index]$lookahead[$index]* ]]" + else + print -lr - \ + "$test [[ \$_ra_right = $pattern[$index]$lookahead[$index]* ]] && {" \ + "$guard[$index]" \ + "}" + fi + test='elif' + (( $old[(I)$index] || $new[(I)$index] )) || new=($index "$new[@]") + print -lr - \ + "then" \ + "_ra_state=$index" \ + '_ra_right="${_ra_right[mend[1] + 1, -1]}"' + actions=() + for i in $=tbl[$index]; do + if [[ -n $action[$i] ]]; then + actions=($actions $i) + fi + done + case "$cutoff[$index]" in + +) print -lr - \ + '_ra_left="$_ra_left$match[1]"' + if (( $#actions )); then + print -lr - \ + "_ra_actions=($actions \$_ra_actions)" + fi + ;; + /) print -lr - \ + '_ra_left=' + print -lr - \ + 'if (( mend[1] )); then' \ + "_ra_actions=($actions)" + if (( $#actions )); then + print -lr - \ + 'else' \ + "_ra_actions=($actions \$_ra_actions)" + fi + print -lr - \ + 'fi' + ;; + -) print -lr - \ + '_ra_left=' \ + "_ra_actions=($actions)" + ;; + esac + fi + done + + if [[ $test != 'if' ]]; then + # Some branchs are exists. But all of them are failed. + print -lr - \ + 'else' \ + 'if [[ "$_ra_left$_ra_right" = *$'\''\0'\''* ]]; then' \ + '_message "parse failed before current word"' \ + '_ra_actions=()' \ + 'else' \ + 'compset -p $(( $#PREFIX - $#_ra_right - $#_ra_left ))' \ + 'fi' \ + 'break' \ + 'fi' + else + # There are no branch. + print -lr - \ + '_message "no more arguments"' \ + '_ra_actions=()' \ + 'break' + fi +} + _regex_arguments () { setopt localoptions extendedglob local funcname="_regex_arguments_tmp" local funcdef - typeset -A tbl cutoff pattern lookahead parse_action complete_action + typeset -A tbl cutoff pattern lookahead guard action actions local regex index first last nullable local i state next @@ -408,18 +337,16 @@ _regex_arguments () { local cache_test if ! [[ -f "$cache_file" ]] || ! source "$cache_file" "$@"; then - cache_test='[[ $# -eq '$#' && "$*" = '"${*:q}"' ]]' - funcname="$1" - shift - regex=("$@") + regex=("${(@)argv[2,-1]}") index=1 tbl=() pattern=() lookahead=() - parse_action=() - complete_action=() + guard=() + action=() + actions=() _ra_parse_alt if (( $? == 2 || index != $#regex + 1 )); then @@ -431,18 +358,21 @@ _regex_arguments () { return 1 fi - funcdef="$(_ra_gen_func)" + tbl[0]=" $first" unfunction "$funcname" 2>/dev/null - eval "${(F)funcdef}" + funcdef="$(_ra_gen_func)" - [[ -d "$cache_dir" && -w "$cache_dir" ]] && { + if [[ -d "$cache_dir" && -w "$cache_dir" ]]; then print -lr - \ - "if $cache_test; then" \ + 'if [[ $# -eq '$#' && "$*" = '"${*:q}"' ]]; then' \ "$funcdef" \ 'true; else false; fi' > "${cache_file}.$HOST.$$" + source "${cache_file}.$HOST.$$" "$@" mv "${cache_file}.$HOST.$$" "${cache_file}" - } + else + source =(print -lr - "$funcdef") + fi fi } diff --git a/Completion/Debian/_apt b/Completion/Debian/_apt index d73b0df85..f4ed69a45 100644 --- a/Completion/Debian/_apt +++ b/Completion/Debian/_apt @@ -17,7 +17,7 @@ _apt_arguments () { funcname="$1" shift - typeset -A canonicalize options + typeset -A canonicalize num_options local short_hasarg short_bool short_intlevel short_configfile short_arbitem local long_hasarg long_bool long_intlevel long_configfile long_arbitem local comp_hasarg='' @@ -28,11 +28,11 @@ _apt_arguments () { type="${1#*:}" case $type in - bool) options[$opts]=1;; - intlevel) options[$opts]=-1;; - configfile) options[$opts]=1;; - arbitem) options[$opts]=-1;; - *) options[$opts]=1 + bool) num_options[$opts]=1;; + intlevel) num_options[$opts]=-1;; + configfile) num_options[$opts]=1;; + arbitem) num_options[$opts]=-1;; + *) num_options[$opts]=1 comp_hasarg="${comp_hasarg}$opts) $type;;"$'\n' type=hasarg;; esac @@ -82,25 +82,25 @@ _apt_arguments () { comp_short=\ 'if [[ $PREFIX = -'"$short_seq"' ]]; then _apt_consume_short ${PREFIX[2,-1]} - tmp1=(${(M)${(s:,:)${(kj:,:)options[(R)*~0]}}:#-?}) + tmp1=(${(M)${(s:,:)${(kj:,:)num_options[(R)*~0]}}:#-?}) tmp2=(${PREFIX}${^tmp1#-}) _describe -o option tmp1 tmp2 elif [[ -z "$PREFIX" ]]; then - tmp1=(${(M)${(s:,:)${(kj:,:)options[(R)*~0]}}:#-?}) + tmp1=(${(M)${(s:,:)${(kj:,:)num_options[(R)*~0]}}:#-?}) _describe -o option tmp1 fi' comp_long=\ -'tmp1="${(j:|:)${(@)${(@M)${(@s:,:)${(@kj:,:)options[(R)*~0]}}:#--*}#--}}" +'tmp1="${(j:|:)${(@)${(@M)${(@s:,:)${(@kj:,:)num_options[(R)*~0]}}:#--*}#--}}" tmp2=(--${(M)^long_bool:#$~tmp1} --${(M)^long_intlevel:#$~tmp1}) tmp3=(--${(M)^long_hasarg:#$~tmp1} --${(M)^long_configfile:#$~tmp1} --${(M)^long_arbitem:#$~tmp1}) _describe -o option tmp2 -- tmp3 -S= -- bool_prefix -S ""' comp_long_prefix=\ -'tmp1="${(j:|:)${(@)${(@M)${(@s:,:)${(@kj:,:)options[(R)*~0]}}:#--*}#--}}" +'tmp1="${(j:|:)${(@)${(@M)${(@s:,:)${(@kj:,:)num_options[(R)*~0]}}:#--*}#--}}" tmp2=($_ra_left${(M)^long_bool:#$~tmp1} $_ra_left${(M)^long_intlevel:#$~tmp1}) tmp3=($_ra_left${(M)^long_hasarg:#$~tmp1} $_ra_left${(M)^long_configfile:#$~tmp1} $_ra_left${(M)^long_arbitem:#$~tmp1}) -tmp1="${(j:|:)${(@)${(@M)${(@s:,:)${(@kj:,:)options[(R)*~0]}}:#-?}#-}}" +tmp1="${(j:|:)${(@)${(@M)${(@s:,:)${(@kj:,:)num_options[(R)*~0]}}:#-?}#-}}" tmp2=("$tmp2[@]" $_ra_left${(M)^short_bool:#$~tmp1} $_ra_left${(M)^short_intlevel:#$~tmp1}) tmp3=("$tmp3[@]" $_ra_left${(M)^short_hasarg:#$~tmp1} $_ra_left${(M)^short_configfile:#$~tmp1} $_ra_left${(M)^short_arbitem:#$~tmp1}) _describe -o option tmp2 -- tmp3 -S=' @@ -113,203 +113,200 @@ _describe -o option tmp2 -- tmp3 -S=' if (( $#short_hasarg )); then regex_short=("$regex_short[@]" - /"$short_seq(${(j:|:)short_hasarg})(=|)" - -'_apt_consume_short ${_ra_match%=}; current_option=${canonicalize[-${${_ra_match%=}[-1]}]}' - \( /"$word1" !"$comp_hasarg" \| /"$nul" /"$word" !"$comp_hasarg" \) \| + /"$short_seq(${(j:|:)short_hasarg})(=|)"/ + -'_apt_consume_short ${match[1]%=}; current_option=${canonicalize[-${${match[1]%=}[-1]}]}' + \( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)short_hasarg})$nul" - -'_apt_consume_short ${_ra_match[-2]}; current_option=${canonicalize[-${${_ra_match%$nul}[-1]}]}' - /"$word" !"$comp_hasarg" \| - /"(${(j:|:)short_hasarg})=" - -'_apt_consume_short ${_ra_match[-2]}; current_option=${canonicalize[-${${_ra_match%=}[-1]}]}' - \( /"$word1" !"$comp_hasarg" \| /"$nul" /"$word" !"$comp_hasarg" \) \| + /"(${(j:|:)short_hasarg})$nul"/ + -'_apt_consume_short ${match[1][-2]}; current_option=${canonicalize[-${${match[1]%$nul}[-1]}]}' + /"$word"/ :"$comp_hasarg" \| + /"(${(j:|:)short_hasarg})="/ + -'_apt_consume_short ${match[1][-2]}; current_option=${canonicalize[-${${match[1]%=}[-1]}]}' + \( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \| ) fi if (( $#short_bool )); then regex_short=("$regex_short[@]" - /"$short_seq(${(j:|:)short_bool})($nul(${(j:|:)bool})|(${(j:|:)bool})|)$nul" - -"_apt_consume_short \${_ra_match%%($nul(${(j:|:)bool})|(${(j:|:)bool})|)$nul}" \| - /"$short_seq(${(j:|:)short_bool})=" - -"_apt_consume_short \${_ra_match%=}" - \( /"$word1" !"$comp_bool" \| /"$nul" /"$word" !"$comp_bool" \) \| + /"$short_seq(${(j:|:)short_bool})($nul(${(j:|:)bool})|(${(j:|:)bool})|)$nul"/ + -"_apt_consume_short \${match[1]%%($nul(${(j:|:)bool})|(${(j:|:)bool})|)$nul}" \| + /"$short_seq(${(j:|:)short_bool})="/ + -"_apt_consume_short \${match[1]%=}" + \( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)short_bool})=" - -"_apt_consume_short \${_ra_match[-2]}" - \( /"$word1" !"$comp_bool" \| /"$nul" /"$word" !"$comp_bool" \) \| - /"(${(j:|:)short_bool})$nul" - -"_apt_consume_short \${_ra_match[-2]}" - /"((${(j:|:)bool})$nul|)" !"$comp_bool" \| + /"(${(j:|:)short_bool})="/ + -"_apt_consume_short \${match[1][-2]}" + \( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \| + /"(${(j:|:)short_bool})$nul"/ + -"_apt_consume_short \${match[1][-2]}" + /"((${(j:|:)bool})$nul|)"/ :"$comp_bool" \| ) fi if (( $#short_intlevel )); then regex_short=("$regex_short[@]" - /"$short_seq(${(j:|:)short_intlevel})($nul$intlevel|$intlevel|)$nul" - -"_apt_consume_short \${_ra_match%%($nul$intlevel|$intlevel|)$nul}" \| - /"$short_seq(${(j:|:)short_intlevel})=" - -"_apt_consume_short \${_ra_match%=}" - \( /"$word1" !"$comp_intlevel" \| /"$nul" /"$word" !"$comp_intlevel" \) \| + /"$short_seq(${(j:|:)short_intlevel})($nul$intlevel|$intlevel|)$nul"/ + -"_apt_consume_short \${match[1]%%($nul$intlevel|$intlevel|)$nul}" \| + /"$short_seq(${(j:|:)short_intlevel})="/ + -"_apt_consume_short \${match[1]%=}" + \( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)short_intlevel})=" - -"_apt_consume_short \${_ra_match[-2]}" - \( /"$word1" !"$comp_intlevel" \| /"$nul" /"$word" !"$comp_intlevel" \) \| - /"(${(j:|:)short_intlevel})$nul" - -"_apt_consume_short \${_ra_match[-2]}" - /"($intlevel$nul|)" !"$comp_intlevel" \| + /"(${(j:|:)short_intlevel})="/ + -"_apt_consume_short \${match[1][-2]}" + \( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \| + /"(${(j:|:)short_intlevel})$nul"/ + -"_apt_consume_short \${match[1][-2]}" + /"($intlevel$nul|)"/ :"$comp_intlevel" \| ) fi if (( $#short_configfile )); then regex_short=("$regex_short[@]" - /"$short_seq(${(j:|:)short_configfile})(=|)" - -"_apt_consume_short \${_ra_match%=}" - \( /"$word1" !"$comp_configfile" \| /"$nul" /"$word" !"$comp_configfile" \) \| + /"$short_seq(${(j:|:)short_configfile})(=|)"/ + -"_apt_consume_short \${match[1]%=}" + \( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)short_configfile})$nul" - -"_apt_consume_short \${_ra_match[-2]}" - /"$word" !"$comp_configfile" \| - /"(${(j:|:)short_configfile})=" - -"_apt_consume_short \${_ra_match[-2]}" - \( /"$word1" !"$comp_configfile" \| /"$nul" /"$word" !"$comp_configfile" \) \| + /"(${(j:|:)short_configfile})$nul"/ + -"_apt_consume_short \${match[1][-2]}" + /"$word"/ :"$comp_configfile" \| + /"(${(j:|:)short_configfile})="/ + -"_apt_consume_short \${match[1][-2]}" + \( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \| ) fi if (( $#short_arbitem )); then regex_short=("$regex_short[@]" - /"$short_seq(${(j:|:)short_arbitem})(=|)" - -"_apt_consume_short \${_ra_match%=}" - \( /"$word1" !"$comp_arbitem" \| /"$nul" /"$word" !"$comp_arbitem" \) \| + /"$short_seq(${(j:|:)short_arbitem})(=|)"/ + -"_apt_consume_short \${match[1]%=}" + \( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)short_arbitem})$nul" - -"_apt_consume_short \${_ra_match[-2]}" - /"$word" !"$comp_arbitem" \| - /"(${(j:|:)short_arbitem})=" - -"_apt_consume_short \${_ra_match[-2]}" - \( /"$word1" !"$comp_arbitem" \| /"$nul" /"$word" !"$comp_arbitem" \) \| + /"(${(j:|:)short_arbitem})$nul"/ + -"_apt_consume_short \${match[1][-2]}" + /"$word"/ :"$comp_arbitem" \| + /"(${(j:|:)short_arbitem})="/ + -"_apt_consume_short \${match[1][-2]}" + \( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \| ) fi if (( $#long_hasarg )); then regex_long=("$regex_long[@]" - /"(${(j:|:)long_hasarg})$nul" - -"_apt_consume_long \${_ra_match%$nul}; current_option=\${canonicalize[--\${_ra_match%$nul}]}" - /"$word" !"$comp_hasarg" \| - /"(${(j:|:)long_hasarg})=" - -"_apt_consume_long \${_ra_match%=}; current_option=\${canonicalize[--\${_ra_match%=}]}" - \( /"$word1" !"$comp_hasarg" \| /"$nul" /"$word" !"$comp_hasarg" \) \| + /"(${(j:|:)long_hasarg})$nul"/ + -"_apt_consume_long \${match[1]%$nul}; current_option=\${canonicalize[--\${match[1]%$nul}]}" + /"$word"/ :"$comp_hasarg" \| + /"(${(j:|:)long_hasarg})="/ + -"_apt_consume_long \${match[1]%=}; current_option=\${canonicalize[--\${match[1]%=}]}" + \( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)long_hasarg})$nul" - -"_apt_consume_long \${_ra_match%$nul}; current_option=\${canonicalize[--\${_ra_match%$nul}]}" - /"$word" !"$comp_hasarg" \| - /"(${(j:|:)long_hasarg})=" - -"_apt_consume_long \${_ra_match%=}; current_option=\${canonicalize[--\${_ra_match%=}]}" - \( /"$word1" !"$comp_hasarg" \| /"$nul" /"$word" !"$comp_hasarg" \) \| + /"(${(j:|:)long_hasarg})$nul"/ + -"_apt_consume_long \${match[1]%$nul}; current_option=\${canonicalize[--\${match[1]%$nul}]}" + /"$word"/ :"$comp_hasarg" \| + /"(${(j:|:)long_hasarg})="/ + -"_apt_consume_long \${match[1]%=}; current_option=\${canonicalize[--\${match[1]%=}]}" + \( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \| ) fi if (( $#long_bool )); then regex_long=("$regex_long[@]" - /"(${(j:|:)long_bool})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_bool" \| /"$nul" /"$word" !"$comp_bool" \) \| - /"(${(j:|:)long_bool})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"((${(j:|:)bool})$nul|)" !"$comp_bool" \| + /"(${(j:|:)long_bool})="/ + -"_apt_consume_long xxx \${match[1]%=}" + \( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \| + /"(${(j:|:)long_bool})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"((${(j:|:)bool})$nul|)"/ :"$comp_bool" \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)long_bool})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_bool" \| /"$nul" /"$word" !"$comp_bool" \) \| - /"(${(j:|:)long_bool})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"((${(j:|:)bool})$nul|)" !"$comp_bool" \| + /"(${(j:|:)long_bool})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \| + /"(${(j:|:)long_bool})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"((${(j:|:)bool})$nul|)"/ :"$comp_bool" \| ) fi if (( $#long_intlevel )); then regex_long=("$regex_long[@]" - /"(${(j:|:)long_intlevel})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_intlevel" \| /"$nul" /"$word" !"$comp_intlevel" \) \| - /"(${(j:|:)long_intlevel})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"($intlevel$nul|)" !"$comp_intlevel" \| + /"(${(j:|:)long_intlevel})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \| + /"(${(j:|:)long_intlevel})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"($intlevel$nul|)"/ :"$comp_intlevel" \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)long_intlevel})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"$intlevel" !"$comp_intlevel" /"$nul" \| - /"(${(j:|:)long_intlevel})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_intlevel" \| /"$nul" /"$word" !"$comp_intlevel" \) \| - /"(${(j:|:)long_intlevel})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"($intlevel$nul|)" !"$comp_intlevel" \| + /"(${(j:|:)long_intlevel})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \| + /"(${(j:|:)long_intlevel})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"($intlevel$nul|)"/ :"$comp_intlevel" \| ) fi if (( $#long_configfile )); then regex_long=("$regex_long[@]" - /"(${(j:|:)long_configfile})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"$word" !"$comp_configfile" \| - /"(${(j:|:)long_configfile})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_configfile" \| /"$nul" /"$word" !"$comp_configfile" \) \| + /"(${(j:|:)long_configfile})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"$word"/ :"$comp_configfile" \| + /"(${(j:|:)long_configfile})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)long_configfile})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"$word" !"$comp_configfile" \| - /"(${(j:|:)long_configfile})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_configfile" \| /"$nul" /"$word" !"$comp_configfile" \) \| + /"(${(j:|:)long_configfile})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"$word"/ :"$comp_configfile" \| + /"(${(j:|:)long_configfile})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \| ) fi if (( $#long_arbitem )); then regex_long=("$regex_long[@]" - /"(${(j:|:)long_arbitem})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"$word" !"$comp_arbitem" \| - /"(${(j:|:)long_arbitem})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_arbitem" \| /"$nul" /"$word" !"$comp_arbitem" \) \| + /"(${(j:|:)long_arbitem})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"$word"/ :"$comp_arbitem" \| + /"(${(j:|:)long_arbitem})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \| ) regex_long_prefix=("$regex_long_prefix[@]" - /"(${(j:|:)long_arbitem})$nul" - -"_apt_consume_long \${_ra_match%$nul}" - /"$word" !"$comp_arbitem" \| - /"(${(j:|:)long_arbitem})=" - -"_apt_consume_long \${_ra_match%=}" - \( /"$word1" !"$comp_arbitem" \| /"$nul" /"$word" !"$comp_arbitem" \) \| + /"(${(j:|:)long_arbitem})$nul"/ + -"_apt_consume_long \${match[1]%$nul}" + /"$word"/ :"$comp_arbitem"/ \| + /"(${(j:|:)long_arbitem})="/ + -"_apt_consume_long \${match[1]%=}" + \( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \| ) fi regex_all=( - /"$word" - \( %-- \( "$regex_long[@]" - %"(${(j:|:)bool})-" - \( "$regex_long_prefix[@]" /"[]" !"$comp_long_prefix" \) \| - /"[]" !"$comp_long" \) \| - %- \( "$regex_short[@]" /"[]" !"$comp_short; $comp_long" \) \| - /"[]" !"$comp_opt" \) \# + /"$word"/ + \( /--/+ \( "$regex_long[@]" + /"(${(j:|:)bool})-"/+ + \( "$regex_long_prefix[@]" + /"[]"/ :"$comp_long_prefix" \) \) \| + /-/+ \( "$regex_short[@]" /"[]"/ \) \| + /"[]"/ :"$comp_opt" \) \# "$regex_all[@]" ) _regex_arguments "${funcname}_sm" "$regex_all[@]" eval "$funcname () { - typeset -A canonicalize options + typeset -A canonicalize num_options canonicalize=(${(kv)canonicalize}) - options=(${(kv)options}) + num_options=(${(kv)num_options}) local short_hasarg short_bool short_intlevel short_configfile short_arbitem local long_hasarg long_bool long_intlevel long_configfile long_arbitem @@ -341,14 +338,16 @@ _apt_consume_short () { local short opt for short in ${(s::)1}; do opt="$canonicalize[-$short]" - (( 0 < options[$opt] && options[$opt]-- )) + (( 0 < num_options[$opt] && num_options[$opt]-- )) done + return 0 } _apt_consume_long () { local long opt opt="$canonicalize[--$1]" - (( 0 < options[$opt] && options[$opt]-- )) + (( 0 < num_options[$opt] && num_options[$opt]-- )) + return 0 } _apt-get () { @@ -373,18 +372,18 @@ _apt-get () { -c,--config-file:configfile \ -o,--option:arbitem \ -- \ - /$'update\0' \| \ - /$'upgrade\0' \| \ - /$'install\0' /$'[^\0]#\0' !'_deb_packages uninstalled "$expl_packages[@]" || _deb_packages installed "$expl_packages[@]" ' \# \| \ - /$'remove\0' /$'[^\0]#\0' !'_deb_packages installed "$expl_packages[@]"' \# \| \ - /$'dist-upgrade\0' \| \ - /$'dselect-upgrade\0' \| \ - /$'clean\0' \| \ - /$'autoclean\0' \| \ - /$'check\0' \| \ - /$'source\0' /$'[^\0]#\0' !'_deb_packages avail "$expl_packages[@]"' \# \| \ - /$'help\0' \| \ - /"[]" !'compadd "$expl_action[@]" update upgrade install remove dist-upgrade dselect-upgrade clean autoclean check source help' + /$'update\0'/ \| \ + /$'upgrade\0'/ \| \ + /$'install\0'/ /$'[^\0]#\0'/ :'_deb_packages uninstalled "$expl_packages[@]" || _deb_packages installed "$expl_packages[@]" ' \# \| \ + /$'remove\0'/ /$'[^\0]#\0'/ :'_deb_packages installed "$expl_packages[@]"' \# \| \ + /$'dist-upgrade\0'/ \| \ + /$'dselect-upgrade\0'/ \| \ + /$'clean\0'/ \| \ + /$'autoclean\0'/ \| \ + /$'check\0'/ \| \ + /$'source\0'/ /$'[^\0]#\0'/ :'_deb_packages avail "$expl_packages[@]"' \# \| \ + /$'help\0/' \| \ + /"[]"/ :'compadd "$expl_action[@]" update upgrade install remove dist-upgrade dselect-upgrade clean autoclean check source help' _apt-get () { local expl_action expl_packages @@ -410,19 +409,19 @@ _apt-cache () { -c,--config-file:configfile \ -o,--option:arbitem \ -- \ - /$'help\0' \| \ - /$'add\0' /$'[^\0]#\0' !'_files' \# \| \ - /$'gencaches\0' \| \ - /$'showpkg\0' /$'[^\0]#\0' !'_deb_packages avail "$expl_packages[@]"' \# \| \ - /$'stats\0' \| \ - /$'dump\0' \| \ - /$'dumpavail\0' \| \ - /$'unmet\0' \| \ - /$'check\0' \| \ - /$'search\0' \| \ - /$'show\0' \| \ - /$'depends\0' \| \ - /"[]" !'compadd "$expl_action[@]" help add gencaches showpkg stats dump dumpavail unmet check search show depends' + /$'help\0'/ \| \ + /$'add\0'/ /$'[^\0]#\0'/ :'_files' \# \| \ + /$'gencaches\0'/ \| \ + /$'showpkg\0'/ /$'[^\0]#\0'/ :'_deb_packages avail "$expl_packages[@]"' \# \| \ + /$'stats\0'=$status[4]/ \| \ + /$'dump\0'/ \| \ + /$'dumpavail\0'/ \| \ + /$'unmet\0'/ \| \ + /$'check\0'/ \| \ + /$'search\0'/ \| \ + /$'show\0'/ \| \ + /$'depends\0'/ \| \ + /"[]"/ :'compadd "$expl_action[@]" help add gencaches showpkg stats dump dumpavail unmet check search show depends' _apt-cache () { local expl_action expl_packages expl_pkg_cache expl_src_cache @@ -450,8 +449,8 @@ _apt-cdrom () { -c,--config-file:configfile \ -o,--option:arbitem \ -- \ - /$'add\0' \| \ - /"[]" !'compadd "$expl_action[@]" add' + /$'add\0'/ \| \ + /"[]"/ :'compadd "$expl_action[@]" add' _apt-cdrom () { local expl_action expl_mount_point @@ -471,13 +470,13 @@ _apt-config () { -c,--config-file:configfile \ -o,--option:arbitem \ -- \ - /$'shell\0' \ + /$'shell\0'/ \ \( \ - /$'[^\0]#\0' !'compgen "$expl_shell_var[@]" -v' \ - /$'[^\0]#\0' !'compadd "$expl_config_key[@]" - ${${(f)"$(apt-config dump 2>&1)"}% *}' \ + /$'[^\0]#\0'/ :'compgen "$expl_shell_var[@]" -v' \ + /$'[^\0]#\0'/ :'compadd "$expl_config_key[@]" - ${${(f)"$(apt-config dump 2>&1)"}% *}' \ \) \# \| \ - /$'dump\0' \| \ - /"[]" !'compadd "$expl_action[@]" shell dump' + /$'dump\0'/ \| \ + /"[]"/ :'compadd "$expl_action[@]" shell dump' _apt-config () { local expl_action expl_shell_var expl_config_key diff --git a/Completion/User/_combination b/Completion/User/_combination index a122bd86f..631547311 100644 --- a/Completion/User/_combination +++ b/Completion/User/_combination @@ -1,9 +1,9 @@ #autoload # Usage: -# _combination [-s SEP] VARIABLE KEYi=PATi KEYj=PATj ... KEYm=PATm KEY EXPL... +# _combination [-s S] V[:K1:...] Ki1[:Ni1]=Pi1 Ki2[:Ni2]=Pi2 ... Kim[:Nim]=Pim Kj[:Nj] EXPL... # -# VARIABLE must be formd as PREFIX_KEY1_..._KEYn. +# It is assumed that V is formed as PRE_K1_..._Kn if `:K1:...' is not specified. # # Example: telnet # @@ -48,7 +48,7 @@ # the port argument if they are exist. And if it is failed, `_users' is # called. -local sep var keys pats key tmp +local sep var keys pats key num tmp if [[ "$1" = -s ]]; then sep="$2" @@ -60,21 +60,31 @@ fi var=$1 shift -keys=( "${(@s:_:)${var#*_}}" ) +if [[ $var = *:* ]]; then + keys=( ${(s/:/)var} ) + shift keys + var="${var%%:*}" +else + keys=( "${(@s:_:)${var#*_}}" ) +fi pats=( "${(@)keys/*/*}" ) while [[ "$1" = *=* ]]; do - pats[$keys[(i)${1%%\=*}]]="${1#*\=}" + tmp="${1%%\=*}" + key="${tmp%:*}" + num="${${tmp##*:}:-1}" + pats[$keys[(in:num:)$key]]="${1#*\=}" shift done -key="$1" +key="${1%:*}" +num="${${1##*:}:-1}" shift if (( ${(P)+${var}} )); then - eval "tmp=( \"\${(@M)${var}:#\${(j!$sep!)~pats}}\" )" - if (( keys[(i)$key] != 1 )); then - eval "tmp=( \${tmp#\${(j!${sep}!)~\${(@)\${(@)keys[2,(r)\$key]}/*/*}}$sep} )" + eval "tmp=( \"\${(@M)${var}:#\${(j($sep))~pats}}\" )" + if (( keys[(in:num:)$key] != 1 )); then + eval "tmp=( \${tmp#\${(j(${sep}))~\${(@)\${(@)keys[2,(rn:num:)\$key]}/*/*}}$sep} )" fi tmp=( ${tmp%%$sep*} ) |