diff options
-rw-r--r-- | Completion/Core/_description | 23 | ||||
-rw-r--r-- | Completion/Core/_files | 67 | ||||
-rw-r--r-- | Completion/Core/_multi_parts | 28 | ||||
-rw-r--r-- | Completion/Core/_path_files | 314 | ||||
-rw-r--r-- | Completion/Core/_sep_parts | 23 | ||||
-rw-r--r-- | Doc/Zsh/mod_zutil.yo | 24 | ||||
-rw-r--r-- | Src/Modules/zutil.c | 139 | ||||
-rw-r--r-- | Src/Modules/zutil.mdd | 2 |
8 files changed, 378 insertions, 242 deletions
diff --git a/Completion/Core/_description b/Completion/Core/_description index b35889c4d..06b54e807 100644 --- a/Completion/Core/_description +++ b/Completion/Core/_description @@ -1,14 +1,11 @@ #autoload -local name gropt format gname hidden hide match ign +local name gropt=-J format gname hidden hide match opts -gropt=(-J) -hide=() -match=() -ign=() +opts=() if [[ "$1" = -([12]|)[VJ] ]]; then - gropt=("$1") + gropt="$1" shift fi @@ -24,14 +21,14 @@ zstyle -s ":completion:${curcontext}:$1" format format || zstyle -s ":completion:${curcontext}:$1" hidden hidden if [[ "$hidden" = (all|yes|true|1|on) ]]; then [[ "$hidden" = all ]] && format='' - hide=(-n) + opts=(-n) fi zstyle -s ":completion:${curcontext}:$1" group-name gname && [[ -z "$gname" ]] && gname="$1" zstyle -s ":completion:${curcontext}:$1" matcher match && - match=(-M "${(q)match}") + opts=($opts -M "${(q)match}") if zstyle -a ":completion:${curcontext}:$1" ignored-patterns _comp_ignore; then - ign=(-F _comp_ignore) + opts=( $opts -F _comp_ignore) else _comp_ignore=() fi @@ -41,15 +38,15 @@ shift 2 if [[ -n "$gname" ]]; then if [[ -n "$format" ]]; then - eval "${name}=($hide $match $ign $gropt ${(q)gname} -X \"${format}\")" + eval "${name}=($opts $gropt ${(q)gname} -X \"${format}\")" else - eval "${name}=($hide $match $ign $gropt ${(q)gname})" + eval "${name}=($opts $gropt ${(q)gname})" fi else if [[ -n "$format" ]]; then - eval "${name}=($hide $match $ign $gropt -default- -X \"${format}\")" + eval "${name}=($opts $gropt -default- -X \"${format}\")" else - eval "${name}=($hide $match $ign $gropt -default-)" + eval "${name}=($opts $gropt -default-)" fi fi diff --git a/Completion/Core/_files b/Completion/Core/_files index cd337713a..f02ac7c38 100644 --- a/Completion/Core/_files +++ b/Completion/Core/_files @@ -3,24 +3,21 @@ local opts opt type=file glob group gopts dopts aopts tmp _file_pat_checked=yes local hasign ign -opts=() -group=() -gopts=() -dopts=(-/) -aopts=(-f) -ign=() -while getopts "P:S:qr:R:W:F:J:V:X:f/g:M:12n" opt; do - case "$opt" in - /) type="${type}dir" ;; - g) type="${type}glob"; gopts=(-g "$OPTARG") ;; - [qn12]) opts=("$opts[@]" "-$opt" ) ;; - [JV]) group=( "-$opt" "$OPTARG") ;; - F) opts=("$opts[@]" "-$opt" "$OPTARG"); hasign=yes ;; - [^f]) opts=("$opts[@]" "-$opt" "$OPTARG") ;; - esac -done +zparseopts \ + /tmp ftmp g+tmp \ + qopts nopts 1opts 2opts P:opts S:opts r:opts R:opts W:opts X:opts M:opts \ + F:opts J:group V:group + +type="${(@j::M)${(@)tmp#-}#?}" +[[ -n "$type" ]] || type=f +if (( $tmp[(I)-g*] )); then + gopts=( -g ${(j: :)${(M)tmp:#-g*}#-g} ) +else + gopts=() +fi +(( $opts[(I)-F*] )) && hasign=yes -if [[ "$group[2]" = files ]]; then +if [[ "$group[1]" = -?files ]]; then opts=("$opts[@]" "$group[@]") group=() fi @@ -32,36 +29,36 @@ fi if zstyle -s ":completion:${curcontext}:directories" file-patterns tmp && [[ -n "$tmp" ]]; then dopts=(-g "$tmp") - if [[ "$type" = (*dir*glob*|*glob*dir*) ]]; then - type=glob - elif [[ "$type" != *(dir|glob)* ]]; then - type="${type}dir" + if [[ "$type" = (*/*g*|*g*/*) ]]; then + type=g + elif [[ "$type" != *[/g]* ]]; then + type="${type}/" fi fi if zstyle -s ":completion:${curcontext}:globbed-files" file-patterns tmp && [[ -n "$tmp" ]]; then gopts=(-g "$tmp") - if [[ "$type" != (*dir*glob*|*glob*dir*) ]]; then - if [[ "$type" = *(dir|glob)* ]]; then - type=glob + if [[ "$type" != (*/*g*|*g*/*) ]]; then + if [[ "$type" = *[g/]* ]]; then + type=g else - type=globall + type=ga fi fi fi case "$type" in -*dir*glob*|*glob*dir*) _tags globbed-files all-files ;; -*all*glob*|*glob*all*) _tags globbed-files all-files ;; -*glob*) _tags globbed-files directories all-files ;; -*dir*) _tags directories all-files ;; -*) _tags all-files ;; +*/*g*|*g*/*) _tags globbed-files all-files ;; +*a*g*|*g*a*) _tags globbed-files all-files ;; +*g*) _tags globbed-files directories all-files ;; +*/*) _tags directories all-files ;; +*) _tags all-files ;; esac while _tags; do if _requested all-files; then if (( $#group )); then - group[2]=all-files + group[1]="${group[1][1,2]}all-files" _setup all-files [[ -z "$hasign" ]] && zstyle -a ":completion:${curcontext}:all-files" ignored-patterns _comp_ignore && @@ -72,7 +69,7 @@ while _tags; do elif _requested directories; then if _requested globbed-files; then if (( $#group )); then - group[2]=globbed-files + group[1]="${group[1][1,2]}globbed-files" _setup globbed-files [[ -z "$hasign" ]] && zstyle -a ":completion:${curcontext}:all-files" ignored-patterns _comp_ignore && @@ -81,7 +78,7 @@ while _tags; do _path_files "$opts[@]" "$ign[@]" "$dopts[@]" "$gopts[@]" && return 0 else if (( $#group )); then - group[2]=directories + group[1]="${group[1][1,2]}directories" _setup directories [[ -z "$hasign" ]] && zstyle -a ":completion:${curcontext}:all-files" ignored-patterns _comp_ignore && @@ -91,13 +88,13 @@ while _tags; do fi elif _requested globbed-files; then if (( $#group )); then - group[2]=globbed-files + group[1]="${group[1][1,2]}globbed-files" _setup globbed-files [[ -z "$hasign" ]] && zstyle -a ":completion:${curcontext}:all-files" ignored-patterns _comp_ignore && ign=(-F _comp_ignore) fi - if [[ "$type" = (*dir*glob*|*glob*dir*) ]]; then + if [[ "$type" = (*/*g*|*g*/*) ]]; then _path_files "$opts[@]" "$ign[@]" "$dopts[@]" "$gopts[@]" && return 0 else _path_files "$opts[@]" "$ign[@]" "$gopts[@]" && return 0 diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts index e64e2906f..61bd3acaf 100644 --- a/Completion/Core/_multi_parts +++ b/Completion/Core/_multi_parts @@ -13,22 +13,18 @@ typeset -U tmp2 # Get the options. -group=() -expl=() -opts=() -sopts=() -while getopts "J:V:X:P:F:S:r:R:qM:12n" opt; do - case "$opt" in - [JV]) group=("-$opt" "$OPTARG");; - X) expl=(-X "$OPTARG");; - [PF]) opts=( "$opts[@]" "-$opt" "$OPTARG") - sopts=( "$sopts[@]" "-$opt" "$OPTARG");; - [SrR]) sopts=( "$sopts[@]" -P "$OPTARG");; - [q12n]) sopts=( "$sopts[@]" "-$opt");; - M) match="$OPTARG";; - esac -done -shift OPTIND-1 +zparseopts -D \ + J:group V:group X:expl \ + P:opts F:opts \ + S:sopts r:sopts R:sopts qsopts 1sopts 2sopts nsopts \ + M:match + +sopts=( "$sopts[@]" "$opts[@]" ) +if (( $#match )); then + match="${match[1][3,-1]}" +else + match='' +fi # Get the arguments, first the separator, then the array. The array is # stored in `matches'. Further on this array will always contain those diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files index 3454654c7..2e226eaf3 100644 --- a/Completion/Core/_path_files +++ b/Completion/Core/_path_files @@ -3,9 +3,9 @@ # Utility function for in-path completion. This allows `/u/l/b<TAB>' # to complete to `/usr/local/bin'. -local linepath realpath donepath prepath testpath exppath skips +local linepath realpath donepath prepath testpath exppath skips skipped local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre -local pats haspats=no ignore group expl addpfx addsfx remsfx rem remt +local pats haspats=no ignore pfxsfx rem remt sopt gopt opt local nm=$compstate[nmatches] menu mspec matcher mopts atmp sort match typeset -U prepaths exppaths @@ -13,73 +13,50 @@ typeset -U prepaths exppaths setopt localoptions nullglob rcexpandparam unsetopt markdirs globsubst shwordsplit nounset -local sopt='-' gopt='' opt exppaths=() -prepaths=('') -ignore=() -group=() -pats=() -addpfx=() -addsfx=() -remsfx=() -expl=() -matcher=() -mopts=() # Get the options. -while getopts "P:S:qr:R:W:F:J:V:X:f/g:M:12n" opt; do - case "$opt" in - [12n]) mopts=( "$mopts[@]" "-$opt" ) - ;; - P) addpfx=(-P "$OPTARG") - ;; - S) addsfx=(-S "$OPTARG") - ;; - q) tmp1=yes - ;; - [rR]) remsfx=("-$opt" "$OPTARG") - ;; - W) tmp1="$OPTARG" - if [[ "$tmp1[1]" = '(' ]]; then - prepaths=( ${^=tmp1[2,-2]%/}/ ) - elif [[ "$tmp1[1]" = '/' ]]; then - prepaths=( "$tmp1/" ) - else - # In this case the variable should be an array, so - # don't use an extra ${=...}. - prepaths=( ${(P)^tmp1%/}/ ) - (( ! $#prepaths )) && prepaths=( ${tmp1%/}/ ) - fi - (( ! $#prepaths )) && prepaths=( '' ) - ;; - F) tmp1="$OPTARG" - if [[ "$tmp1[1]" = '(' ]]; then - ignore=( ${=tmp1[2,-2]} ) - else - ignore=( ${(P)tmp1} ) - fi - ;; - [JV]) group=("-$opt" "$OPTARG") - ;; - X) expl=(-X "$OPTARG") - ;; - f) sopt="${sopt}f" - pats=("$pats[@]" '*') - ;; - /) sopt="${sopt}/" - pats=("$pats[@]" '*(-/)') - haspats=yes - ;; - g) gopt='-g' - pats=("$pats[@]" ${=OPTARG}) - haspats=yes - ;; - M) mspec="$OPTARG" - matcher=(-M "$OPTARG") - ;; - esac -done +zparseopts \ + P:pfxsfx S:pfxsfx qpfxsfx r:pfxsfx R:pfxsfx \ + W:prepaths F:ignore M+matcher \ + J:mopts V:mopts X:mopts 1:mopts 2:mopts n:mopts \ + ftmp1 /tmp1 g+tmp1 + +sopt="-${(@j::M)${(@)tmp1#-}#?}" +(( $tmp1[(I)-[/g]*] )) && haspats=yes +(( $tmp1[(I)-g*] )) && gopt=yes +if (( $tmp1[(I)-/] )); then + pats=( '*(-/)' ${=${(M)tmp1:#-g*}#-g} ) +else + pats=( "${(@)=${(@M)tmp1:#-g*}#-g}" ) +fi + +if (( $#prepaths )); then + tmp1="${prepaths[1][3,-1]}" + if [[ "$tmp1[1]" = '(' ]]; then + prepaths=( ${^=tmp1[2,-2]%/}/ ) + elif [[ "$tmp1[1]" = '/' ]]; then + prepaths=( "$tmp1/" ) + else + prepaths=( ${(P)^tmp1%/}/ ) + (( ! $#prepaths )) && prepaths=( ${tmp1%/}/ ) + fi + (( ! $#prepaths )) && prepaths=( '' ) +else + prepaths=( '' ) +fi + +if (( $#ignore )); then + tmp1="${ignore[1][3,-1]}" + if [[ "$tmp1[1]" = '(' ]]; then + ignore=( ${=tmp1[2,-2]} ) + else + ignore=( ${(P)tmp1} ) + fi +fi + +(( $#matcher )) && mspec="${matcher[1][3,-1]}" if [[ -z "$_file_pat_checked" ]] && zstyle -s ":completion:${curcontext}:files" file-patterns tmp1 && @@ -88,20 +65,34 @@ if [[ -z "$_file_pat_checked" ]] && gopt='' sopt=-/ else - gopt='-g' + gopt=yes sopt=- fi pats=( $=tmp1 ) haspats=yes fi -if (( ! ( $#group + $#expl ) )); then +# If we were given no file selection option, we behave as if we were given +# a `-f'. + +if [[ "$sopt" = -(f|) ]]; then + if [[ -z "$gopt" ]]; then + sopt='-f' + pats=('*') + else + unset sopt + fi +fi + +if (( ! $mopts[(I)-[JVX]*] )); then + local expl + if [[ -z "$gopt" && "$sopt" = -/ ]]; then _description directories expl directory else _description files expl file fi - tmp1=$expl[(I)-M] + tmp1=$expl[(I)-M*] if (( tmp1 )); then mspec="$mspec $expl[1+tmp1]" if (( $#matcher )); then @@ -110,20 +101,7 @@ if (( ! ( $#group + $#expl ) )); then matcher=(-M "$expl[1+tmp1]") fi fi -fi - -[[ -n "$tmp1" && $#addsfx -ne 0 ]] && addsfx[1]=-qS - -# If we were given no file selection option, we behave as if we were given -# a `-f'. - -if [[ "$sopt" = - ]]; then - if [[ -z "$gopt" ]]; then - sopt='-f' - pats=('*') - else - unset sopt - fi + mopts=( "$mopts[@]" "$expl[@]" ) fi if zstyle -s ":completion:${curcontext}:files" sort tmp1; then @@ -140,8 +118,7 @@ if zstyle -s ":completion:${curcontext}:files" sort tmp1; then if [[ "$sort" = on ]]; then sort='' else - group=( "${(@)group/#-J/-V}" ) - expl=( "${(@)expl/#-J/-V}" ) + mopts=( "${(@)mopts/#-J/-V}" ) tmp2=() for tmp1 in "$pats[@]"; do @@ -157,9 +134,15 @@ if zstyle -s ":completion:${curcontext}:files" sort tmp1; then fi fi -# Skip over sequences of slashes. +# Check if we have to skip over sequences of slashes. The value of $skips +# is used below to match the pathname components we always have to accept +# immediatly. -zstyle -t ":completion:${curcontext}:paths" squeeze-slashes && skips=yes +if zstyle -t ":completion:${curcontext}:paths" squeeze-slashes; then + skips='((.|..|)/)##' +else + skips='((.|..)/)##' +fi # We get the prefix and the suffix from the line and save the whole # original string. Then we see if we will do menucompletion. @@ -182,7 +165,7 @@ eorig="$orig" if (( $#ignore )); then _comp_ignore=( "$_comp_ignore[@]" "$ignore[@]" ) - (( $expl[(I)-F] )) || expl=( "$expl[@]" -F _comp_ignore ) + (( $mopts[(I)-F*] )) || mopts=( "$mopts[@]" -F _comp_ignore ) fi # Now let's have a closer look at the string to complete. @@ -256,25 +239,12 @@ for prepath in "$prepaths[@]"; do tsuf="$suf" testpath="$donepath" - tmp1=( "$prepath$realpath$donepath" ) - - while true; do - - # Skip over `./' and `../'. - - if [[ "$tpre" = (.|..)/* ]]; then - tmp1=( ${^tmp1}${tpre%%/*}/ ) - tpre="${tpre#*/}" - continue - fi + tmp2="${(M)tpre##${~skips}}" + tpre="${tpre#$tmp2}" - # Skip over multiple slashes? + tmp1=( "$prepath$realpath$donepath$tmp2" ) - if [[ -n "$skips" && "$tpre" = /* ]]; then - tmp1=( ${^tmp1}/ ) - tpre="${tpre#/}" - continue - fi + while true; do # Get the prefix and suffix for matching. @@ -289,45 +259,24 @@ for prepath in "$prepaths[@]"; do # Get the matching files by globbing. if [[ "$tpre$tsuf" = */* ]]; then - tmp2=( ${^tmp1}*(-/) ) - [[ ! -o globdots && "$PREFIX" = .* ]] && - tmp2=( "$tmp2[@]" ${^tmp1}.*(-/) ) + if [[ ! -o globdots && "$PREFIX" = .* ]]; then + tmp1=( ${^tmp1}${skipped}*(-/) ${^tmp1}${slash}.*(-/) ) + else + tmp1=( ${^tmp1}${skipped}*(-/) ) + fi if [[ -o globdots || "$PREFIX" = .* ]] && zstyle -s ":completion:${curcontext}:paths" special-dirs atmp; then if [[ "$atmp" = (yes|true|1|on) ]]; then - tmp2=( "$tmp2[@]" . .. ) + tmp1=( "$tmp1[@]" . .. ) elif [[ "$atmp" = .. ]]; then - tmp2=( "$tmp2[@]" .. ) + tmp1=( "$tmp1[@]" .. ) fi fi else - tmp2=( ${^tmp1}${^~pats} ) - [[ ! -o globdots && "$PREFIX" = .* ]] && - tmp2=( "$tmp2[@]" ${^tmp1}.${^~pats} ) - if (( $#tmp2 )) && - zstyle -s ":completion:${curcontext}:files" ignore-parents rem && - [[ ( "$rem" != *dir* || "$pats" = '*(-/)' ) && - ( "$rem" != *..* || "$tmp1" = *../* ) ]]; then - if [[ "$rem" = *parent* ]]; then - for i in "$tmp2[@]"; do - if [[ -d "$i" && "$i" = */* ]]; then - remt="${i%/*}" - while [[ "$remt" = */* ]]; do - [[ "$remt" -ef "$i" ]] && break - remt="${remt%/*}" - done - [[ "$remt" = */* || "$remt" -ef "$i" ]] && - _comp_ignore=( "$_comp_ignore[@]" "${(q)i}" ) - fi - done - fi - if [[ "$rem" = *pwd* ]]; then - for i in "$tmp2[@]"; do - [[ "$i" -ef "$PWD" ]] && _comp_ignore=( "$_comp_ignore[@]" "${(q)i}" ) - done - fi - (( $#_comp_ignore )) && (( $expl[(I)-F] )) || - expl=( "$expl[@]" -F _comp_ignore ) + if [[ ! -o globdots && "$PREFIX" = .* ]]; then + tmp1=( ${^tmp1}${skipped}${^~pats} ${^tmp1}${slash}.${^~pats} ) + else + tmp1=( ${^tmp1}${skipped}${^~pats} ) fi if [[ "$sopt" = *[/f]* && ( -o globdots || "$PREFIX" = .* ) ]] && zstyle -s ":completion:${curcontext}:paths" special-dirs atmp; then @@ -338,22 +287,28 @@ for prepath in "$prepaths[@]"; do fi fi fi - tmp1=( "$tmp2[@]" ) if [[ -n "$PREFIX$SUFFIX" ]]; then # See which of them match what's on the line. - builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" + if [[ -n "$_comp_correct" ]]; then + tmp2=( "$tmp1[@]" ) + builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" - if [[ $#tmp1 -eq 0 && -n "$_comp_correct" ]]; then - tmp1=( "$tmp2[@]" ) - compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}" + if [[ $#tmp1 -eq 0 && -n "$_comp_correct" ]]; then + tmp1=( "$tmp2[@]" ) + compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}" + fi + else + [[ "$tmp1[1]" = */* ]] && tmp2=( "$tmp1[@]" ) + + builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}" fi # If no file matches, save the expanded path and continue with # the outer loop. - if [[ $#tmp1 -eq 0 ]]; then + if (( ! $#tmp1 )); then if [[ "$tmp2[1]" = */* ]]; then tmp2=( "${(@)tmp2#${prepath}${realpath}}" ) if [[ "$tmp2[1]" = */* ]]; then @@ -384,8 +339,7 @@ for prepath in "$prepaths[@]"; do if [[ -z "$tpre$tsuf" && -n "$pre$suf" ]]; then tmp1=( "$tmp2[@]" ) - addsfx=(-S '') - remsfx=() + pfxsfx=(-S '' "$pfxsfx[@]") break; elif [[ "$haspats" = no && -z "$tpre$tsuf" && "$pre" = */ && -z "$suf" ]]; then @@ -397,6 +351,29 @@ for prepath in "$prepaths[@]"; do continue 2 fi + if [[ "$tpre$tsuf" != */* && $#tmp1 -ne 0 ]] && + zstyle -s ":completion:${curcontext}:files" ignore-parents rem && + [[ ( "$rem" != *dir* || "$pats" = '*(-/)' ) && + ( "$rem" != *..* || "$tmp1" = *../* ) ]]; then + if [[ "$rem" = *parent* ]]; then + for i in ${(M)^tmp1:#*/*}(-/); do + remt="${${i#$prepath$realpath$donepath}%/*}" + while [[ "$remt" = */* ]]; do + [[ "$prepath$realpath$donepath$remt" -ef "$i" ]] && break + remt="${remt%/*}" + done + [[ "$remt" = */* || "$remt" -ef "$i" ]] && + _comp_ignore=( "$_comp_ignore[@]" "${(q)i}" ) + done + fi + if [[ "$rem" = *pwd* ]]; then + for i in ${^tmp1}(-/); do + [[ "$i" -ef "$PWD" ]] && _comp_ignore=( "$_comp_ignore[@]" "${(q)i}" ) + done + fi + (( $#_comp_ignore && $mopts[(I)-F*] )) || mopts=( "$mopts[@]" -F _comp_ignore ) + fi + # Step over to the next component, if any. if [[ "$tpre" = */* ]]; then @@ -408,10 +385,16 @@ for prepath in "$prepaths[@]"; do break fi - # There are more components, so add a slash to the files we are - # collecting. + # There are more components, so skip over the next components and make a + # slash be added. - tmp1=( ${^tmp1}/ ) + tmp2="${(M)tpre##((.|..|)/)##}" ### + if [[ -n "$tmp2" ]]; then + skipped="/$tmp2" + tpre="${tpre#$tmp2}" + else + skipped=/ + fi done # The next loop searches the first ambiguous component. @@ -471,30 +454,30 @@ for prepath in "$prepaths[@]"; do if [[ "$tmp3" = */* ]]; then compadd -Qf "$mopts[@]" -p "$linepath$tmp2" -s "/${tmp3#*/}" \ -W "$prepath$realpath$testpath" \ - "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ - -M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \ + "$pfxsfx[@]" \ + -M "r:|/=* r:|=* $mspec" \ - "${(@)tmp1%%/*}" else compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \ -W "$prepath$realpath$testpath" \ - "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ - -M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \ + "$pfxsfx[@]" \ + -M "r:|/=* r:|=* $mspec" \ - "$tmp1[@]" fi else if [[ "$tmp3" = */* ]]; then atmp=( -Qf "$mopts[@]" -p "$linepath$tmp2" -W "$prepath$realpath$testpath" - "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" - -M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" ) + "$pfxsfx[@]" \ + -M "r:|/=* r:|=* $mspec" ) for i in "$tmp1[@]"; do compadd "$atmp[@]" -s "/${i#*/}" - "${i%%/*}" done else compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \ -W "$prepath$realpath$testpath" \ - "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ - -M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \ + "$pfxsfx[@]" \ + -M "r:|/=* r:|=* $mspec" \ - "$tmp1[@]" fi fi @@ -543,8 +526,8 @@ for prepath in "$prepaths[@]"; do compquote tmp4 tmp1 compadd -Qf "$mopts[@]" -p "$linepath$tmp4" \ -W "$prepath$realpath$testpath" \ - "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \ - -M "r:|/=* r:|=* $mspec" "$group[@]" "$expl[@]" \ + "$pfxsfx[@]" \ + -M "r:|/=* r:|=* $mspec" \ - "$tmp1[@]" fi done @@ -553,14 +536,17 @@ done # expanded paths that are different from the string on the line, we add # them as possible matches. -exppaths=( "${(@)exppaths:#$eorig}" ) - if zstyle -t ":completion:${curcontext}:paths" expand prefix && - [[ $#exppaths -gt 0 && nm -eq compstate[nmatches] ]]; then - PREFIX="${opre}" - SUFFIX="${osuf}" - compadd -Q "$mopts[@]" -S '' "$group[@]" "$expl[@]" \ - -M "r:|/=* r:|=* $mspec" -p "$linepath" - "$exppaths[@]" + [[ nm -eq compstate[nmatches] ]]; then + + exppaths=( "${(@)exppaths:#$eorig}" ) + + if (( $#exppaths )); then + PREFIX="${opre}" + SUFFIX="${osuf}" + compadd -Q "$mopts[@]" -S '' \ + -M "r:|/=* r:|=* $mspec" -p "$linepath" - "$exppaths[@]" + fi fi [[ nm -ne compstate[nmatches] ]] diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts index 4af470caa..cff34db0c 100644 --- a/Completion/Core/_sep_parts +++ b/Completion/Core/_sep_parts @@ -22,19 +22,16 @@ local matchflags opt group expl nm=$compstate[nmatches] opre osuf opts match # Get the options. -group=() -expl=() -opts=() -while getopts "J:V:X:P:F:S:r:R:qM:12n" opt; do - case "$opt" in - [JV]) group=("-$opt" "$OPTARG");; - X) expl=(-X "$OPTARG");; - [q12n]) opts=( "$opts[@]" "-$opt" );; - M) match="$OPTARG";; - *) opts=( "$opts[@]" "-$opt" "$OPTARG" );; - esac -done -shift OPTIND-1 +zparseopts -D \ + J:group V:group \ + P:opts F:opts S:opts r:opts R:opts qopts 1opts 2opts nopts \ + X:expl M:match + +if (( $#match )); then + match="${match[1][3,-1]}" +else + match='' +fi # Get the string from the line. diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index 92f155b23..c488f8b02 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -111,4 +111,28 @@ The resulting strings are stored in the var(array). item(tt(zregexparse))( This implements the internals of the `tt(_regex_arguments)'. ) +item(tt(zparseopts) [ tt(-D) ] var(specs))( +This builtin can be used to parse the positional arguments and put +options found in them into separate arrays. Each var(spec) describes +one option which is given as its first character. If the second +character is either `tt(:)' or `tt(+)', the option takes an argument +(either directly following the option in the same word or in the next +word). After the option character or the `tt(:)' or `tt(+)' follows +the name of the array in which the option should be stored. The only +difference between the form with a `t(:)' and the one with a `tt(+)' +is that in the first case the option and its argument will be put in +the array only once (later occurrences overwriting earlier ones), +whereas with `tt(+)' all occurrences are moved to the array. + +If the tt(-D) option is given, all options found are removed from the +positional parameters leaving only those strings that did not match +any of the var(specs). + +For example, calling `tt(zparseopts afoo b:bar c+bar)' with the +strings `tt(-a)', `tt(-bx)', `tt(-c)', `tt(y)', `tt(-cz)', `tt(baz)' +and `tt(-cend)' as positional arguments will set the array tt(foo) to +contain the element `tt(-a)' and the array tt(bar) to the strings +`tt(-bx)', `tt(-cy)' and `tt(-cz)'. The `tt(baz)' and any strings +after it will not be used. +) enditem() diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 3c04d03d8..56b22b5f9 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1155,10 +1155,149 @@ bin_zregexparse(char *nam, char **args, char *ops, int func) return ret; } +typedef struct zoptdesc *Zoptdesc; +typedef struct zoptarr *Zoptarr; +typedef struct zoptval *Zoptval; + +struct zoptdesc { + int arg; + Zoptarr arr; +}; + +struct zoptarr { + char *name; + Zoptval vals, last; + Zoptarr next; + int num; +}; + +#define ZOF_ARG 1 +#define ZOF_ADD 2 + +struct zoptval { + char *str; + Zoptval next; +}; + +static Zoptarr opt_arrs; + +static Zoptarr +get_opt_arr(char *name) +{ + Zoptarr p; + + for (p = opt_arrs; p; p = p->next) + if (!strcmp(name, p->name)) + return p; + + return NULL; +} + +static int +bin_zparseopts(char *nam, char **args, char *ops, int func) +{ + char *o, *n, **pp, *str, **aval; + Zoptdesc opts[256], d; + Zoptarr a; + Zoptval v; + + memset(opts, 0, 256 * sizeof(Zoptdesc)); + opt_arrs = NULL; + + while ((o = *args++)) { + if (opts[STOUC(*o)]) { + zerrnam(nam, "option described more than once: %s", o, 0); + return 1; + } + d = (Zoptdesc) zhalloc(sizeof(*d)); + d->arg = (o[1] == ':' ? ZOF_ARG : (o[1] == '+' ? ZOF_ADD : 0)); + if (!(a = get_opt_arr((n = o + (d->arg ? 2 : 1))))) { + a = (Zoptarr) zhalloc(sizeof(*a)); + a->name = n; + a->num = 0; + a->vals = a->last = NULL; + a->next = opt_arrs; + opt_arrs = a; + } + d->arr = a; + opts[STOUC(*o)] = d; + } + for (pp = pparams; (o = *pp); pp++) { + if (*o != '-') + break; + while (*++o) { + if (!(d = opts[STOUC(*o)])) + break; + if (d->arg) { + if (o[1]) { + str = (char *) zhalloc(strlen(o) + 2); + str[0] = '-'; + strcpy(str + 1, o); + } else if (!pp[1]) { + zerrnam(nam, "missing argument for option: -%c", NULL, *o); + return 1; + } else { + str = (char *) zhalloc(strlen(pp[1]) + 3); + str[0] = '-'; + str[1] = *o; + strcpy(str + 2, pp[1]); + pp++; + } + o = "" - 1; + } else { + str = (char *) zhalloc(3); + str[0] = '-'; + str[1] = *o; + str[2] = '\0'; + } + if (d->arg != ZOF_ADD) { + for (v = d->arr->vals; v; v = v->next) { + if (str[1] == v->str[1]) { + v->str = str; + str = NULL; + break; + } + } + } + if (str) { + v = (Zoptval) zhalloc(sizeof(*v)); + v->str = str; + v->next = NULL; + + if (d->arr->last) + d->arr->last->next = v; + else + d->arr->vals = v; + d->arr->last = v; + d->arr->num++; + } + } + if (*o) + break; + } + if (ops['D']) { + PERMALLOC { + pp = arrdup(pp); + } LASTALLOC; + + freearray(pparams); + pparams = pp; + } + for (a = opt_arrs; a; a = a->next) { + aval = (char **) zalloc((a->num + 1) * sizeof(char *)); + for (pp = aval, v = a->vals; v; pp++, v = v->next) + *pp = ztrdup(v->str); + *pp = NULL; + setaparam(a->name, aval); + } + return 0; +} + static struct builtin bintab[] = { BUILTIN("zstyle", 0, bin_zstyle, 0, -1, 0, NULL, NULL), BUILTIN("zformat", 0, bin_zformat, 3, -1, 0, NULL, NULL), BUILTIN("zregexparse", 0, bin_zregexparse, 3, -1, 0, "c", NULL), + BUILTIN("zparseopts", 0, bin_zparseopts, 1, -1, 0, "D", NULL), }; diff --git a/Src/Modules/zutil.mdd b/Src/Modules/zutil.mdd index 5c2660e92..9a84dbbbd 100644 --- a/Src/Modules/zutil.mdd +++ b/Src/Modules/zutil.mdd @@ -4,4 +4,4 @@ moddeps="zsh/complete" objects="zutil.o" -autobins="zformat zstyle" +autobins="zformat zstyle zregexparse zparseopts" |