1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-05-13 11:06:17 +02:00

44274: allow finer control of completion match soring with compadd's -o option

This commit is contained in:
Oliver Kiddle 2019-05-07 23:24:49 +02:00
parent 5200637bda
commit cd6fd2b0a3
24 changed files with 257 additions and 101 deletions

View File

@ -1,5 +1,21 @@
2019-05-07 Oliver Kiddle <okiddle@yahoo.co.uk>
* 44274: Completion/Base/Core/_description,
Completion/Base/Utility/_describe, Completion/Base/Utility/_guard,
Completion/Base/Utility/_multi_parts,
Completion/Base/Utility/_sep_parts,
Completion/Base/Utility/_sequence, Completion/Zsh/Command/_compadd,
Completion/Darwin/Type/_mac_files_for_application,
Completion/Redhat/Command/_yum, Completion/Unix/Command/_git,
Completion/Unix/Type/_canonical_paths,
Completion/Unix/Type/_baudrates, Completion/Unix/Type/_files,
Completion/Unix/Type/_list_files, Completion/Unix/Type/_path_files,
Completion/Zsh/Type/_file_descriptors,
Doc/Zsh/compsys.yo, Doc/Zsh/compwid.yo, NEWS,
Src/Zle/comp.h, Src/Zle/compcore.c, Src/Zle/complete.c:
allow finer control of completion match soring with
arguments to compadd's -o option
* Gautam Iyer: gitlab !6: Completion/Unix/Command/_rclone:
Rudimentary rclone completion

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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]=,

View File

@ -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

View File

@ -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() {

View File

@ -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}
}

View File

@ -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[@]}"

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

5
NEWS
View File

@ -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
---------------------------

View File

@ -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() */

View File

@ -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,35 @@ 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, SORTIT_IGNORING_BACKSLASHES|
((isset(NUMERICGLOBSORT) ||
matchorder & CGF_NUMSORT) ? SORTIT_NUMERICALLY : 0));
}
/* This tests whether two matches are equal (would produce the same
@ -3205,6 +3212,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);

View File

@ -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);

View File

@ -138,6 +138,46 @@ F:regression test workers/31611
>FI:{\|foo}
>FI:{\~foo}
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