From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from primenet.com.au (ns1.primenet.com.au [203.24.36.2]) by inbox.vuxu.org (OpenSMTPD) with ESMTP id 151fba78 for ; Mon, 6 May 2019 21:17:35 +0000 (UTC) Received: (qmail 15063 invoked by alias); 6 May 2019 21:17:18 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 44274 Received: (qmail 4864 invoked by uid 1010); 6 May 2019 21:17:18 -0000 X-Qmail-Scanner-Diagnostics: from park01.gkg.net by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.101.2/25440. spamassassin: 3.4.2. Clear:RC:0(205.235.26.22):SA:0(-1.6/5.0):. Processed in 4.204672 secs); 06 May 2019 21:17:18 -0000 X-Envelope-From: SRS0=7iYR=TG=yahoo.co.uk=okiddle@bounces.park01.gkg.net X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | Received-SPF: pass (ns1.primenet.com.au: SPF record at bounces.park01.gkg.net designates 205.235.26.22 as permitted sender) X-Virus-Scanned: by amavisd-new at gkg.net Authentication-Results: amavisd4.gkg.net (amavisd-new); dkim=pass (2048-bit key) header.d=yahoo.co.uk X-Greylist: domain auto-whitelisted by SQLgrey-1.8.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.co.uk; s=s2048; t=1557177377; bh=aIQ3sSf4SubdKx9rL0Pit23nY5ysX/IBpgDuTt4NZgQ=; h=From:References:To:Subject:Date:From:Subject; b=Si71IJPbqq+ySJUYBHyhe9rY835Fe3fI/jR+OzjZ8PQ8X9/bcb80J97uPhQJe13QRz+fJenYZTJsaMbJPG2XdFeDBbK7UJ690o6nemmaP7JKnaaRsEyBP/pqQNGyGr/1c0NYGc2EfV9ZmWH1f2s4k3W3qwWW6x07PAbaBzrpxQveBrGtYDJfpq3dJhxjo3Xvvw+ULXDpynf6l27warwna6TCtuQBABscNEdfxMnZj8EPkYHZroDqxNWyfQpd4KVn2h2u5rM8E5ruVeuHssWLJIgPpFt9gBGVER1rMLlLKDlGfo9CCJgSVR4vn9z9cCnrkWkKOVp1GObefYv9cGKdxg== X-YMail-OSG: XAkkbVwVM1k4GLqCcdbqP._2ZcIo5XJhFvLfmiTk2a3vaY9M2JyKnEt2zeRYgnd fnQlCN0xBkYFiwAPCvH9FVS9cEvOunpB1byT3tVXdzKYBAaB4ypDpn_UB6H8hcEXMmwtadjzxZFK qRM5SY.c4iB6yuJmZzbva55dPg4O.DxnqzYkMDI31NBnLNq0ZQsJvezrN6zXUrkPK03gPC_bozbb i3ALYt9fuQmVdaONA2ldaOWUHQaG07xU4YKmgXtrkde2elssPR5TAizLxaIsUkw_IE.Zp8nBepdm I0Y443e4CAZ_EXNyk0EyvkIfn.HGBJojs5o8HktRfyHxjqCLeCwPoG3rMomL7Yqtn01kk0Kba0LX .PuRjAoMLnq2bezxj4i9WBTIsOisRQCwbt1ENH6awArLyk7JrVcJPHGbyATiG6AD39thKeQ8eVBV bLYCXUfznw_sEyTvTygucho9dsYjDDVPEK8VNC4InPmfF9ZQmv2PFo4uXU9beCdvAwNf6OW2m39y .px_O.3rjMBM8VqN2GXnDedv5CDCEA5pcZlIrsfatuLjT7lQPZygiA2kFlGrq2slI1SIfx9.QGHJ Jd3tFrvivgulxQCdG7vq6BY9GLnLQN2qDw8put5VP2T3Q26hcrMGI.jqTBLeBusTfxZGpHtpMJ3O _YmVSfEWgUvcyfhJFKAO9PowasEWwCnQltjrlpwGG1J3srJKvQ_jHkdvBF9PNmLFD0LwE5jecyWe TARFG78lr6U9A2bUbqT2TkZeW7YOVtlHgnzUlcZ0lWd_s6qSOo2.mpiga.XfXJg8nFdA732TjFCZ I3e.GYu54kavxQlChAPIj2CbLXUydAveJe0OrqNxvw7f9BK6y_8bt7Q0.QOaUxQxxPcDu9_7GNBf 75DUqG0c6hr8tFIM0Gf.CsvmgmPvMk5h5HnL0BJAOtIvKTQBETCuZoGBk3Nirrvsn2RoEk60L97W YmTlTHZEhU0oDp21_irRpFW0hu875YIGfER.kR6Q45JWiq1hAqf_l5IwCsr_F7_eYm_NiW5vMrhS ewx_OdSr5zOJavbcs4VmbPhTqkupx0KU8Z.gtUxSMyksqeXgumKPuW8m.4HWhyCkmBBS6QcZo6nI hVRGEO8G8CM1.ZumsXpJw0PieHhdVewAN In-reply-to: <76839-1543195550.251964@c6AU.RX4q.p78d> From: Oliver Kiddle References: <75B26F45-E6E6-44BB-80A4-7301CBE480FE@dana.is> <88812-1541586959.338018@YaNA.ZOZt.NKaA> <76839-1543195550.251964@c6AU.RX4q.p78d> To: Zsh workers Subject: PATCH: completion match ordering MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <93342.1557177371.1@hydra> Date: Mon, 06 May 2019 23:16:11 +0200 Message-ID: <93343-1557177371.866119@fZLB.yW79.IWgX> I finally got around to finishing off the changes to the options for completion match ordering via compadd -o. Thanks to dana for reminding me about this. The argument to -o is now optional and can be abbreviated, e.g. -omat,rev The sort style also accepts values like "numeric" and documentation and tests have been added. _description, _all_labels etc all take -V, -1, -2 options and in the previous patch, I had added -o to that. However, as far as I can tell that only has any use in the case of -V because the functions need to decide whether to add -J group or -V group. So it was only needed because the interface conflates the use of -V - specifying the group name and making it unsorted. I guess -1 and -2 were handled because it is common to combine them as -1V, -2J etc. Given that it'd be useless, I decided not to add it. Back when sort was added to _description in 18859, it was decided to have it explicitly ignore the style if -V was passed. While, I would tend towards the view that it is better to give users control anyway, it remains that way for now. You don't need to pass -V to _wanted as such. To allow the style to enable sorting, we would need to add a compadd -o value corresponding to normal sort order: perhaps `sort' or `lexical' (i.e. not numeric). We could then rely on compadd honouring only the first -o option passed to it. The combination nosort,reverse could perhaps preserve (but reverse) the original order. We could perhaps also add the backslash ignoring that dana recently restored as an option to -o. In my testing, that patch looks good by the way - thanks dana. Oliver diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description index 304c747a6..c2a0e080b 100644 --- a/Completion/Base/Core/_description +++ b/Completion/Base/Core/_description @@ -1,13 +1,13 @@ #autoload -local name gropt nopt xopt format gname hidden hide match opts tag sort +local name nopt xopt format gname hidden hide match opts tag +local -a gropt sort opts=() -gropt=(-J) xopt=(-X) nopt=() -zparseopts -K -D -a nopt 1 2 V=gropt J=gropt x=xopt +zparseopts -K -D -a nopt 1 2 V=gropt J x=xopt 3="${${3##[[:blank:]]#}%%[[:blank:]]#}" [[ -n "$3" ]] && _lastdescr=( "$_lastdescr[@]" "$3" ) @@ -33,14 +33,18 @@ zstyle -s ":completion:${curcontext}:$1" matcher match && # Use sort style, but ignore `menu' value to help _expand. # Also don't override explicit use of -V. -if { zstyle -s ":completion:${curcontext}:$1" sort sort || - zstyle -s ":completion:${curcontext}:" sort sort; } && - [[ "$gropt" = -J && $sort != menu ]]; then - if [[ "$sort" = (yes|true|1|on) ]]; then - gropt=(-J) - else - gropt=(-V) +if [[ -z "$gropt" ]]; then + if zstyle -a ":completion:${curcontext}:$1" sort sort || + zstyle -a ":completion:${curcontext}:" sort sort + then + if [[ -z "${(@)sort:#(match|numeric|reverse)}" ]]; then + gropt=( -o ${(j.,.)sort} ) + elif [[ "$sort" != (yes|true|1|on|menu) ]]; then + gropt=( -o nosort ) fi + fi +else + gropt=( -o nosort ) fi if [[ -z "$_comp_no_ignore" ]]; then @@ -79,15 +83,15 @@ fi if [[ -n "$gname" ]]; then if [[ -n "$format" ]]; then - set -A "$name" "$opts[@]" "$nopt[@]" "$gropt" "$gname" "$xopt" "$format" + set -A "$name" "$opts[@]" "$nopt[@]" "$gropt[@]" -J "$gname" "$xopt" "$format" else - set -A "$name" "$opts[@]" "$nopt[@]" "$gropt" "$gname" + set -A "$name" "$opts[@]" "$nopt[@]" "$gropt[@]" -J "$gname" fi else if [[ -n "$format" ]]; then - set -A "$name" "$opts[@]" "$nopt[@]" "$gropt" -default- "$xopt" "$format" + set -A "$name" "$opts[@]" "$nopt[@]" "$gropt[@]" -J -default- "$xopt" "$format" else - set -A "$name" "$opts[@]" "$nopt[@]" "$gropt" -default- + set -A "$name" "$opts[@]" "$nopt[@]" "$gropt[@]" -J -default- fi fi diff --git a/Completion/Base/Utility/_describe b/Completion/Base/Utility/_describe index 76ab1d995..c12eb0eab 100644 --- a/Completion/Base/Utility/_describe +++ b/Completion/Base/Utility/_describe @@ -108,10 +108,10 @@ while _tags; do fi if [[ -n $_mats ]]; then - compadd "$_opts[@]" "${(@)_expl:/-J/-2V}" -D $_strs -O $_mats - \ + compadd "$_opts[@]" -2 -o nosort "${_expl[@]}" -D $_strs -O $_mats - \ "${(@)${(@M)${(@P)_mats}##([^:\\]|\\?)##}//\\(#b)(?)/$match[1]}" else - compadd "$_opts[@]" "${(@)_expl:/-J/-2V}" -D $_strs - \ + compadd "$_opts[@]" -2 -o nosort "${_expl[@]}" -D $_strs - \ "${(@)${(@M)${(@P)_strs}##([^:\\]|\\?)##}//\\(#b)(?)/$match[1]}" fi done diff --git a/Completion/Base/Utility/_guard b/Completion/Base/Utility/_guard index ff62981ce..1cbd4f39b 100644 --- a/Completion/Base/Utility/_guard +++ b/Completion/Base/Utility/_guard @@ -2,7 +2,7 @@ local garbage -zparseopts -K -D -a garbage M: J: V: 1 2 n F: X: +zparseopts -K -D -a garbage M+: J+: V+: 1 2 o+: n F: X+: [[ "$PREFIX$SUFFIX" != $~1 ]] && return 1 diff --git a/Completion/Base/Utility/_multi_parts b/Completion/Base/Utility/_multi_parts index 12ff965ed..00e3e26cc 100644 --- a/Completion/Base/Utility/_multi_parts +++ b/Completion/Base/Utility/_multi_parts @@ -14,8 +14,8 @@ typeset -U tmp1 matches # Get the options. zparseopts -D -a sopts \ - 'J+:=group' 'V+:=group' 'X+:=expl' 'P:=opts' 'F:=opts' \ - S: r: R: q 1 2 n f 'M+:=matcher' 'i=imm' + 'J+:=group' 'V+:=group' 'x+:=expl' 'X+:=expl' 'P:=opts' 'F:=opts' \ + S: r: R: q 1 2 o+: n f 'M+:=matcher' 'i=imm' sopts=( "$sopts[@]" "$opts[@]" ) if (( $#matcher )); then diff --git a/Completion/Base/Utility/_sep_parts b/Completion/Base/Utility/_sep_parts index de836a696..6fcf54ec4 100644 --- a/Completion/Base/Utility/_sep_parts +++ b/Completion/Base/Utility/_sep_parts @@ -22,8 +22,8 @@ local matchflags opt group expl nm=$compstate[nmatches] opre osuf opts matcher # Get the options. -zparseopts -D -a opts \ - 'J+:=group' 'V+:=group' P: F: S: r: R: q 1 2 n 'X+:=expl' 'M+:=matcher' +zparseopts -D -a opts 'J+:=group' 'V+:=group' P: F: S: r: R: q 1 2 o+: n \ + 'x+:=expl' 'X+:=expl' 'M+:=matcher' # Get the string from the line. diff --git a/Completion/Base/Utility/_sequence b/Completion/Base/Utility/_sequence index f52955f46..101934490 100644 --- a/Completion/Base/Utility/_sequence +++ b/Completion/Base/Utility/_sequence @@ -10,7 +10,8 @@ local curcontext="$curcontext" nm="$compstate[nmatches]" pre qsep nosep minus local -a sep num pref suf end uniq dedup -zparseopts -D -a opts s:=sep n:=num p:=pref i:=pref P:=pref I:=suf S:=suf q=suf r:=suf R:=suf C:=cont d=uniq M: J: X: x: +zparseopts -D -a opts s:=sep n:=num p:=pref i:=pref P:=pref I:=suf S:=suf \ + q=suf r:=suf R:=suf C:=cont d=uniq M+: J+: V+: 1 2 o+: X+: x+: (( $#cont )) && curcontext="${curcontext%:*}:$cont[2]" (( $#sep )) || sep[2]=, diff --git a/Completion/Darwin/Type/_mac_files_for_application b/Completion/Darwin/Type/_mac_files_for_application index 299d8ff5c..44516b4db 100644 --- a/Completion/Darwin/Type/_mac_files_for_application +++ b/Completion/Darwin/Type/_mac_files_for_application @@ -35,7 +35,7 @@ _mac_parse_info_plist() { # Try to complete files for the specified application. _mac_files_for_application() { local -a opts - zparseopts -D -a opts q n 1 2 P: S: r: R: W: X+: M+: F: J+: V+: + zparseopts -D -a opts q n 1 2 o+: P: S: r: R: W: x+: X+: M+: F: J+: V+: local app_path _retrieve_mac_apps diff --git a/Completion/Redhat/Command/_yum b/Completion/Redhat/Command/_yum index 34a337109..8abd23ebb 100644 --- a/Completion/Redhat/Command/_yum +++ b/Completion/Redhat/Command/_yum @@ -212,7 +212,7 @@ _yum_ids() { # `${(@)@[...]}' selects a subrange from $@ # `${(@)@[1,-2]}' are all except the last argument # `$@[$#]' is the last argument, e.g. the first suggestable ID - compadd "${(@)@[1,-2]:/-J/-V}" -M "B:0=" {$@[$#]..$maxid} + compadd "${(@)@[1,-2]}" -o numeric -M "B:0=" {$@[$#]..$maxid} } _yum_ranges() { diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git index 4eb07e921..112098d3a 100644 --- a/Completion/Unix/Command/_git +++ b/Completion/Unix/Command/_git @@ -5688,7 +5688,7 @@ __git_ignore_line () { __git_ignore_line_inside_arguments () { declare -a compadd_opts - zparseopts -D -E -a compadd_opts V: J: 1 2 n f X: M: P: S: r: R: q F: + zparseopts -D -E -a compadd_opts V+: J+: 1 2 o+: n f x+: X+: M+: P: S: r: R: q F: __git_ignore_line $* $compadd_opts } @@ -6160,7 +6160,7 @@ __git_ref_fields () { local match mbegin mend local -a cfields fields append opts all - zparseopts -D -E -a opts x: X: J: V: a=all + zparseopts -D -E -a opts M+: x+: X+: J+: V+: o+: 1 2 a=all if compset -P 1 '(#b)(*):'; then case $match[1] in @@ -6731,7 +6731,7 @@ __git_tags_of_type () { tags=(${${(M)${(f)"$(_call_program ${(q)type}-tag-refs "git for-each-ref --format='%(*objecttype)%(objecttype) %(refname)' refs/tags 2>/dev/null")"}:#$type(tag|) *}#$type(tag|) refs/tags/}) __git_command_successful $pipestatus || return 1 - _wanted $type-tags expl "$type tag" compadd -M 'r:|/=* r:|=*' "$@" -a - tags + _wanted $type-tags expl "$type tag" compadd -M 'r:|/=* r:|=*' "$@" -o numeric -a - tags } # Reference Argument Types @@ -6826,7 +6826,7 @@ __git_files_relative () { __git_files () { local compadd_opts opts tag description gitcdup gitprefix files expl - zparseopts -D -E -a compadd_opts V: J: 1 2 n f X: M: P: S: r: R: q F: + zparseopts -D -E -a compadd_opts V+: J+: 1 2 o+: n f x+: X+: M+: P: S: r: R: q F: zparseopts -D -E -a opts -- -cached -deleted -modified -others -ignored -unmerged -killed x+: --exclude+: tag=$1 description=$2; shift 2 @@ -6957,7 +6957,7 @@ __git_tree_files () { shift fi - zparseopts -D -E -a compadd_opts V: J: 1 2 n f X: M: P: S: r: R: q F: + zparseopts -D -E -a compadd_opts V+: J+: 1 2 o+: n f x+: X+: M+: P: S: r: R: q F: [[ "$1" == */ ]] && Path="$1" || Path="${1:h}/" shift @@ -7037,7 +7037,7 @@ __git_any_repositories_or_references () { __git_guard () { declare -A opts - zparseopts -K -D -A opts M: J: V: 1 2 n F: X: + zparseopts -K -D -A opts M+: J+: V+: 1 2 o+: n F: x+: X+: [[ "$PREFIX$SUFFIX" != $~1 ]] && return 1 @@ -7075,7 +7075,7 @@ __git_guard_diff-stat-width () { __git_guard_number () { declare -A opts - zparseopts -K -D -A opts M: J: V: 1 2 n F: X: + zparseopts -K -D -A opts M+: J+: V+: 1 2 o+: n F: x+: X+: _guard '[[:digit:]]#' ${1:-number} } diff --git a/Completion/Unix/Type/_baudrates b/Completion/Unix/Type/_baudrates index add359d13..6e9ba4d97 100644 --- a/Completion/Unix/Type/_baudrates +++ b/Completion/Unix/Type/_baudrates @@ -72,7 +72,6 @@ if (( ${+opts[-f]} )); then done fi -# -1V removes dupes (which there shouldn't be) and otherwise leaves the -# order in the $rates array intact. -_description -1V baud-rates expl 'baud rate' -compadd "${(@)argv/#-J/-V}" "$expl[@]" -- "${rates[@]}" +# -1 removes dupes (which there shouldn't be) +_description -1 -o numeric baud-rates expl 'baud rate' +compadd "${argv[@]}" "$expl[@]" -- "${rates[@]}" diff --git a/Completion/Unix/Type/_canonical_paths b/Completion/Unix/Type/_canonical_paths index 6eab7b677..cddc3b405 100644 --- a/Completion/Unix/Type/_canonical_paths +++ b/Completion/Unix/Type/_canonical_paths @@ -5,13 +5,14 @@ # (relative path when an absolute path is given, and vice versa; when ..'s are # present in the word to be completed, and some paths got from symlinks). -# Usage: _canonical_paths [-A var] [-N] [-MJV12nfX] tag desc [paths...] +# Usage: _canonical_paths [-A var] [-N] [-MJV12onfX] tag desc [paths...] # -A, if specified, takes the paths from the array variable specified. Paths # can also be specified on the command line as shown above. -N, if specified, # prevents canonicalizing the paths given before using them for completion, in # case they are already so. `tag' and `desc' arguments are well, obvious :) In -# addition, the options -M, -J, -V, -1, -2, -n, -F, -X are passed to compadd. +# addition, the options -M, -J, -V, -1, -2, -o, -n, -F, -x, -X are passed to +# compadd. _canonical_paths_add_paths () { # origpref = original prefix @@ -59,7 +60,7 @@ _canonical_paths() { local __index typeset -a __gopts __opts - zparseopts -D -a __gopts M: J: V: 1 2 n F: X: A:=__opts N=__opts + zparseopts -D -a __gopts M+: J+: V+: o+: 1 2 n F: x+: X+: A:=__opts N=__opts : ${1:=canonical-paths} ${2:=path} diff --git a/Completion/Unix/Type/_files b/Completion/Unix/Type/_files index 467ed747c..6adaa8154 100644 --- a/Completion/Unix/Type/_files +++ b/Completion/Unix/Type/_files @@ -27,7 +27,7 @@ local opts tmp glob pat pats expl tag i def descr end ign tried local type sdef ignvars ignvar prepath oprefix rfiles rfile zparseopts -a opts \ - '/=tmp' 'f=tmp' 'g+:-=tmp' q n 1 2 P: S: r: R: W: X+: M+: F: J+: V+: + '/=tmp' 'f=tmp' 'g+:-=tmp' q n 1 2 P: S: r: R: W: x+: X+: M+: F: J+: V+: o+: type="${(@j::M)${(@)tmp#-}#?}" if (( $tmp[(I)-g*] )); then diff --git a/Completion/Unix/Type/_list_files b/Completion/Unix/Type/_list_files index 6c52bc1f4..0ea02a55a 100644 --- a/Completion/Unix/Type/_list_files +++ b/Completion/Unix/Type/_list_files @@ -64,6 +64,6 @@ for f in ${(PQ)1}; do ${(r:8:)stat[gid]} ${(l:8:)stat[size]} $stat[mtime] $f") done -(( ${#listfiles} )) && listopts=(-d listfiles -l -o) +(( ${#listfiles} )) && listopts=(-d listfiles -l -o match) return 0 diff --git a/Completion/Unix/Type/_path_files b/Completion/Unix/Type/_path_files index 1021c34e6..19ae59629 100644 --- a/Completion/Unix/Type/_path_files +++ b/Completion/Unix/Type/_path_files @@ -59,7 +59,7 @@ exppaths=() zparseopts -a mopts \ 'P:=pfx' 'S:=pfxsfx' 'q=pfxsfx' 'r:=pfxsfx' 'R:=pfxsfx' \ 'W:=prepaths' 'F:=ignore' 'M+:=matcher' \ - J+: V+: X+: 1 2 n 'f=tmp1' '/=tmp1' 'g+:-=tmp1' + J+: V+: x+: X+: 1 2 o+: n 'f=tmp1' '/=tmp1' 'g+:-=tmp1' sopt="-${(@j::M)${(@)tmp1#-}#?}" (( $tmp1[(I)-[/g]*] )) && haspats=yes @@ -168,7 +168,7 @@ if zstyle -s ":completion:${curcontext}:" file-sort tmp1; then if [[ "$sort" = on ]]; then sort= else - mopts=( "${(@)mopts/#-J/-V}" ) + mopts=( -o nosort "${mopts[@]}" ) tmp2=() for tmp1 in "$pats[@]"; do diff --git a/Completion/Zsh/Command/_compadd b/Completion/Zsh/Command/_compadd index e709e400e..781fa2af8 100644 --- a/Completion/Zsh/Command/_compadd +++ b/Completion/Zsh/Command/_compadd @@ -14,9 +14,13 @@ _arguments -C -s -S -A "-*" \ '(-a)-k[matches are keys of specified associative arrays]' \ '-d+[specify display strings]:array:_parameters -g "*array*"' \ '-l[list display strings one per line, not in columns]' \ - '-o[order matches by match string not by display string]' \ - '(-1 -E)-J+[specify match group which will be sorted]:group' \ - '-V+[specify pre-ordered match group]:group' \ + '-o[specify order for matches by match string not by display string]:: : _values -s , order + "match[order by match not by display string]" + "nosort[matches are pre-ordered]" + "numeric[order numerically]" + "reverse[order backwards]"' \ + '(-1 -E)-J+[specify match group]:group' \ + '!-V+:group' \ '(-J -E)-1[remove only consecutive duplicates from group]' \ '-2[preserve all duplicates]' \ '(-x)-X[specify explanation]:explanation' \ @@ -45,7 +49,7 @@ if [[ -n $state ]]; then elif (( $+opt_args[-k] )); then _parameters -g "*assoc*" && ret=0 else - _message -e candidate candidates + _message -e candidates candidate fi fi diff --git a/Completion/Zsh/Type/_file_descriptors b/Completion/Zsh/Type/_file_descriptors index 3e9f4968b..be96dd0e4 100644 --- a/Completion/Zsh/Type/_file_descriptors +++ b/Completion/Zsh/Type/_file_descriptors @@ -56,4 +56,4 @@ fi fds=( 0 1 2 $fds ) _description -V file-descriptors expl 'file descriptor' -compadd $disp "${@/-J/-V}" "$expl[@]" -a fds +compadd $disp -o nosort "$@" "$expl[@]" -a fds diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 8b00517b7..66c476fab 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -2534,20 +2534,20 @@ is started, making it easy to select either of them. ) kindex(sort, completion style) item(tt(sort))( -Many completion widgets call tt(_description) at some point which -decides whether the matches are added sorted or unsorted (often -indirectly via tt(_wanted) or tt(_requested)). This style can be set -explicitly to one of the usual `true' or `false' values as an override. -If it is not set for the context, the standard behaviour of the -calling widget is used. +This allows the standard ordering of matches to be overridden. + +If its value is `tt(true)' or `tt(false)', sorting is enabled or disabled. +Additionally the values associated with the `tt(-o)' option to tt(compadd) can +also be listed: tt(match), tt(nosort), tt(numeric), tt(reverse). If it is not +set for the context, the standard behaviour of the calling widget is used. The style is tested first against the full context including the tag, and if that fails to produce a value against the context without the tag. -If the calling widget explicitly requests unsorted matches, this is usually -honoured. However, the default (unsorted) behaviour of completion -for the command history may be overridden by setting the style to -`true'. +In many cases where a calling widget explicitly selects a particular ordering +in lieu of the default, a value of `tt(true)' is not honoured. An example of +where this is not the case is for command history where the default of sorting +matches chronologically may be overridden by setting the style to `true'. In the tt(_expand) completer, if it is set to `true', the expansions generated will always be sorted. If it is set @@ -4404,11 +4404,11 @@ convention is not enforced). The description for the corresponding set of matches is passed to the function in var(descr). The styles tested are: tt(format), tt(hidden), tt(matcher), -tt(ignored-patterns) and tt(group-name). The tt(format) style is first -tested for the given var(tag) and then for the tt(descriptions) tag if -no value was found, while the remainder are only tested for the tag -given as the first argument. The function also calls tt(_setup) -which tests some more styles. +tt(ignore-line), tt(ignored-patterns), tt(group-name) and tt(sort). +The tt(format) style is first tested for the given var(tag) and then for +the tt(descriptions) tag if no value was found, while the remainder are +only tested for the tag given as the first argument. The function also +calls tt(_setup) which tests some more styles. The string returned by the tt(format) style (if any) will be modified so that the sequence `tt(%d)' is replaced by the var(descr) given as the third diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 1cc94bf95..0d8d4cc40 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -446,12 +446,13 @@ startitem() findex(compadd) cindex(completion widgets, adding specified matches) redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) -xitem(tt(compadd )[ tt(-akqQfenUlo12C) ] [ tt(-F) var(array) ]) +xitem(tt(compadd )[ tt(-akqQfenUl12C) ] [ tt(-F) var(array) ]) xitem(SPACES()[tt(-P) var(prefix) ] [ tt(-S) var(suffix) ]) xitem(SPACES()[tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ]) xitem(SPACES()[tt(-i) var(ignored-prefix) ] [ tt(-I) var(ignored-suffix) ]) xitem(SPACES()[tt(-W) var(file-prefix) ] [ tt(-d) var(array) ]) -xitem(SPACES()[tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ] [ tt(-x) var(message) ]) +xitem(SPACES()[tt(-J) var(group-name) ] [ tt(-X) var(explanation) ] [ tt(-x) var(message) ]) +xitem(SPACES()[tt(-V) var(group-name) ] [ tt(-o) [ var(order) ] ]) xitem(SPACES()[tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ]) xitem(SPACES()[tt(-D) var(array) ] [ tt(-O) var(array) ] [ tt(-A) var(array) ]) xitem(SPACES()[tt(-E) var(number) ]) @@ -540,18 +541,40 @@ This option only has an effect if used together with the tt(-d) option. If it is given, the display strings are listed one per line, not arrayed in columns. ) -item(tt(-o))( -This option only has an effect if used together with the tt(-d) -option. If it is given, the order of the output is determined by the -match strings; otherwise it is determined by the display strings -(i.e. the strings given by the tt(-d) option). +item(tt(-o) [ var(order) ])( +This controls the order in which matches are sorted. var(order) is a +comma-separated list comprising the following possible values. These values +can be abbreviated to their initial two or three characters. Note that the +order forms part of the group name space so matches with different orderings +will not be in the same group. + +startitem() +item(tt(match))( +If given, the order of the output is determined by the match strings; +otherwise it is determined by the display strings (i.e. the strings given +by the tt(-d) option). This is the default if `tt(-o)' is specified but +the var(order) argument is omitted. ) -item(tt(-J) var(name))( +item(tt(nosort))( +This specifies that the matches are pre-sorted and their order should be +preserved. This value only makes sense alone and cannot be combined with any +others. +) +item(tt(numeric))( +If the matches include numbers, sort them numerically rather than +lexicographically. +) +item(tt(reverse))( +Arrange the matches backwards by reversing the sort ordering. +) +enditem() +) +item(tt(-J) var(group-name))( Gives the name of the group of matches the words should be stored in. ) -item(tt(-V) var(name))( -Like tt(-J) but naming an unsorted group. These are in a different name -space than groups created with the tt(-J) flag. +item(tt(-V) var(group-name))( +Like tt(-J) but naming an unsorted group. This option is identical to +the combination of tt(-J) and tt(-o nosort). ) item(tt(-1))( If given together with the tt(-V) option, makes diff --git a/NEWS b/NEWS index 7e9ec05ff..ec20b4982 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,11 @@ functions may wish to familiarise themselves with `_normal -p` and The option CD_SILENT was added to suppress all output from cd (whether explicit or implicit with AUTO_CD). It is disabled by default. +The compadd builtin's -o option now takes an optional argument to +specify the order of completion matches. This affects the display +of candidate matches and the order in which they are selected when +cycling between them using menu completion. + Changes from 5.6.2 to 5.7.1 --------------------------- diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 3e9834560..743a2e3ac 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -90,6 +90,9 @@ struct cmgroup { #define CGF_PACKED 32 /* LIST_PACKED for this group */ #define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */ #define CGF_FILES 128 /* contains file names */ +#define CGF_MATSORT 256 /* sort by match rather than by display string */ +#define CGF_NUMSORT 512 /* sort numerically */ +#define CGF_REVSORT 1024 /* sort in reverse */ /* This is the struct used to hold matches. */ @@ -300,6 +303,9 @@ struct menuinfo { #define CAF_ARRAYS 32 /* compadd -a or -k: array/assoc parameter names */ #define CAF_KEYS 64 /* compadd -k: assoc parameter names */ #define CAF_ALL 128 /* compadd -C: _all_matches */ +#define CAF_MATSORT 256 /* compadd -o match: sort by match rather than by display string */ +#define CAF_NUMSORT 512 /* compadd -o numeric: sort numerically */ +#define CAF_REVSORT 1024 /* compadd -o numeric: sort in reverse */ /* Data for compadd and addmatches() */ diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 0a454ad5f..12c70e46e 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -2080,6 +2080,9 @@ addmatches(Cadata dat, char **argv) /* Select the group in which to store the matches. */ gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | + ((dat->aflags & CAF_MATSORT) ? CGF_MATSORT : 0) | + ((dat->aflags & CAF_NUMSORT) ? CGF_NUMSORT : 0) | + ((dat->aflags & CAF_REVSORT) ? CGF_REVSORT : 0) | ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); if (dat->group) { @@ -3034,8 +3037,9 @@ begcmgroup(char *n, int flags) HEAP_ERROR(p->heap_id); } #endif - if (p->name && - flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) && + if (p->name && flags == + (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON| + CGF_MATSORT|CGF_NUMSORT|CGF_REVSORT)) && !strcmp(n, p->name)) { mgroup = p; @@ -3118,32 +3122,34 @@ addexpl(int always) /* The comparison function for matches (used for sorting). */ +static int matchorder; + /**/ static int matchcmp(Cmatch *a, Cmatch *b) { - if ((*a)->disp && !((*a)->flags & CMF_MORDER)) { - if ((*b)->disp) { - if ((*a)->flags & CMF_DISPLINE) { - if ((*b)->flags & CMF_DISPLINE) - return strcmp((*a)->disp, (*b)->disp); - else - return -1; - } else { - if ((*b)->flags & CMF_DISPLINE) - return 1; - else - return strcmp((*a)->disp, (*b)->disp); - } - } - return -1; - } - if ((*b)->disp && !((*b)->flags & CMF_MORDER)) - return 1; + const char *as, *bs; + int cmp = !!(*b)->disp - !!(*a)->disp; + int sortdir = (matchorder & CGF_REVSORT) ? -1 : 1; - return zstrcmp((*a)->str, (*b)->str, (SORTIT_IGNORING_BACKSLASHES| - (isset(NUMERICGLOBSORT) ? - SORTIT_NUMERICALLY : 0))); + /* if match sorting selected or we have no display strings */ + if ((matchorder & CGF_MATSORT) || (!cmp && !(*a)->disp)) { + as = (*a)->str; + bs = (*b)->str; + } else { + if (cmp) /* matches with display strings come first */ + return cmp; + + cmp = ((*b)->flags & CMF_DISPLINE) - ((*a)->flags & CMF_DISPLINE); + if (cmp) /* sort one-per-line display strings first */ + return cmp; + + as = (*a)->disp; + bs = (*b)->disp; + } + + return sortdir * zstrcmp(as, bs, (isset(NUMERICGLOBSORT) || + matchorder & CGF_NUMSORT) ? SORTIT_NUMERICALLY : 0); } /* This tests whether two matches are equal (would produce the same @@ -3205,6 +3211,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) } else { if (!(flags & CGF_NOSORT)) { /* Now sort the array (it contains matches). */ + matchorder = flags; qsort((void *) rp, n, sizeof(Cmatch), (int (*) _((const void *, const void *)))matchcmp); diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 1dc2b01c2..c2f46c7f5 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -558,12 +558,53 @@ parse_class(Cpattern p, char *iptr) return iptr; } +static struct { char *name; int abbrev; int oflag; } orderopts[] = { + { "nosort", 2, CAF_NOSORT }, + { "match", 3, CAF_MATSORT }, + { "numeric", 3, CAF_NUMSORT }, + { "reverse", 3, CAF_REVSORT } +}; + +/* Parse the option to compadd -o, if flags is non-NULL set it + * returns -1 if the argument isn't a valid ordering, 0 otherwise */ + +/**/ +static int +parse_ordering(const char *arg, int *flags) +{ + int o, fl = 0; + const char *next, *opt = arg; + do { + int found = 0; + next = strchr(opt, ','); + if (!next) + next = opt + strlen(opt); + + for (o = sizeof(orderopts)/sizeof(*orderopts) - 1; o >= 0 && + !found; --o) + { + if ((found = next - opt >= orderopts[o].abbrev && + !strncmp(orderopts[o].name, opt, next - opt))) + fl |= orderopts[o].oflag; + } + if (!found) { + if (flags) /* default to "match" */ + *flags = CAF_MATSORT; + return -1; + } + } while (*next && ((opt = next + 1))); + if (flags) + *flags |= fl; + return 0; +} + /**/ static int bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) { struct cadata dat; char *mstr = NULL; /* argument of -M options, accumulated */ + char *oarg = NULL; /* argument of -o option */ int added; /* return value */ Cmatcher match = NULL; @@ -572,7 +613,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) return 1; } dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.mesg = - dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = + dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL; dat.match = NULL; dat.flags = 0; @@ -587,6 +628,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) } for (p = *argv + 1; *p; p++) { char *m = NULL; /* argument of -M option (this one only) */ + int order = 0; /* if -o found (argument to which is optional) */ char **sp = NULL; /* the argument to an option should be copied to *sp. */ const char *e; /* error message */ @@ -710,7 +752,11 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) dat.flags |= CMF_DISPLINE; break; case 'o': - dat.flags |= CMF_MORDER; + /* we honour just the first -o option but need to skip + * over a valid argument to subsequent -o options */ + order = oarg ? -1 : 1; + sp = &oarg; + /* no error string because argument is optional */ break; case 'E': if (p[1]) { @@ -741,15 +787,18 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) if (sp) { if (p[1]) { /* Pasted argument: -Xfoo. */ - if (!*sp) + if (!*sp) /* take first option only */ *sp = p + 1; - p += strlen(p+1); + if (!order || !parse_ordering(oarg, order == 1 ? &dat.aflags : NULL)) + p += strlen(p+1); } else if (argv[1]) { /* Argument in a separate word: -X foo. */ argv++; if (!*sp) *sp = *argv; - } else { + if (order && parse_ordering(oarg, order == 1 ? &dat.aflags : NULL)) + --argv; + } else if (!order) { /* Missing argument: argv[N] == "-X", argv[N+1] == NULL. */ zwarnnam(name, e, *p); zsfree(mstr); diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst index b1c0e40e5..901556167 100644 --- a/Test/Y01completion.ztst +++ b/Test/Y01completion.ztst @@ -116,6 +116,46 @@ F:regression test workers/31611 0:allow for suffixes when moving cursor to end of match (without ignored suffix) >line: {tst word:/}{} + comptesteval "_tst() { compadd -onum,rev -J versions r1.10 r1.1 r1.2 r2.3 r2.34 }" + comptest $'tst r\t' +0:reverse numeric sorting of matches +>line: {tst r}{} +>NO:{r2.34} +>NO:{r2.3} +>NO:{r1.10} +>NO:{r1.2} +>NO:{r1.1} + + comptesteval "_tst() { local expl; _wanted times expl time compadd -o match r1.10 r1.2 r2.3 r2.34 }" + comptesteval "zstyle ':completion:*:tst:*' sort reverse numeric" + comptest $'tst r\t' +0:reverse numeric sorting of matches via a style +>line: {tst r}{} +>DESCRIPTION:{time} +>NO:{r2.34} +>NO:{r2.3} +>NO:{r1.10} +>NO:{r1.2} + + comptesteval "_tst() { local disp=(a b c); compadd -o -J letters -d disp 3 2 1 }" + comptest $'tst \t' +0:sort in match rather than display name order +>line: {tst }{} +>NO:{c} +>NO:{b} +>NO:{a} + + comptesteval "_tst() { local expl; _wanted times expl time compadd 3am 12pm 3pm 10pm }" + comptesteval "zstyle ':completion:*:tst:*' sort false" + comptest $'tst \t' +0:sorting disabled via the sort style +>line: {tst }{} +>DESCRIPTION:{time} +>NO:{3am} +>NO:{12pm} +>NO:{3pm} +>NO:{10pm} + %clean zmodload -ui zsh/zpty