zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: list units in brackets at the end of completion group descriptions
@ 2021-08-27 11:52 Oliver Kiddle
  2021-08-27 12:10 ` Daniel Shahaf
  2021-08-27 14:44 ` Mikael Magnusson
  0 siblings, 2 replies; 10+ messages in thread
From: Oliver Kiddle @ 2021-08-27 11:52 UTC (permalink / raw)
  To: Zsh workers

Continuing with the bulk completion fixups for single issues, this
applies the convention of units in round brackets at the end of the
per-group descriptions just before the defaults I was adding in 49156
in square brackets. There aren't so many here because this is harder to
grep for and we mostly already do this consistently. For anyone writing
completions (or --help output), I'd recommend always considering units
and defaults because it's not always obvious.

There might be some more useful way we might indicate where suffixes can
be appended to a value to denote units. Completing them after a number
doesn't really make the options clear before a number has been entered.
Perhaps an initial slash, e.g. 'timeout (seconds) [5] /ms /us /ns'

As I've alluded to before, with consistent forms, _description could
parse these and allow formatting for these components to be configured.
But how often do we put random comments in brackets in descriptions or
other things that would break this? Yet more backslashes will be ugly.

Oliver

diff --git Completion/Debian/Command/_dak Completion/Debian/Command/_dak
index 8ecfd84b3..9976c7371 100644
--- Completion/Debian/Command/_dak
+++ Completion/Debian/Command/_dak
@@ -207,7 +207,7 @@ case $cmd in
 
 (poolize)
     args+=(
-  '(-l --limit)'{-l,--limit=}'[only migrate amount of packages]:amount in kb:'
+  '(-l --limit)'{-l,--limit=}'[only migrate amount of packages]:amount (kb)'
   '(-n --no-action)'{-n,--no-action}'[do not do anything]'
   '(-v --verbose)'{-v,--verbose}'[explain what is being done]'
     )
diff --git Completion/Linux/Command/_tpb Completion/Linux/Command/_tpb
index c4c4c2bbd..2bdee0942 100644
--- Completion/Linux/Command/_tpb
+++ Completion/Linux/Command/_tpb
@@ -9,7 +9,7 @@ _arguments \
   '(-x --xevents)'{-x,--xevents=}':state:(on off)' \
   '(-m --mixer)'{-m,--mixer=}':state:(on off)' \
   '(-o --osd)'{-o,--osd=}':state:(on off)' \
-  '(-p --poll)'{-p,--poll=}':delay in microseconds:' \
+  '(-p --poll)'{-p,--poll=}':delay (microseconds)' \
   '(-t --thinkpad)'{-t,--thinkpad=}':command to execute:_command_names' \
   '(-H --home)'{-H,--home=}':command to execute:_command_names' \
   '(-S --search)'{-S,--search=}':command to execute:_command_names' \
diff --git Completion/Mandriva/Command/_urpmi Completion/Mandriva/Command/_urpmi
index 9ab9e45b7..be6350129 100644
--- Completion/Mandriva/Command/_urpmi
+++ Completion/Mandriva/Command/_urpmi
@@ -73,7 +73,7 @@ _urpmi() {
 
   opts_net=(
     "($help --wget)--curl[use curl to retrieve distant files]"
-    "($help)--limit-rate[limit the download speed (b/s)]:download speed in b/s: "
+    "($help)--limit-rate[limit the download speed]:download speed (b/s)"
     "($help)--proxy:proxy host:_hosts"
     "($help)--proxy-user:proxy user:_users"
     "($help --curl)--wget[use wget to retrieve distant files]"
diff --git Completion/Unix/Command/_cpio Completion/Unix/Command/_cpio
index 699b1d8f2..20f8c712d 100644
--- Completion/Unix/Command/_cpio
+++ Completion/Unix/Command/_cpio
@@ -25,15 +25,15 @@ else
 	   '--message=:message at end of volume:'
 	   '--null' '--reset-access-time'
 	   '--verbose' '--dot' '--append'
-	   '--block-size=:block size in 512 byte units:'
+           '--block-size=:block size (512 byte units)'
 	   '--dereference'
-	   '--io-size=:block size in bytes'
+           '--io-size=:block size (bytes)'
 	   '--quiet' '--force-local'  '--help'   '--version')
     fi
     args+=(
 	  '-A[append files to archive]'
 	  '-B[block size 5120 bytes with special file]'
-	  '-C[set block size per record]:block size in bytes:(8192)'
+          '-C[set block size per record]:block size (bytes)'
 	  '-F[set archive file to use]:archive file:->afile'
 	  '(-H)-c[read/write header in ASCII]'
 	  "(-c)-H[set format type for archive header]:$fmts"
@@ -49,7 +49,7 @@ else
             '--preserve-modification-time' '--numeric'
 	    '--rename' '--list' '--swap-bytes' '--swap-halfwords'
 	    '--dot' '--unconditional' '--verbose'
-	    '--block-size=:block size in 512 byte units:'
+            '--block-size=:block size (512 byte units)'
 	    '--swap-halfwords'
 	    '--io-size=:block size in bytes:'
 	    '--pattern-file=:file with list of patterns:_files'
@@ -63,7 +63,7 @@ else
 	  '-b[reverse bytes in word]'
 	  '-B[block size 5120 bytes with special file]'
 	  '-d[create directories as needed]'
-	  '-C[set block size per record]:block size in bytes:(8192)'
+          '-C[set block size per record]:block size (bytes)'
 	  '-E[read filenames from file]:file name for list of files:_files'
 	  '-f[only copy files not matching patterns]'
 	  '-F[set archive file to use]:archive file:->afile'
diff --git Completion/Unix/Command/_dropbox Completion/Unix/Command/_dropbox
index 115f9eb45..45c77ae09 100644
--- Completion/Unix/Command/_dropbox
+++ Completion/Unix/Command/_dropbox
@@ -55,8 +55,8 @@ else
 
     (throttle)
     _arguments \
-      '2:download limit in KB/s:(unlimited)' \
-      '3:upload limit in KB/s:(unlimited)'
+      '2:download limit (KB/s):(unlimited)' \
+      '3:upload limit (KB/s):(unlimited)'
     ;;
 
     (*)
diff --git Completion/Unix/Command/_growisofs Completion/Unix/Command/_growisofs
index 8f4da4030..206d2c2c7 100644
--- Completion/Unix/Command/_growisofs
+++ Completion/Unix/Command/_growisofs
@@ -270,7 +270,7 @@ else
     '-sort[sort files on the image based on weights defined in the given file]:sort-weighting file:_files' \
     '-sparc-label[set the sun disk-label-name used for the -sparc-boot option]:label' \
     '-split-output[split the output image into several files ~1GiB in size]' \
-    '-stream-media-size[enable streaming operation and set media size to given number of sectors]:media size in sectors' \
+    '-stream-media-size[enable streaming operation and set media size to given number of sectors]:media size (sectors)' \
     '-stream-file-name[reserved for future use]:name' \
     '-sunx86-boot[specify a comma-separated list of file-system images needed to make a bootable Solaris-x86-CD]:list' \
     '-sunx86-label[specify the SVr4 disk-label-name for the SVr4 disk-label created with the -sunx86-boot option]:label' \
diff --git Completion/Unix/Command/_mysql_utils Completion/Unix/Command/_mysql_utils
index a7b285f1c..9f70687b0 100644
--- Completion/Unix/Command/_mysql_utils
+++ Completion/Unix/Command/_mysql_utils
@@ -220,8 +220,8 @@ _mysql_utils() {
   )
 
   _mysql_variables_opts=(
-    '--max_allowed_packet[maximum allowed packet size]:packet size in bytes'
-    '--net_buffer_length[network buffer length]:buffer length in bytes'
+    '--max_allowed_packet[maximum allowed packet size]:packet size (bytes)'
+    '--net_buffer_length[network buffer length]:buffer length (bytes)'
     '--connect_timeout:timeout'
     '--select_limit:limit'
     '--max_join_size:size'
diff --git Completion/Unix/Command/_perforce Completion/Unix/Command/_perforce
index cb6cdb79f..a52ac1d06 100644
--- Completion/Unix/Command/_perforce
+++ Completion/Unix/Command/_perforce
@@ -2668,12 +2668,12 @@ _perforce_cmd_passwd() {
 _perforce_cmd_ping() {
   _arguments -s : \
     '-c[specify count of messages]:count of messages: ' \
-    '-t[specify total time of test]:time in seconds: ' \
+    '-t[specify total time of test]:time (seconds)' \
     '-i[specify iterations for test]:number of iterations: ' \
     '-f[transmit continuously without waiting for responses]' \
-    '-p[specify pause between tests]:pause in seconds: ' \
-    '-s[specify send size]:send size in octets: ' \
-    '-r[specify receive size]:receive size in octets: '
+    '-p[specify pause between tests]:pause (seconds)' \
+    '-s[specify send size]:send size (octets)' \
+    '-r[specify receive size]:receive size (octets)'
 }
 
 
@@ -2942,7 +2942,7 @@ _perforce_cmd_status() {
 _perforce_cmd_sizes() {
   _arguments -s : \
     '-a[list all revisions in range]' \
-    '-b[specify blocksize]:blocksize in bytes: ' \
+    '-b[specify blocksize]:blocksize (bytes)' \
     '(-H)-h[print sizes in human-readable form (GiB)]' \
     '(-h)-H[print sizes in human-readable form (GB)]' \
     '-m[limit max files]:max files: ' \
diff --git Completion/Unix/Command/_transmission Completion/Unix/Command/_transmission
index 8951eacb0..c060cbae3 100644
--- Completion/Unix/Command/_transmission
+++ Completion/Unix/Command/_transmission
@@ -13,10 +13,10 @@ local general_options=(
 )
 # `general_actions`: *actions* that can be specified when a specific set of torrents is either specified or either not
 local general_actions=(
-  '(- :)'{-gsr,--global-seedratio}'[all torrents, unless overridden by a per-torrent setting, should seed until a specific ratio]:ratio:{_message "ratio in kB/sec"}'
+  '(- :)'{-gsr,--global-seedratio}'[all torrents, unless overridden by a per-torrent setting, should seed until a specific ratio]:ratio (kB/s)'
   '(- :)'{-GSR,--no-global-seedratio}'[all torrents, unless overridden by a per-torrent setting, should seed regardless of ratio]'
   '(- :)'{-pr,--peers}'[set the maximum number of peers]:number: '
-  '(- :)'{-d+,--downlimit=}'[limit the maximum download speed to limit kB/s]:limit:{_message "limit in kB/sec"}'
+  '(- :)'{-d+,--downlimit=}'[limit the maximum download speed to limit kB/s]:limit (kB/s)'
   '(- :)'{-D,--no-downlimit}'[disable download speed limits]'
 )
 # `global_only_actions`: *actions* that can be specified only when no specific torrent is specified
@@ -27,8 +27,8 @@ local global_only_actions=(
   '(- :)'{-asu,--alt-speed-uplimit}'[limit the alternate upload speed to limit kilobytes per second]'
   '(- :)'{-asc,--alt-speed-scheduler}'[use the scheduled on/off times]'
   '(- :)'{-asc,--not-alt-speed-scheduler}'[don'"'"'t use the scheduled on/off times]'
-  '(- :)--alt-speed-time-begin[time to start using the alt speed limits]:hhmm:_transmission-remote_time'
-  '(- :)--alt-speed-time-end[time to stop using the alt speed limits]:hhmm:_transmission-remote_time'
+  '(- :)--alt-speed-time-begin[time to start using the alt speed limits]:time (HHMM)'
+  '(- :)--alt-speed-time-end[time to stop using the alt speed limits]:time (HHMM)'
   '(- :)--alt-speed-days[set the number of days on which to enable the speed scheduler]:days:_transmission-remote_days'
   '(- :)--torrent-done-script[specify a file to run each time a torrent finishes]:script:_files'
   '(- :)--no-torrent-done-script[don'"'"'t run any script when a torrent finishes]'
@@ -51,7 +51,7 @@ local global_only_actions=(
   '(- :)'{-M,--no-portmap}'[disable portmapping]'
   '(- :)'{-o,--dht}'[enable distributed hash table]'
   '(- :)'{-O,--no-dht}'[disable distributed hash table]'
-  '(- :)'{-u,--uplimit}'[limit the maximum upload speed to limit kB/s]:limit:{_message "limit in kB/sec"}'
+  '(- :)'{-u,--uplimit}'[limit the maximum upload speed to limit kB/s]:limit (kB/s)'
   '(- :)'{-U,--no-uplimit}'[disable upload speed limits]'
   '(- :)--utp[enable uTP for peer connections]'
   '(- :)--no-utp[disable uTP for peer connections]'
@@ -89,7 +89,7 @@ local torrent_action_only_actions=(
   {-rad,--remove-and-delete}'[remove the current torrent(s) and delete the downloaded data]'
   '--reannounce[reannounce the current torrent(s)]'
   '--move[move the current torrents'"'"' data from their current locations to the specified directory]:{_files -/}'
-  {-sr+,--seedratio=}'[let the current torrent(s) seed until a specific ratio]:ratio:{_message "ratio in kB/sec"}'
+  {-sr+,--seedratio=}'[let the current torrent(s) seed until a specific ratio]:ratio'
   {-SR,--no-seedratio}'[let the current torrent(s) use the global seedratio settings]'
   {-hl,--honor-session}'[make the current torrent(s) honor the session limits]'
   {-HL,--no-honor-session}'[make the current torrent(s) not honor the session limits]'
@@ -104,12 +104,6 @@ local torrent_action_only_actions=(
 
 # helper functions
 
-# complete argument specified after global only action: `--alt-speed-time-begin`
-# completes time in HHMM format
-(( $+functions[_transmission-remote_time] )) ||
-_transmission-remote_time(){
-  _message "time in format HHMS"
-}
 # complete argument specified after global only action: `--alt-speed-days`
 # completes days in a list such as "2,4-6".
 (( $+functions[_transmission-remote_days] )) ||
diff --git Completion/X/Command/_urxvt Completion/X/Command/_urxvt
index 661897c3a..2d7c09579 100644
--- Completion/X/Command/_urxvt
+++ Completion/X/Command/_urxvt
@@ -43,8 +43,8 @@ _x_arguments \
   '-sl:save lines' \
   '-embed:window id to embed terminal in:_x_window' \
   '-pty-fd:file descriptor of pty to use' \
-  '-w:external border in pixels' \
-  '-b:internal border in pixels' \
+  {-bw,-w}':external border (pixels)' \
+  '-b:internal border (pixels)' \
   '-lsp:number of extra pixels between rows' \
   '-letsp:letter spacing adjustment' \
   '-mod:meta modifier:_x_modifier' \


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-08-27 11:52 PATCH: list units in brackets at the end of completion group descriptions Oliver Kiddle
@ 2021-08-27 12:10 ` Daniel Shahaf
  2021-10-27 20:51   ` Oliver Kiddle
  2021-08-27 14:44 ` Mikael Magnusson
  1 sibling, 1 reply; 10+ messages in thread
From: Daniel Shahaf @ 2021-08-27 12:10 UTC (permalink / raw)
  To: zsh-workers

Oliver Kiddle wrote on Fri, 27 Aug 2021 11:52 +00:00:
> There might be some more useful way we might indicate where suffixes can
> be appended to a value to denote units. Completing them after a number
> doesn't really make the options clear before a number has been entered.
> Perhaps an initial slash, e.g. 'timeout (seconds) [5] /ms /us /ns'

This design first says "Input a value in seconds", then the default
value, then goes back to say that other units are possible.

I think we should try for a design with more locality in the provision
of information.  For instance, assuming the default unit may also be
specified explicitly:

    timeout [5 = 5s]
    # This intends to communicate that both «5» and «5s» are valid inputs, and equivalent.

    timeout [5] ; hr/[s]/ms/us/ns
    # Indicate the default value and default unit separately, indicating all units in a group

I'm sure there are better options than these.

Cheers,

Daniel


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-08-27 11:52 PATCH: list units in brackets at the end of completion group descriptions Oliver Kiddle
  2021-08-27 12:10 ` Daniel Shahaf
@ 2021-08-27 14:44 ` Mikael Magnusson
  2021-08-27 16:14   ` Oliver Kiddle
  1 sibling, 1 reply; 10+ messages in thread
From: Mikael Magnusson @ 2021-08-27 14:44 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: Zsh workers

On 8/27/21, Oliver Kiddle <opk@zsh.org> wrote:
> Continuing with the bulk completion fixups for single issues, this
> applies the convention of units in round brackets at the end of the
> per-group descriptions just before the defaults I was adding in 49156
> in square brackets. There aren't so many here because this is harder to
> grep for and we mostly already do this consistently. For anyone writing
> completions (or --help output), I'd recommend always considering units
> and defaults because it's not always obvious.
>
> There might be some more useful way we might indicate where suffixes can
> be appended to a value to denote units. Completing them after a number
> doesn't really make the options clear before a number has been entered.
> Perhaps an initial slash, e.g. 'timeout (seconds) [5] /ms /us /ns'
>
> As I've alluded to before, with consistent forms, _description could
> parse these and allow formatting for these components to be configured.
> But how often do we put random comments in brackets in descriptions or
> other things that would break this? Yet more backslashes will be ugly.
>
> Oliver
>
> diff --git Completion/Debian/Command/_dak Completion/Debian/Command/_dak
> index 8ecfd84b3..9976c7371 100644
> --- Completion/Debian/Command/_dak
> +++ Completion/Debian/Command/_dak
> @@ -207,7 +207,7 @@ case $cmd in
>
>  (poolize)
>      args+=(
> -  '(-l --limit)'{-l,--limit=}'[only migrate amount of packages]:amount in
> kb:'
> +  '(-l --limit)'{-l,--limit=}'[only migrate amount of packages]:amount
> (kb)'
>    '(-n --no-action)'{-n,--no-action}'[do not do anything]'
>    '(-v --verbose)'{-v,--verbose}'[explain what is being done]'
>      )
> diff --git Completion/Mandriva/Command/_urpmi
> Completion/Mandriva/Command/_urpmi
> index 9ab9e45b7..be6350129 100644
> --- Completion/Mandriva/Command/_urpmi
> +++ Completion/Mandriva/Command/_urpmi
> @@ -73,7 +73,7 @@ _urpmi() {
>
>    opts_net=(
>      "($help --wget)--curl[use curl to retrieve distant files]"
> -    "($help)--limit-rate[limit the download speed (b/s)]:download speed in
> b/s: "
> +    "($help)--limit-rate[limit the download speed]:download speed (b/s)"
>      "($help)--proxy:proxy host:_hosts"
>      "($help)--proxy-user:proxy user:_users"
>      "($help --curl)--wget[use wget to retrieve distant files]"
> diff --git Completion/Unix/Command/_dropbox
> Completion/Unix/Command/_dropbox
> index 115f9eb45..45c77ae09 100644
> --- Completion/Unix/Command/_dropbox
> +++ Completion/Unix/Command/_dropbox
> @@ -55,8 +55,8 @@ else
>
>      (throttle)
>      _arguments \
> -      '2:download limit in KB/s:(unlimited)' \
> -      '3:upload limit in KB/s:(unlimited)'
> +      '2:download limit (KB/s):(unlimited)' \
> +      '3:upload limit (KB/s):(unlimited)'
>      ;;
>
>      (*)

some inconsistencies here with kilos and bytes, it should always be kB
although I doubt anyone would ever think kilobits or kelvinbytes are
refered to in these contexts. The urpmi one should probably be spelled
out as bytes/s or B/s though (somehow it feels less clear to me
without the kilo). Perhaps worth a separate batch cleanup.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-08-27 14:44 ` Mikael Magnusson
@ 2021-08-27 16:14   ` Oliver Kiddle
  0 siblings, 0 replies; 10+ messages in thread
From: Oliver Kiddle @ 2021-08-27 16:14 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: Zsh workers

Mikael Magnusson wrote:
> > -      '3:upload limit in KB/s:(unlimited)'

> some inconsistencies here with kilos and bytes, it should always be kB
> although I doubt anyone would ever think kilobits or kelvinbytes are
> refered to in these contexts. The urpmi one should probably be spelled
> out as bytes/s or B/s though (somehow it feels less clear to me
> without the kilo). Perhaps worth a separate batch cleanup.

I intentionally left them exactly as they were before. That should
be done as a separate cleanup but is not really something I'm keen
to tackle because you'd have to look into each programme to find out
whether it is bits or bytes and kilo or kibi. I'd guess there are plenty
more of these too.

For bandwidth, bits rather than bytes is actually fairly likely.

Oliver


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-08-27 12:10 ` Daniel Shahaf
@ 2021-10-27 20:51   ` Oliver Kiddle
  2021-11-02 12:08     ` Jun T
  0 siblings, 1 reply; 10+ messages in thread
From: Oliver Kiddle @ 2021-10-27 20:51 UTC (permalink / raw)
  To: zsh-workers

On 27 Aug, Daniel wrote:
> Oliver Kiddle wrote on Fri, 27 Aug 2021 11:52 +00:00:
> > There might be some more useful way we might indicate where suffixes can
> > be appended to a value to denote units. Completing them after a number
> > doesn't really make the options clear before a number has been entered.
> > Perhaps an initial slash, e.g. 'timeout (seconds) [5] /ms /us /ns'

> This design first says "Input a value in seconds", then the default
> value, then goes back to say that other units are possible.

On further relection, for handling unit suffixes, we ideally also want
to complete them. So having to specify them in the description would
only lead to duplication.

Units are nearly always only applicable on the end of numbers. So how
about we add _numbers as a helper function to take care of completing
unit suffixes. This also side-steps Daniel's concern about locality - at
least if we don't display the units in the description in the default
format.

(I've always been somewhat unhappy about the existence of
__git_guard_number - there's nothing git specific about it and the guard
behaviour is questionable. I still wouldn't bother adding calls to
_numbers for plain values where it doesn't add anything.)

The attached patch shows a possible approach. It includes _numbers and a
patch to _description to parse the description for units, range and a
default. That's turning what has hitherto only been a convention into an
explicit part of the _descriptions interface. Should we worry about that
and the effect it might have on existing descriptions. The patterns can
perhaps be tightened up if they pick up things that aren't meant to be
units etc. I can't think of an alternative way to add the functionality
if we want it applied throughout and not just sporadically. With this
patch the parsing can be disabled by passing an extra zformat spec
(H:0 may be a good choice).

_description has always had the ability to accept extra zformat
specifications. It isn't used as often as was perhaps originally
envisaged (just %o with expansions). I've used %m for units (measurement
- u is taken for underline), %r for range (e.g 1-30), %o for default
(otherwise - d is taken), %x for the list of suffixes (s is taken) and
%h for the unadorned description (heading). For backward compatibility
%d is contructed to look as before. I'm open to better suggestions on
these if someone has better ideas?

Corresponding to these are uppercase forms for use with the zformat
conditional. This allows, e.g. %(M. %F{28}(%m%)%f.) for adding the units
conditionally. Perhaps we should change zformat so %(m. .) can be used
directly but that would break compatibility.

_numbers also constructs the list of suffixes using zformat; looked up
using unit-suffixes as the tag. (A name that wouldn't clash is needed
because _description looks up the format style first under the tag it is
passed before checking with the tag "descriptions".) In this case %x is
used for an individual suffix, %X the description, %i and %r for index
and reverse index and %d for a default. The results then being
concatenated. So, e.g. for blank space before the first, underline for a
default and vertical bars after all but the last it would need the
following style:

  zstyle ':completion:*:unit-suffixes' format \
      '%(i:       :)%(d.%U.)%x%(d.%u.)%(r..|)'

This _numbers function takes quite a few options. Should we allow a way
to configure how a range is presented - this hardcodes a dash separating
min and max and has no way to indicate for decimal numbers whether the
min and max values are inclusive?

The patterns don't handle exponents, hex or an initial unary + operator
but would be easy to add if there's a need.

In some existing completions we fully expand the numbers as matches
with, e.g. {1..32}. Perhaps a style can enable this to a specified
limit. There are also cases where a specific set of numbers are
possible, e.g. baud-rates but I've not bothered to consider that case
because they typically wouldn't be followed by unit suffixes.

Does anyone have any other thoughts or ideas on this?

Oliver

diff --git a/Completion/BSD/Command/_ipfw b/Completion/BSD/Command/_ipfw
index 2354a70fe..49d0ef1e8 100644
--- a/Completion/BSD/Command/_ipfw
+++ b/Completion/BSD/Command/_ipfw
@@ -249,8 +249,8 @@ actions=(
     $'/(pipe|queue|sched)[ \t\0]/' -'pqs=${match%?}' ':dummynet-commands:dummynet configuration:$ca pipe queue sched'
     $word ': _message -e numbers number'
     $word ':options:config:$ca config'
-    \( $'/bw[ \t\0]/' \( $'/<->/' ': _guard "[0-9]#" bandwidth'
-         $word ':units:unit:compadd -M "m:{a-z}={A-Z}" {K,M,G}{bit,Byte}/s'
+    \( $'/bw[ \t\0]/'
+      \( $word ':bandwidths: :_numbers -M "m:{a-z}={A-Z}" bandwidth {K,M,G}{bit,Byte}/s'
       \| $word ':devices:device:_net_interfaces -qS " "' \)
     \| $'/delay[ \t\0]/' $word ': _message -e numbers "propagation delay (ms)"'
     \| $'/burst[ \t\0]/' $word ': _message -e numbers "size (bytes)"'
diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description
index bdb4007a6..48d9f6e55 100644
--- a/Completion/Base/Core/_description
+++ b/Completion/Base/Core/_description
@@ -2,6 +2,7 @@
 
 local name nopt xopt format gname hidden hide match opts tag
 local -a ign gropt sort
+local -a match mbegin mend
 
 opts=()
 
@@ -78,6 +79,13 @@ shift 2
 if [[ -z "$1" && $# -eq 1 ]]; then
   format=
 elif [[ -n "$format" ]]; then
+  if [[ -z $2 ]]; then
+    argv+=( h:${1%%( ##\((#b)([^\)]#[^0-9-][^\)]#)(#B)\)|)( ##\((#b)([0-9-]##)(#B)\)|)( ##\[(#b)([^\]]##)(#B)\]|)} H:1 )
+    [[ -n $match[1] ]] && argv+=( m:$match[1] M:1 )
+    [[ -n $match[2] ]] && argv+=( r:$match[2] R:1 )
+    [[ -n $match[3] ]] && argv+=( o:$match[3] O:1 )
+  fi
+
   zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}"
 fi
 
diff --git a/Completion/Base/Utility/_numbers b/Completion/Base/Utility/_numbers
new file mode 100644
index 000000000..312d0a7a0
--- /dev/null
+++ b/Completion/Base/Utility/_numbers
@@ -0,0 +1,78 @@
+#autoload
+
+# Usage: _numbers [compadd options] [-t tag] [-f|-N] [-u units] [-l min] [-m max] \
+#                 [-d default] ["description"] [unit-suffix...]
+
+#   -t : specify a tag (defaults to 'numbers')
+#   -u : indicate the units, e.g. seconds
+#   -l : lowest possible value
+#   -m : maximum possible value
+#   -d : default value
+#   -N : allow negative numbers (implied by range including a negative)
+#   -f : allow decimals (float)
+
+# For a unit-suffix, an initial colon indicates a unit that asserts the default
+# otherwise, colons allow for descriptions, e.g:
+
+#   :s:seconds m:minutes h:hours
+
+# unit-suffixes are not sorted by the completion system when listed
+# Specify them in order of magnitude, this tends to be ascending unless
+# the default is of a higher magnitude, in which case, descending.
+# So for, example
+#   bytes kB MB GB
+#   s ms us ns
+# Where the compadd options include matching control or suffixes, these
+# are applied to the units
+
+# For each unit-suffix, the format style is looked up with the
+# unit-suffixes tag and the results concatenated.
+
+# _description is called with u and U tokens set
+
+local desc tag range suffixes suffix suffixfmt pat='<->' partial=''
+local -a expl formats
+local -a default max min keep tags units
+local -i i
+local -A opts
+
+zparseopts -K -D -A opts M+:=keep q:=keep s+:=keep S+:=keep J+: V+: 1 2 o+: n F: x+: X+: \
+  t:=tags u:=units l:=min m:=max d:=default f=type e=type N=type
+
+desc="${1:-number}" tag="${tags[2]:-numbers}"
+(( $# )) && shift
+
+[[ -n ${(M)type:#-f} ]] && pat='(<->.[0-9]#|[0-9]#.<->|<->)' partial='(|.)'
+[[ -n ${(M)type:#-N} || $min[2] = -* || $max[2] = -* ]] && \
+    pat="(|-)$pat" partial="(|-)$partial"
+
+if (( $#argv )) && compset -P "$pat"; then
+  zstyle -s ":completion:${curcontext}:units" list-separator sep || sep=--
+  _description -V units expl unit
+  disp=( ${${argv#:}/:/ $sep } )
+  compadd -M 'r:|/=* r:|=*' -d disp "$keep[@]" "$expl[@]" - ${${argv#:}%%:*}
+  return
+elif [[ -prefix $~pat || $PREFIX = $~partial ]]; then
+  formats=( "h:$desc" H:1 )
+  (( $#units )) && formats+=( m:${units[2]} M:${#units} ) desc+=" ($units[2])"
+  (( $#min )) && range="$min[2]-"
+  (( $#max )) && range="${range:--}$max[2]"
+  [[ -n $range ]] && formats+=( r:$range R:1 ) desc+=" ($range)"
+  (( $#default )) && formats+=( o:${default[2]} O:1 ) desc+=" [$default[2]]"
+
+  zstyle -s ":completion:${curcontext}:unit-suffixes" format suffixfmt
+  for ((i=0;i<$#;i++)); do
+    zformat -f suffix "$suffixfmt" "x:${${argv[i+1]#:}%%:*}" \
+        "X:${${argv[i+1]#:}#*:}" "d:${#${argv[i+1]}[1]#:}" \
+	i:i r:$(( $# - i - 1))
+    suffixes+="$suffix"
+  done
+  [[ -n $suffixes ]] && formats+=( x:$suffixes X:1 )
+
+  _comp_mesg=yes
+  _description -x $tag expl "$desc" $formats
+  compadd "$expl[@]"
+  return 0
+fi
+
+return 1
diff --git a/Completion/Unix/Command/_killall b/Completion/Unix/Command/_killall
index 36accb2e0..3ddd36341 100644
--- a/Completion/Unix/Command/_killall
+++ b/Completion/Unix/Command/_killall
@@ -38,15 +38,9 @@ if _pick_variant psmisc=PSmisc unix --version; then
 
   case $state in
     (time)
-      local -a units=( 's:seconds' 'm:minutes' 'h:hours' 'd:days'
-			'w:weeks' 'M:months' 'y:years' )
-      if compset -P '[0-9]##(|.[0-9]#)'; then
-	_alternative 'float-numbers:: _message "float number"' \
-		    'units:: _describe unit units' && ret=0
-      else
-	_message 'float number and unit' && ret=0
-      fi
-      ;;
+      _numbers -fN age 's:seconds' 'm:minutes' 'h:hours' 'd:days' \
+          'w:weeks' 'M:months' 'y:years'
+    ;;
   esac
 
   return ret
diff --git a/Completion/Unix/Command/_pv b/Completion/Unix/Command/_pv
index 68f8e8586..d02d3a35d 100644
--- a/Completion/Unix/Command/_pv
+++ b/Completion/Unix/Command/_pv
@@ -25,7 +25,7 @@ _arguments -s -S $args \
   '(-q --quiet)'{-q,--quiet}"[don't output any transfer information at all, useful with -L]" \
   '(-W --wait)'{-W,--wait}'[display nothing until first byte transferred]' \
   '(-D --delay-start -R --remote)'{-D+,--delay-start=}'[display nothing until delay has passed]:delay (seconds)' \
-  '(-s --size)'{-s+,--size=}'[set estimated data size]:size (bytes):->size-unit' \
+  '(-s --size)'{-s+,--size=}'[set estimated data size]: :_numbers -u bytes size K M G T' \
   '(-l --line-mode -R --remote)'{-l,--line-mode}'[count lines instead of bytes]' \
   '(-0 --null -l --line-mode)'{-0,--null}'[lines are null-terminated]' \
   '(-i --interval)'{-i+,--interval=}'[update every after specified interval]:interval (seconds) [1]' \
@@ -34,8 +34,8 @@ _arguments -s -S $args \
   '(-N --name)'{-N+,--name=}'[prefix visual information with given name]:name' \
   '(-f --force -R --remote)'{-f,--force}'[output even if standard error is not a terminal]' \
   '(-c --cursor -R --remote)'{-c,--cursor}'[use cursor positioning escape sequences]' \
-  '(-L --rate-limit)'{-L+,--rate-limit=}'[limit transfer rate]:rate (bytes per second):->size-unit' \
-  '(-B --buffer-size)'{-B+,--buffer-size=}'[use a buffer size of given size]:size (bytes):->size-unit' \
+  '(-L --rate-limit)'{-L+,--rate-limit=}'[limit transfer rate]: :_numbers -u "bytes per second" rate K M G T' \
+  '(-B --buffer-size)'{-B+,--buffer-size=}'[use a buffer size of given size]: :_numbers -u bytes size K M G T' \
   '(-C --no-splice)'{-C,--no-splice}'[never use splice(), always use read/write]' \
   '(-R --remote)*'{-E,--skip-errors}"[skip read errors in input${Edesc}]" \
   '(-S --stop-at-size -R --remote)'{-S,--stop-at-size}'[stop after --size bytes have been transferred]' \
@@ -70,18 +70,6 @@ case $state in
       _pids $suf && ret=0
     fi
   ;;
-  size-unit)
-    if compset -P '<->'; then
-      _tags values units
-    else
-      _tags values
-    fi
-    while _tags; do
-      _requested values && _message -e values "$state_descr" && ret=0
-      _requested units expl unit compadd -o nosort - K M G T && ret=0
-      (( ret )) || break
-    done
-  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_rclone b/Completion/Unix/Command/_rclone
index 27b4dd926..a2e3429f5 100644
--- a/Completion/Unix/Command/_rclone
+++ b/Completion/Unix/Command/_rclone
@@ -62,7 +62,7 @@ _arguments -C \
   '--backup-dir[make backups into hierarchy based at specified directory]:directory:_directories' \
   '--bind[specify socal address to bind to for outgoing connections]:IPv4, IPv6 or name' \
   '--buffer-size[specify in memory buffer size when reading files for each --transfer]:size [16M]' \
-  '--bwlimit[specify bandwidth limit]:BwTimetable (kBytes/s or b|k|M|G suffix)' \
+  '--bwlimit[specify bandwidth limit]: :_numbers -u kBytes/s limit b k M G' \
   '--cache-dir[specify directory rclone will use for caching]:directory [~/.cache/rclone]:_directories' \
   '--checkers[specify number of checkers to run in parallel]:number [8]': \
   '(-c --checksum)'{-c,--checksum}'[skip based on checksum & size, not mod-time & size]' \
@@ -99,15 +99,15 @@ _arguments -C \
   '--log-format[specify comma separated list of log format options]:string ["date,time"]' \
   '--log-level[specify log level]:string [NOTICE]:(DEBUG INFO NOTICE ERROR)'  \
   '--low-level-retries[number of low level retries to do]:int [10]' \
-  '--max-age[only transfer files younger than this in s or suffix ms|s|m|h|d|w|M|y]:duration [default off]' \
+  '--max-age[only transfer files younger than specified age]: :_numbers -u seconds age ms\:milliseconds \:s\:seconds m\:minutes h\:hours d\:days w\:weeks M\:months y\:years' \
   '--max-backlog[maximum number of objects in sync or check backlog]:int [10000]' \
   '--max-delete[when synchronizing, limit the number of deletes]:delete limit [-1]' \
   '--max-depth[limit the recursion depth]:depth [-1]' \
-  '--max-size[only transfer files smaller than this in k or suffix b|k|M|G]:int [default off]' \
+  '--max-size[only transfer files smaller than specified size]: :_numbers -u kBytes size \:k M G' \
   '--max-transfer[maximum size of data to transfer]:int [default off]' \
   '--memprofile[write memory profile to file]:file:_files' \
-  '--min-age[only transfer files older than this in s or suffix ms|s|m|h|d|w|M|y]:duration [default off]' \
-  '--min-size[only transfer files bigger than this in k or suffix b|k|M|G]:int [default off]' \
+  '--min-age[only transfer files older than specified age]: :_numbers -u seconds age ms\:milliseconds \:s\:seconds m\:minutes h\:hours d\:days w\:weeks M\:months y\:years' \
+  '--min-size[only transfer files bigger than specified size]: :_numbers -u kBytes size \:k M G' \
   '--modify-window[specify max time delta to be considered the same]:duration [1ns]' \
   '--multi-thread-cutoff[use multi-threaded downloads for files above specified size]:size (250M)' \
   '--multi-thread-streams[specify max number of streams to use for multi-threaded downloads]:number (4)' \
diff --git a/Completion/Unix/Command/_rsync b/Completion/Unix/Command/_rsync
index b1a4f6046..eb906e974 100644
--- a/Completion/Unix/Command/_rsync
+++ b/Completion/Unix/Command/_rsync
@@ -99,7 +99,7 @@ _rsync() {
   _arguments -s \
     '*'{-v,--verbose}'[increase verbosity]' \
     {--no-v,--no-verbose}'[turn off --verbose]' \
-    '--bwlimit=[limit I/O bandwidth]:limit (KiB per second)' \
+    '--bwlimit=[limit I/O bandwidth]: :_numbers -f -u "KiB per second" -d 1g limit B K M G T P' \
     '--outbuf=[set output buffering]:buffering:(none line block)' \
     '--port=[specify alternate port number]:port:(873)' \
     '--address=[bind to the specified address]:bind address:_bind_addresses' \
@@ -181,7 +181,7 @@ _rsync() {
     {--no-W,--no-whole-file}'[turn off --whole-file]' \
     '(--cc --checksum-choice)'{--cc,--checksum-choice}'=[choose the checksum algorithms]:algorithm:_sequence -n 2 compadd - auto md4 md5 none' \
     '(-x --one-file-system)'{-x,--one-file-system}"[don't cross filesystem boundaries]" \
-    '(-B --block-size)'{-B+,--block-size=}'[force a fixed checksum block-size]:block size (bytes)' \
+    '(-B --block-size)'{-B+,--block-size=}'[force a fixed checksum block-size]: :_numbers -f -u bytes -d 1g "block size" B K M G T P' \
     '(-e --rsh)'{-e+,--rsh=}'[specify the remote shell to use]:remote-shell command:(rsh ssh)' \
     '--rsync-path=[specify path to rsync on the remote machine]:remote command' \
     '--ignore-existing[ignore files that already exist on receiving side]' \
@@ -199,10 +199,10 @@ _rsync() {
     '--force-change[affect user-/system-immutable files/dirs]' \
     '--force-uchange[affect user-immutable files/dirs]' \
     '--force-schange[affect system-immutable files/dirs]' \
-    '--max-delete=[do not delete more than NUM files]:number' \
-    '--max-size=[do not transfer any file larger than specified size]:number' \
+    "--max-delete=[don't delete more than NUM files]: :_numbers -f -u bytes size B K M G T P" \
+    "--max-size=[don't transfer any file larger than specified size]: :_numbers -f -u bytes size B K M G T P" \
     '--min-size=[do not transfer any file smaller than specified size]:number' \
-    '--max-alloc=[set limit to individual memory allocation]:size (bytes) [1g]' \
+    '--max-alloc=[set limit to individual memory allocation]: :_numbers -f -u bytes -d 1g size B K M G T P' \
     '(-P)--partial[keep partially transferred files]' \
     '--no-partial[turn off --partial]' \
     '--partial-dir=[put a partially transferred file into specified directory]:directory:_directories' \
diff --git a/Completion/Unix/Command/_zfs b/Completion/Unix/Command/_zfs
index 452e1160d..a83ce12f4 100644
--- a/Completion/Unix/Command/_zfs
+++ b/Completion/Unix/Command/_zfs
@@ -238,8 +238,8 @@ _zfs() {
 			':filesystem:_zfs_dataset -t fs -e "parent dataset"' \
 			- set2 \
 			'-s[Create sparse volume]' \
-			'-b[Set volblocksize]:blocksize:' \
-			'-V[Set size]:size:' \
+			'-b+[set volblocksize]:block size:_numbers -M 'm:{a-zA-Z}={A-Za-z}' -u bytes {k,M,G,T,P,E,Z}{,B}' \
+			'-V+[set size]:size:_numbers -M 'm:{a-zA-Z}={A-Za-z}' -u bytes {k,M,G,T,P,E,Z}{,B}' \
 			':volume:_zfs_dataset -t fs -e "parent dataset"'
 		;;
 
diff --git a/Completion/X/Command/_xset b/Completion/X/Command/_xset
index b35a6466b..adda47f01 100644
--- a/Completion/X/Command/_xset
+++ b/Completion/X/Command/_xset
@@ -91,8 +91,8 @@ _regex_arguments _xset_parse \
     \( "/(blank|noblank|expose|noexpose|default|on|activate|reset)$nul/" \
        ':option-s:screen saver:(blank noblank expose noexpose default on activate reset off)' \
     \| "/off$nul/" \( "/off$nul/" ':option-s-off-period:period off:(off)' \| \) \
-    \| "/[0-9]##$nul/" ':option-s-timeout:length:' \
-      \( "/[0-9]##$nul/" ':option-s-period:period:' \
+    \| "/[0-9]##$nul/" ':option-s-timeout: :_numbers -u seconds length' \
+      \( "/[0-9]##$nul/" ':option-s-period: :_numbers -u seconds period' \
       \| \) \
     \| \) \
   \| "/-r$nul/" "$guard" \


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-10-27 20:51   ` Oliver Kiddle
@ 2021-11-02 12:08     ` Jun T
  2021-11-07  1:16       ` Oliver Kiddle
  0 siblings, 1 reply; 10+ messages in thread
From: Jun T @ 2021-11-02 12:08 UTC (permalink / raw)
  To: zsh-workers


> 2021/10/28 5:51, Oliver Kiddle <opk@zsh.org> wrote:
> 
> On further relection, for handling unit suffixes, we ideally also want
> to complete them.

Yes, for example _head/_tail completes the unit suffix in a dirty way.

> Units are nearly always only applicable on the end of numbers. So how
> about we add _numbers as a helper function to take care of completing
> unit suffixes.
(snip)
> The attached patch shows a possible approach.

Thank you for the patch. It works great. I can't think of any better idea.

I thought I could improve _head/_tail by using _numbers, but these
command (and maybe some others) accept a '+' sign before the number
(in addition to '-'). So either let _numbers accept '+' if the option -N
is given, or add another option, say -P, to make it accept '+'.

> I've used %m for units (measurement
> - u is taken for underline), %r for range (e.g 1-30), %o for default
> (otherwise - d is taken), %x for the list of suffixes (s is taken) and
> %h for the unadorned description (heading).
(snip)
> Corresponding to these are uppercase forms for use with the zformat
> conditional. This allows, e.g. %(M. %F{28}(%m%)%f.) for adding the units
> conditionally.

As you know, %(M is equivalent to %0(M. We need to use %2(M here since
> +  (( $#units )) && formats+=( m:${units[2]} M:${#units} ) desc+=" ($units[2])"
${#units} is 2 if option -u is given (and 0 otherwise).
For %R %O %X and %H we need to use %1(R etc.
At least we need to set M to 1 as others.
Or is it better to set them to 0 so that %(M etc. works?

> _numbers also constructs the list of suffixes using zformat; looked up
> using unit-suffixes as the tag.

This works fine (some users may feel it too complicated, but I think it is
unavoidable).

Isn't it better to use some default format if the format style is not set for
the unit-suffixes tag? Maybe '%(d.%U.)%x%(d.%u.)%(r..|)' ?

I guess most of the users just set the format style for the descriptions tag to
'%B%d%b'. Currently %d gives 'header (unit) [default]'. I guess some (many?)
users also want to see the possible suffixes before they start typing the
number. But since () and [] are already used the only possibility would be {},
like '{k|M|G|T}' But some users may not understand that this is a list of
possible suffixes. Probably it is enough to show an example format, such as
'%B%d%(X.{%x}.)%b', in the document?

> This _numbers function takes quite a few options. Should we allow a way
> to configure how a range is presented - this hardcodes a dash separating
> min and max and has no way to indicate for decimal numbers whether the
> min and max values are inclusive?

I think we need not to indicate that min/max is inclusive.
For the '-' (dash) as a separator: if min/max is negative it would look like
'-5--2'. But I guess negative min/max are quite rare and maybe we don't
need to care about such cases. Or we could use '[-5,-2]' (a mathematical
notation for a closed interval), but it may be confused with the default value.


> diff --git a/Completion/Unix/Command/_zfs b/Completion/Unix/Command/_zfs
(snip)
> -			'-b[Set volblocksize]:blocksize:' \
> -			'-V[Set size]:size:' \
> +			'-b+[set volblocksize]:block size:_numbers -M 'm:{a-zA-Z}={A-Za-z}' -u bytes {k,M,G,T,P,E,Z}{,B}' \
> +			'-V+[set size]:size:_numbers -M 'm:{a-zA-Z}={A-Za-z}' -u bytes {k,M,G,T,P,E,Z}{,B}' \

There are typos here. The two lines should be

                        '-b+[set volblocksize]: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes blocksize {k,M,G,T,P,E,Z}{,B}' \
                        '-V+[set size]: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size {k,M,G,T,P,E,Z}{,B}' \




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-11-02 12:08     ` Jun T
@ 2021-11-07  1:16       ` Oliver Kiddle
  2021-11-08 10:56         ` Jun T
  0 siblings, 1 reply; 10+ messages in thread
From: Oliver Kiddle @ 2021-11-07  1:16 UTC (permalink / raw)
  To: Jun T; +Cc: zsh-workers

On 2 Nov, Jun T wrote:
> > Units are nearly always only applicable on the end of numbers. So how
> > about we add _numbers as a helper function to take care of completing
> > unit suffixes.

> I thought I could improve _head/_tail by using _numbers, but these
> command (and maybe some others) accept a '+' sign before the number
> (in addition to '-'). So either let _numbers accept '+' if the option -N
> is given, or add another option, say -P, to make it accept '+'.

I'd avoid -P because it's a common compadd option. zparseopts doesn't
appear to cope with -+ so we'd need to pick another letter. In the
case of head and tail an initial + does have an actual meaning and
it's useful to have the description saying "start at the specified
byte/line". So I'm not sure it's especially useful to add. You can
always do compset -P '+' first.

_head and _tail actually take the approach of completing digits 0-9 with
no suffix. I'm not sure how useful that is - perhaps for someone who
loves menu completion. For head and tail, someone may want to set the
fake style to prime them with specific values such as $(( LINES - 1 ))
and $(( LINES/2 - 1 )). Completing 0-9 gets in the way of that.

Do you see an issue with the behaviour of the following:
  local alts
  [[ -z $PREFIX ]] && alts=( 'sign:sign:((-\:"print all but the last specified bytes/lines" +\:"print the first specified bytes/lines (default)"))' )
  compset -P '+'
  alts+=( 'numbers: :_numbers number b\:512 K\:1024 KB\:1000 M\:1024\^2 MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4' )
  _alternative $alts

> > I've used %m for units (measurement
> > - u is taken for underline), %r for range (e.g 1-30), %o for default
> > (otherwise - d is taken), %x for the list of suffixes (s is taken) and
> > %h for the unadorned description (heading).
> (snip)
> > Corresponding to these are uppercase forms for use with the zformat
> > conditional. This allows, e.g. %(M. %F{28}(%m%)%f.) for adding the units
> > conditionally.
>
> As you know, %(M is equivalent to %0(M. We need to use %2(M here since
> > +  (( $#units )) && formats+=( m:${units[2]} M:${#units} ) desc+=" ($units[2])"
> ${#units} is 2 if option -u is given (and 0 otherwise).
> For %R %O %X and %H we need to use %1(R etc.
> At least we need to set M to 1 as others.

My intention was for it to be 1 like the others. I had started out with
them all like that - $#range etc - and changed them to 1 later. Units
somehow got missed so thanks for spotting that. I had initially given
some thought to whether we might be able to do some sort of alignment,
e.g. right aligning the suffixes but zformat isn't really up to that.

> Or is it better to set them to 0 so that %(M etc. works?

The absence of any M: spec is treated as 0, unfortunately. In this case
M: is only there as a way to flag that an m: spec is present. So we end
up needing to use the else condition or the literal 1:

  either %(M.. (%m)) or %1(M. (%m).)

I don't like that much either. Perhaps we should add an option to
zformat so that we can simply use $(m. %(m).) with no need for the
uppercase spec.

Currently that would do math evaluation on whatever is used in the m:
spec and defaults to 0. An option to zformat would not be backward
compatible for the format style (not that we ever used the feature but
someone might). A different spec form such as m+:$units would still
suffer from the problem that in the absence of the spec %(m. would
give you the true branch. Any thoughts or ideas? Or should we just stick
with M:1 and needing the else branch or a 1.

> This works fine (some users may feel it too complicated, but I think it is
> unavoidable).

The formats look worse to read than when constructing them. Many users
just install a "theme" and will never see this anyway. And as you say,
it's probably unavoidable.

> Isn't it better to use some default format if the format style is not set for
> the unit-suffixes tag? Maybe '%(d.%U.)%x%(d.%u.)%(r..|)' ?

Yes, probably. The main reason I didn't do that was because it forces me
to come up with a default format. I'll take your suggestion.

> I guess most of the users just set the format style for the descriptions tag to
> '%B%d%b'. Currently %d gives 'header (unit) [default]'. I guess some (many?)
> users also want to see the possible suffixes before they start typing the
> number. But since () and [] are already used the only possibility would be {},
> like '{k|M|G|T}' But some users may not understand that this is a list of
> possible suffixes. Probably it is enough to show an example format, such as
> '%B%d%(X.{%x}.)%b', in the document?

If we do want to include it, braces are probably best in terms of people
being able to guess correctly at their meaning. Daniel's point about
locality aside, I also prefer them at the end because I think of them as
a sort of lookahead. And there may be scope for other forms of lookahead
syntax hints in the future. I don't have a strong opinion on it but I'm
inclined to just leave them out for now. If someone wants to add them, I
won't object.

Your example is at least a short and understandable example even if it
doesn't divide things up with bold for the header, italics for the units
and a lighter colour for the suffixes. I'll see if it can fit in in the
documentation.

> > This _numbers function takes quite a few options. Should we allow a way
> > to configure how a range is presented - this hardcodes a dash separating
> > min and max and has no way to indicate for decimal numbers whether the
> > min and max values are inclusive?
>
> I think we need not to indicate that min/max is inclusive.
> For the '-' (dash) as a separator: if min/max is negative it would look like
> '-5--2'. But I guess negative min/max are quite rare and maybe we don't
> need to care about such cases. Or we could use '[-5,-2]' (a mathematical
> notation for a closed interval), but it may be confused with the default value.

Negative maximums are probably not common in practice. The mathematical
notation includes things like (0,1] which I find too jarring. Given
something like (0<x<=1) or Rust-style 0..=1, I at least don't need to
check wikipedia to know which side is inclusive.

If someone really wants commas or some unicode like … then we could
add a style later. The same goes for non-inclusive bounds and whatever
formatting mess is needed to allow different notations.

> There are typos here. The two lines should be

I'll include these when I send an updated patch with documentation.

Thanks for taking a look at this, picking out the mistakes and for the
thoughtful comments.

Oliver


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-11-07  1:16       ` Oliver Kiddle
@ 2021-11-08 10:56         ` Jun T
  2021-11-10 23:08           ` PATCH: zformat (was list units in brackets at the end of completion group descriptions) Oliver Kiddle
  2021-11-22 21:24           ` PATCH: list units in brackets at the end of completion group descriptions Oliver Kiddle
  0 siblings, 2 replies; 10+ messages in thread
From: Jun T @ 2021-11-08 10:56 UTC (permalink / raw)
  To: zsh-workers


> 2021/11/07 10:16, Oliver Kiddle <opk@zsh.org> wrote:
> 
> Do you see an issue with the behaviour of the following:
>  local alts
>  [[ -z $PREFIX ]] && alts=( 'sign:sign:((-\:"print all but the last specified bytes/lines" +\:"print the first specified bytes/lines (default)"))' )
>  compset -P '+'
>  alts+=( 'numbers: :_numbers number b\:512 K\:1024 KB\:1000 M\:1024\^2 MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4' )
>  _alternative $alts

It works if we use either "compset -P '[-+]'" or "_numbers -N".


>> Or is it better to set them to 0 so that %(M etc. works?
> 
> The absence of any M: spec is treated as 0, unfortunately.

Oops, sorry. I thought I've tested that %(M is false if M: is not
given, but it seems I did the test in a wrong way.

> Perhaps we should add an option to
> zformat so that we can simply use $(m. %(m).) with no need for the
> uppercase spec.
(snip)
>  An option to zformat would not be backward
> compatible for the format style (not that we ever used the feature but
> someone might).
(snip)
> Or should we just stick
> with M:1 and needing the else branch or a 1.


I think %1(M is enough for the current purpose.
(or set %M to 0 if unit is given, and nonzero otherwise; i.e., never leave
it undefined).

But, in zshcompsys(1), neither the section for the 'format' style nor the
section for '_description' says that the ternary expression of zformat can
be used (or is it mentioned somewhere?). So if adding a new option to
zformat would have wider use, then I think adding the option and using it in
_description would not break the compatibility.


The format for the unit-suffixes tag accepts %d that is set to 0 if
the suffix is marked as the default (such as :k:kilo) and 1 otherwise.
This may cause some confusion, but I feel it is OK.

---
Jun

^ permalink raw reply	[flat|nested] 10+ messages in thread

* PATCH: zformat (was list units in brackets at the end of completion group descriptions)
  2021-11-08 10:56         ` Jun T
@ 2021-11-10 23:08           ` Oliver Kiddle
  2021-11-22 21:24           ` PATCH: list units in brackets at the end of completion group descriptions Oliver Kiddle
  1 sibling, 0 replies; 10+ messages in thread
From: Oliver Kiddle @ 2021-11-10 23:08 UTC (permalink / raw)
  To: Jun T; +Cc: zsh-workers

On 8 Nov, Jun T wrote:
> I think %1(M is enough for the current purpose.
> (or set %M to 0 if unit is given, and nonzero otherwise; i.e., never leave
> it undefined).

We can't retrospectively set M to nonzero for users of older versions of
zsh. I'd like to be able to configure a format that'll work with new or
old versions.

> But, in zshcompsys(1), neither the section for the 'format' style nor the
> section for '_description' says that the ternary expression of zformat can
> be used (or is it mentioned somewhere?). So if adding a new option to
> zformat would have wider use, then I think adding the option and using it in
> _description would not break the compatibility.

As you say, it doesn't appear to be documented anywhere and I can't see
any cases where we pass values that are intended for use with it. Given
that math evaluation will treat words as variable names, it won't even
be useful with the specs that we do pass. Being able to check for the
presence of specs is more useful so how about the following patch?

This adds -F as an alternative to -f. That was largely chosen out of
lazyness because a second option to be combined with -f requires proper
option parsing rather than the existing string compare on the first
argument. But we can do that if someone is bothered enough to argue in
favour.

With this form, where d:word is passed, a %(d ternary will follow the
true path. Numeric arguments allow for a string width test. I'm not sure
how useful that is but it is consistent with the basic presence checking
(absence being a width of zero) and could have uses like for truncating
long suffix lists.

Or if there's objections to this then the %1(M approach does work.

Oliver

diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description
index bdb4007a6..5b54484c7 100644
--- a/Completion/Base/Core/_description
+++ b/Completion/Base/Core/_description
@@ -78,7 +78,7 @@ shift 2
 if [[ -z "$1" && $# -eq 1 ]]; then
   format=
 elif [[ -n "$format" ]]; then
-  zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}"
+  zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}"
 fi
 
 if [[ -n "$gname" ]]; then
diff --git a/Completion/Base/Core/_message b/Completion/Base/Core/_message
index 4d5645eaf..dbeed4a88 100644
--- a/Completion/Base/Core/_message
+++ b/Completion/Base/Core/_message
@@ -39,7 +39,7 @@ else
 fi
 
 if [[ -n "$format$raw" ]]; then
-  [[ -z "$raw" ]] && zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}"
+  [[ -z "$raw" ]] && zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}"
   builtin compadd "$gopt[@]" -x "$format"
   _comp_mesg=yes
 fi
diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo
index 41d3dfdb0..3cf9e5028 100644
--- a/Doc/Zsh/mod_zutil.yo
+++ b/Doc/Zsh/mod_zutil.yo
@@ -150,8 +150,9 @@ enditem()
 )
 findex(zformat)
 xitem(tt(zformat -f) var(param) var(format) var(spec) ...)
+xitem(tt(zformat -F) var(param) var(format) var(spec) ...)
 item(tt(zformat -a) var(array) var(sep) var(spec) ...)(
-This builtin provides two different forms of formatting. The first form 
+This builtin provides different forms of formatting. The first form
 is selected with the tt(-f) option. In this case the var(format)
 string will be modified by replacing sequences starting with a percent 
 sign in it with strings from the var(spec)s.  Each var(spec) should be
@@ -195,7 +196,13 @@ outputs "The answer is 'yes'." to tt(REPLY) since the value for the format
 specifier tt(c) is 3, agreeing with the digit argument to the ternary
 expression.
 
-The second form, using the tt(-a) option, can be used for aligning
+With tt(-F) instead of tt(-f), ternary expressions choose between the
+`true' or `false' text on the basis of whether the format specifier is
+present and non-empty.  A test number indicates a minimum width for the
+value given in the format specifier. Negative numbers reverse this,
+so the test is for whether the value exceeds a maximum width.
+
+The form, using the tt(-a) option, can be used for aligning
 strings.  Here, the var(spec)s are of the form
 `var(left)tt(:)var(right)' where `var(left)' and `var(right)' are
 arbitrary strings.  These strings are modified by replacing the colons
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 691ba6c2f..2f17c03f1 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -776,10 +776,12 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
  *   ousedp	(*outp)[*ousedp] is where to write next
  *   olenp	*olenp is the size allocated for *outp
  *   endchar    Terminator character in addition to `\0' (may be '\0')
+ *   presence   -F: Ternary expressions test emptyness instead
  *   skip	If 1, don't output, just parse.
  */
 static char *zformat_substring(char* instr, char **specs, char **outp,
-			       int *ousedp, int *olenp, int endchar, int skip)
+			       int *ousedp, int *olenp, int endchar,
+			       int presence, int skip)
 {
     char *s;
 
@@ -813,20 +815,29 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 	    if (testit && STOUC(*s)) {
 		int actval, testval, endcharl;
 
-		/*
-		 * One one number is useful for ternary expressions.
-		 * Remember to put the sign back.
-		 */
+		/* Only one number is useful for ternary expressions. */
 		testval = (min >= 0) ? min : (max >= 0) ? max : 0;
-		if (right)
-		    testval *= -1;
 
-		if (specs[STOUC(*s)])
-		    actval = (int)mathevali(specs[STOUC(*s)]);
-		else
-		    actval = 0;
-		/* zero means values are equal, i.e. true */
-		actval -= testval;
+		if (specs[STOUC(*s)] && *specs[STOUC(*s)]) {
+		    if (presence) {
+			if (testval)
+#ifdef MULTIBYTE_SUPPORT
+			    if (isset(MULTIBYTE))
+				actval = MB_METASTRWIDTH(specs[STOUC(*s)]);
+			    else
+#endif
+				actval = strlen(specs[STOUC(*s)]);
+		        else
+			    actval = 1;
+			actval = right ? (testval < actval) : (testval >= actval);
+		    } else {
+			if (right) /* put the sign back */
+			    testval *= -1;
+			/* zero means values are equal, i.e. true */
+			actval = (int)mathevali(specs[STOUC(*s)]) - testval;
+		    }
+		} else
+		    actval = presence ? !right : testval;
 
 		/* careful about premature end of string */
 		if (!(endcharl = *++s))
@@ -837,10 +848,10 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 		 * vice versa... unless we are already skipping.
 		 */
 		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
-					    olenp, endcharl, skip || actval)))
+			    olenp, endcharl, presence, skip || actval)))
 		    return NULL;
 		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
-					    olenp, ')', skip || !actval)))
+			    olenp, ')', presence, skip || !actval)))
 		    return NULL;
 	    } else if (skip) {
 		continue;
@@ -912,6 +923,7 @@ static int
 bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 {
     char opt;
+    int presence = 0;
 
     if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
 	zwarnnam(nam, "invalid argument: %s", args[0]);
@@ -920,6 +932,9 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
     args++;
 
     switch (opt) {
+    case 'F':
+	presence = 1;
+	/* fall-through */
     case 'f':
 	{
 	    char **ap, *specs[256] = {0}, *out;
@@ -939,7 +954,8 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    }
 	    out = (char *) zhalloc(olen = 128);
 
-	    zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0);
+	    zformat_substring(args[1], specs, &out, &oused, &olen, '\0',
+		    presence, 0);
 	    out[oused] = '\0';
 
 	    setsparam(args[0], ztrdup(out));
diff --git a/Test/V13zformat.ztst b/Test/V13zformat.ztst
index 982866e13..36038fa6e 100644
--- a/Test/V13zformat.ztst
+++ b/Test/V13zformat.ztst
@@ -58,6 +58,30 @@
 0:nested conditionals test
 >yes
 
+ () {
+   zformat -f 1 '%(w.zero.fail) %(x.fail.present) %(y.empty.fail) %(z.missing.fail)' w:0 x:1 y:
+   zformat -F 2 '%(w.zero.fail) %(x.present.fail) %(y.fail.empty) %(z.fail.missing)' w:0 x:1 y:
+   echo $1
+   echo $2
+ }
+0:conditionals with empty and missing values
+>zero present empty missing
+>zero present empty missing
+
+ () {
+   local l
+   for l in 0 1 2 3; do
+     zformat -F 1 "%$l(a.a.A)%$l(b.b.B)%$l(c.c.C)%$l(d.d.D)" a: b:1 c:12 d:123
+     zformat -F 2 "%-$l(a.a.A)%-$l(b.b.B)%-$l(c.c.C)%-$l(d.d.D)" a: b:1 c:12 d:123
+     print - $1 $2
+   done
+ }
+0:minimum and maximum widths
+>Abcd aBCD
+>ABcd abCD
+>ABCd abcD
+>ABCD abcd
+
  zformat -a argv . foo:lorem ipsum:bar bazbaz '\\esc\:ape'
  print -rl -- "$@"
 0:basic -a test


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: list units in brackets at the end of completion group descriptions
  2021-11-08 10:56         ` Jun T
  2021-11-10 23:08           ` PATCH: zformat (was list units in brackets at the end of completion group descriptions) Oliver Kiddle
@ 2021-11-22 21:24           ` Oliver Kiddle
  1 sibling, 0 replies; 10+ messages in thread
From: Oliver Kiddle @ 2021-11-22 21:24 UTC (permalink / raw)
  To: zsh-workers

Attached is an updated version of the patch from 49532 to add a helper
for completing numbers with unit suffixes and for separating out
defaults, ranges and units in descriptions.

I think I've handled everything from Jun T's comments. Documentation is
added. The uppercase format specifiers are gone as this builds on the
earlier zformat -F patch. I've made _numbers slightly more aggressive in
emptying compstate[insert] because it is not uncommon for _alternative
to combine numbers with certain fixed values like "all", "any" or "none"
and completing them unconditionally is not really helpful.

Completions for head and tail also now make use of _numbers along with a
few other commands.

Oliver

diff --git a/Completion/BSD/Command/_ipfw b/Completion/BSD/Command/_ipfw
index 2354a70fe..49d0ef1e8 100644
--- a/Completion/BSD/Command/_ipfw
+++ b/Completion/BSD/Command/_ipfw
@@ -249,8 +249,8 @@ actions=(
     $'/(pipe|queue|sched)[ \t\0]/' -'pqs=${match%?}' ':dummynet-commands:dummynet configuration:$ca pipe queue sched'
     $word ': _message -e numbers number'
     $word ':options:config:$ca config'
-    \( $'/bw[ \t\0]/' \( $'/<->/' ': _guard "[0-9]#" bandwidth'
-         $word ':units:unit:compadd -M "m:{a-z}={A-Z}" {K,M,G}{bit,Byte}/s'
+    \( $'/bw[ \t\0]/'
+      \( $word ':bandwidths: :_numbers -M "m:{a-z}={A-Z}" bandwidth {K,M,G}{bit,Byte}/s'
       \| $word ':devices:device:_net_interfaces -qS " "' \)
     \| $'/delay[ \t\0]/' $word ': _message -e numbers "propagation delay (ms)"'
     \| $'/burst[ \t\0]/' $word ': _message -e numbers "size (bytes)"'
diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description
index 5b54484c7..368b41ee2 100644
--- a/Completion/Base/Core/_description
+++ b/Completion/Base/Core/_description
@@ -2,6 +2,7 @@
 
 local name nopt xopt format gname hidden hide match opts tag
 local -a ign gropt sort
+local -a match mbegin mend
 
 opts=()
 
@@ -78,6 +79,13 @@ shift 2
 if [[ -z "$1" && $# -eq 1 ]]; then
   format=
 elif [[ -n "$format" ]]; then
+  if [[ -z $2 ]]; then
+    argv+=( h:${1%%( ##\((#b)([^\)]#[^0-9-][^\)]#)(#B)\)|)( ##\((#b)([0-9-]##)(#B)\)|)( ##\[(#b)([^\]]##)(#B)\]|)} )
+    [[ -n $match[1] ]] && argv+=( m:$match[1] )
+    [[ -n $match[2] ]] && argv+=( r:$match[2] )
+    [[ -n $match[3] ]] && argv+=( o:$match[3] )
+  fi
+
   zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}"
 fi
 
diff --git a/Completion/Base/Utility/_numbers b/Completion/Base/Utility/_numbers
new file mode 100644
index 000000000..97bb8b4c8
--- /dev/null
+++ b/Completion/Base/Utility/_numbers
@@ -0,0 +1,87 @@
+#autoload
+
+# Usage: _numbers [compadd options] [-t tag] [-f|-N] [-u units] [-l min] [-m max] \
+#                 [-d default] ["description"] [unit-suffix...]
+
+#   -t : specify a tag (defaults to 'numbers')
+#   -u : indicate the units, e.g. seconds
+#   -l : lowest possible value
+#   -m : maximum possible value
+#   -d : default value
+#   -N : allow negative numbers (implied by range including a negative)
+#   -f : allow decimals (float)
+
+# For a unit-suffix, an initial colon indicates a unit that asserts the default
+# otherwise, colons allow for descriptions, e.g:
+
+#   :s:seconds m:minutes h:hours
+
+# unit-suffixes are not sorted by the completion system when listed
+# Specify them in order of magnitude, this tends to be ascending unless
+# the default is of a higher magnitude, in which case, descending.
+# So for, example
+#   bytes kB MB GB
+#   s ms us ns
+# Where the compadd options include matching control or suffixes, these
+# are applied to the units
+
+# For each unit-suffix, the format style is looked up with the
+# unit-suffixes tag and the results concatenated. Specs used are:
+#   x : the suffix
+#   X : suffix description
+#   d : indicate suffix is for the default unit
+#   i : list index
+#   r : reverse list index
+# The latter three of these are useful with ternary expressions.
+
+# _description is called with the x token set to make the completed
+# list of suffixes available to the normal format style
+
+local desc tag range suffixes suffix suffixfmt pat='<->' partial=''
+local -a expl formats
+local -a default max min keep tags units
+local -i i
+local -A opts
+
+zparseopts -K -D -A opts M+:=keep q:=keep s+:=keep S+:=keep J+: V+: 1 2 o+: n F: x+: X+: \
+  t:=tags u:=units l:=min m:=max d:=default f=type e=type N=type
+
+desc="${1:-number}" tag="${tags[2]:-numbers}"
+(( $# )) && shift
+
+[[ -n ${(M)type:#-f} ]] && pat='(<->.[0-9]#|[0-9]#.<->|<->)' partial='(|.)'
+[[ -n ${(M)type:#-N} || $min[2] = -* || $max[2] = -* ]] && \
+    pat="(|-)$pat" partial="(|-)$partial"
+
+if (( $#argv )) && compset -P "$pat"; then
+  zstyle -s ":completion:${curcontext}:units" list-separator sep || sep=--
+  _description -V units expl unit
+  disp=( ${${argv#:}/:/ $sep } )
+  compadd -M 'r:|/=* r:|=*' -d disp "$keep[@]" "$expl[@]" - ${${argv#:}%%:*}
+  return
+elif [[ -prefix $~pat || $PREFIX = $~partial ]]; then
+  formats=( "h:$desc" )
+  (( $#units )) && formats+=( m:${units[2]} ) desc+=" ($units[2])"
+  (( $#min )) && range="$min[2]-"
+  (( $#max )) && range="${range:--}$max[2]"
+  [[ -n $range ]] && formats+=( r:$range ) desc+=" ($range)"
+  (( $#default )) && formats+=( o:${default[2]} ) desc+=" [$default[2]]"
+
+  zstyle -s ":completion:${curcontext}:unit-suffixes" format suffixfmt || \
+      suffixfmt='%(d.%U.)%x%(d.%u.)%(r..|)'
+  for ((i=0;i<$#;i++)); do
+    zformat -f suffix "$suffixfmt" "x:${${argv[i+1]#:}%%:*}" \
+        "X:${${argv[i+1]#:}#*:}" "d:${#${argv[i+1]}[1]#:}" \
+	i:i r:$(( $# - i - 1))
+    suffixes+="$suffix"
+  done
+  [[ -n $suffixes ]] && formats+=( x:$suffixes )
+
+  _comp_mesg=yes
+  _description -x $tag expl "$desc" $formats
+  [[ $compstate[insert] = *unambiguous* ]] && compstate[insert]=
+  compadd "$expl[@]"
+  return 0
+fi
+
+return 1
diff --git a/Completion/Linux/Command/_btrfs b/Completion/Linux/Command/_btrfs
index bb0f724e6..65cf067aa 100644
--- a/Completion/Linux/Command/_btrfs
+++ b/Completion/Linux/Command/_btrfs
@@ -147,16 +147,16 @@ while (( $#state )); do
             '--tbytes[show sizes in TiB, or TB with --si]'
           )
         ;|
-        filesystem:resize) args+=( '1:size:_guard "(|+|-)[0-9]#[GKM]"' '2:path:->mounts' );;
+        filesystem:resize) args+=( '1: :_numbers -u bytes -N size K M G T P E' '2:path:->mounts' );;
         filesystem:defragment)
           args+=( '!-v'
             '-r[defragment files recursively]'
             '-c+[compress files while defragmenting]::compression algorithm:(zlib lzo zstd)'
             '-r[defragment files recursively]'
             '-f[flush after defragmenting]'
-            '-s[start position]:byte position'
-            '-l[defragment limited number of bytes]:length (bytes)'
-            '-t[defragment only files over a certain size]:minimum size (bytes) [32M]'
+            '-s[start position]: :_numbers -u bytes -d "beginning of file" offset K M G T P E'
+            '-l[defragment limited number of bytes]: :_numbers -u bytes length K M G T P E'
+            '-t[defragment only extents up to a certain size]: :_numbers -u bytes -d 32M "maximum extent size" K M G T P E'
             '*:file:_files'
           )
         ;;
diff --git a/Completion/Unix/Command/_dd b/Completion/Unix/Command/_dd
index e5c5e63ce..10682bc8e 100644
--- a/Completion/Unix/Command/_dd
+++ b/Completion/Unix/Command/_dd
@@ -1,18 +1,19 @@
 #compdef dd gdd
 
-local -a vals conv flags
+local -a vals conv flags units
 local variant
 
+units=( w:word b:block k:1024 m g t )
 _pick_variant -r variant gnu=GNU $OSTYPE --version
 
 vals=(
-  '(ibs obs)bs[block size]:block size (bytes)'
-  'cbs[conversion buffer size]:buffer size (bytes)'
+  '(ibs obs)bs[block size]: :_numbers -u bytes "block size" $units'
+  'cbs[conversion buffer size]: :_numbers -u bytes "buffer size" $units'
   'conv[specify conversions to apply]: :_values -s , conversion $conv'
   'count[number of input blocks to copy]:blocks'
-  '(bs)ibs[input block size]:block size (bytes)'
+  '(bs)ibs[input block size]: :_numbers -u bytes -d 512 "block size" $units'
   'if[specify input file]:input file:_tilde_files'
-  '(bs)obs[output block size]:block size (bytes)'
+  '(bs)obs[output block size]: :_numbers -u bytes -d 512 "block size" $units'
   'of[specify output file]:output file:_tilde_files'
   'seek[output blocks initially skipped]:blocks'
   'skip[input blocks initially skipped]:blocks'
@@ -63,7 +64,7 @@ case $variant in
   freebsd*)
     vals+=(
       'fillchar[specify padding character]:character'
-      'speed[limit copying speed]:speed (bytes/second)'
+      'speed[limit copying speed]: :_numbers -u bytes/second speed $units'
     )
     conv+=(
       '(pareven parnone parodd parset)'{pareven,parnone,parodd,parset}
@@ -75,6 +76,7 @@ case $variant in
     )
     flags+=( fullblock noatime nocache count_bytes skip_bytes seek_bytes )
     conv+=( excl nocreat fdatasync fsync )
+    units=( c:1 w:2 b:512 kB:1000 K:1024 MB:1000^2 M:1024\^2 GB G TB T PB P EB E ZB Z YB Y )
   ;;
   netbsd*)
     vals+=(
diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 7c7fb22bc..c757376b0 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -3720,8 +3720,8 @@ _git-fast-import () {
                                                            now\:"use current time and timezone"' \
     '--done[terminate with error if there is no "done" command at the end of the stream]' \
     '--force[force updating modified existing branches]' \
-    '--max-pack-size=-[maximum size of each packfile]: : __git_guard_bytes' \
-    '--big-file-threshold=-[maximum size of blob to create deltas for]: : __git_guard_bytes' \
+    '--max-pack-size=-[maximum size of each packfile]: : __git_guard_bytes -d unlimited size' \
+    '--big-file-threshold=-[maximum size of blob to create deltas for]: : __git_guard_bytes -d 512m size' \
     '--depth=-[maximum delta depth for blob and tree deltification]: :__git_guard_number "maximum delta depth"' \
     '--active-branches=-[maximum number of branches to maintain active at once]: :__git_guard_number "maximum number of branches"' \
     '--export-marks=-[dump internal marks table when complete]: :_files' \
@@ -7506,7 +7506,7 @@ __git_guard_number () {
 
 (( $+functions[__git_guard_bytes] )) ||
 __git_guard_bytes () {
-  _guard '[[:digit:]]#([kKmMgG]|)' ${*:-size}
+  _numbers -u bytes ${*:-size} k m g
 }
 
 (( $+functions[__git_datetimes] )) ||
diff --git a/Completion/Unix/Command/_head b/Completion/Unix/Command/_head
index f25c97c83..0771b1e4d 100644
--- a/Completion/Unix/Command/_head
+++ b/Completion/Unix/Command/_head
@@ -32,20 +32,14 @@ _arguments -C -s -S $opts : $args '*:file:_files' && return 0
 
 case $state in
   (number)
-    local mlt sign digit
-    mlt='multiplier:multiplier:((b\:512 K\:1024 KB\:1000 M\:1024\^2'
-    mlt+=' MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4))'
-    sign='sign:sign:((-\:"print all but the last specified bytes/lines"'
-    sign+=' +\:"print the first specified bytes/lines (default)"))'
-    digit='digits:digit:(0 1 2 3 4 5 6 7 8 9)'
-    if compset -P '(-|+|)[0-9]##'; then
-      _alternative $mlt $digit && ret=0
-    elif [[ -z $PREFIX ]]; then
-      _alternative $sign $digit && ret=0
-    elif compset -P '(+|-)'; then
-      _alternative $digit && ret=0
-    fi
-    ;;
+    local alts
+    [[ -z $PREFIX ]] && alts=(
+      'sign:sign:((-\:"print all but the last specified bytes/lines" +\:"print the first specified bytes/lines (default)"))'
+    )
+    compset -P '+'
+    alts+=( 'numbers: :_numbers -N $state_descr b\:512 K\:1024 KB\:1000 M\:1024\^2 MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4' )
+    _alternative $alts && ret=0
+  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_killall b/Completion/Unix/Command/_killall
index 36accb2e0..3ddd36341 100644
--- a/Completion/Unix/Command/_killall
+++ b/Completion/Unix/Command/_killall
@@ -38,15 +38,9 @@ if _pick_variant psmisc=PSmisc unix --version; then
 
   case $state in
     (time)
-      local -a units=( 's:seconds' 'm:minutes' 'h:hours' 'd:days'
-			'w:weeks' 'M:months' 'y:years' )
-      if compset -P '[0-9]##(|.[0-9]#)'; then
-	_alternative 'float-numbers:: _message "float number"' \
-		    'units:: _describe unit units' && ret=0
-      else
-	_message 'float number and unit' && ret=0
-      fi
-      ;;
+      _numbers -fN age 's:seconds' 'm:minutes' 'h:hours' 'd:days' \
+          'w:weeks' 'M:months' 'y:years'
+    ;;
   esac
 
   return ret
diff --git a/Completion/Unix/Command/_pv b/Completion/Unix/Command/_pv
index 68f8e8586..d02d3a35d 100644
--- a/Completion/Unix/Command/_pv
+++ b/Completion/Unix/Command/_pv
@@ -25,7 +25,7 @@ _arguments -s -S $args \
   '(-q --quiet)'{-q,--quiet}"[don't output any transfer information at all, useful with -L]" \
   '(-W --wait)'{-W,--wait}'[display nothing until first byte transferred]' \
   '(-D --delay-start -R --remote)'{-D+,--delay-start=}'[display nothing until delay has passed]:delay (seconds)' \
-  '(-s --size)'{-s+,--size=}'[set estimated data size]:size (bytes):->size-unit' \
+  '(-s --size)'{-s+,--size=}'[set estimated data size]: :_numbers -u bytes size K M G T' \
   '(-l --line-mode -R --remote)'{-l,--line-mode}'[count lines instead of bytes]' \
   '(-0 --null -l --line-mode)'{-0,--null}'[lines are null-terminated]' \
   '(-i --interval)'{-i+,--interval=}'[update every after specified interval]:interval (seconds) [1]' \
@@ -34,8 +34,8 @@ _arguments -s -S $args \
   '(-N --name)'{-N+,--name=}'[prefix visual information with given name]:name' \
   '(-f --force -R --remote)'{-f,--force}'[output even if standard error is not a terminal]' \
   '(-c --cursor -R --remote)'{-c,--cursor}'[use cursor positioning escape sequences]' \
-  '(-L --rate-limit)'{-L+,--rate-limit=}'[limit transfer rate]:rate (bytes per second):->size-unit' \
-  '(-B --buffer-size)'{-B+,--buffer-size=}'[use a buffer size of given size]:size (bytes):->size-unit' \
+  '(-L --rate-limit)'{-L+,--rate-limit=}'[limit transfer rate]: :_numbers -u "bytes per second" rate K M G T' \
+  '(-B --buffer-size)'{-B+,--buffer-size=}'[use a buffer size of given size]: :_numbers -u bytes size K M G T' \
   '(-C --no-splice)'{-C,--no-splice}'[never use splice(), always use read/write]' \
   '(-R --remote)*'{-E,--skip-errors}"[skip read errors in input${Edesc}]" \
   '(-S --stop-at-size -R --remote)'{-S,--stop-at-size}'[stop after --size bytes have been transferred]' \
@@ -70,18 +70,6 @@ case $state in
       _pids $suf && ret=0
     fi
   ;;
-  size-unit)
-    if compset -P '<->'; then
-      _tags values units
-    else
-      _tags values
-    fi
-    while _tags; do
-      _requested values && _message -e values "$state_descr" && ret=0
-      _requested units expl unit compadd -o nosort - K M G T && ret=0
-      (( ret )) || break
-    done
-  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_rclone b/Completion/Unix/Command/_rclone
index 27b4dd926..a2e3429f5 100644
--- a/Completion/Unix/Command/_rclone
+++ b/Completion/Unix/Command/_rclone
@@ -62,7 +62,7 @@ _arguments -C \
   '--backup-dir[make backups into hierarchy based at specified directory]:directory:_directories' \
   '--bind[specify socal address to bind to for outgoing connections]:IPv4, IPv6 or name' \
   '--buffer-size[specify in memory buffer size when reading files for each --transfer]:size [16M]' \
-  '--bwlimit[specify bandwidth limit]:BwTimetable (kBytes/s or b|k|M|G suffix)' \
+  '--bwlimit[specify bandwidth limit]: :_numbers -u kBytes/s limit b k M G' \
   '--cache-dir[specify directory rclone will use for caching]:directory [~/.cache/rclone]:_directories' \
   '--checkers[specify number of checkers to run in parallel]:number [8]': \
   '(-c --checksum)'{-c,--checksum}'[skip based on checksum & size, not mod-time & size]' \
@@ -99,15 +99,15 @@ _arguments -C \
   '--log-format[specify comma separated list of log format options]:string ["date,time"]' \
   '--log-level[specify log level]:string [NOTICE]:(DEBUG INFO NOTICE ERROR)'  \
   '--low-level-retries[number of low level retries to do]:int [10]' \
-  '--max-age[only transfer files younger than this in s or suffix ms|s|m|h|d|w|M|y]:duration [default off]' \
+  '--max-age[only transfer files younger than specified age]: :_numbers -u seconds age ms\:milliseconds \:s\:seconds m\:minutes h\:hours d\:days w\:weeks M\:months y\:years' \
   '--max-backlog[maximum number of objects in sync or check backlog]:int [10000]' \
   '--max-delete[when synchronizing, limit the number of deletes]:delete limit [-1]' \
   '--max-depth[limit the recursion depth]:depth [-1]' \
-  '--max-size[only transfer files smaller than this in k or suffix b|k|M|G]:int [default off]' \
+  '--max-size[only transfer files smaller than specified size]: :_numbers -u kBytes size \:k M G' \
   '--max-transfer[maximum size of data to transfer]:int [default off]' \
   '--memprofile[write memory profile to file]:file:_files' \
-  '--min-age[only transfer files older than this in s or suffix ms|s|m|h|d|w|M|y]:duration [default off]' \
-  '--min-size[only transfer files bigger than this in k or suffix b|k|M|G]:int [default off]' \
+  '--min-age[only transfer files older than specified age]: :_numbers -u seconds age ms\:milliseconds \:s\:seconds m\:minutes h\:hours d\:days w\:weeks M\:months y\:years' \
+  '--min-size[only transfer files bigger than specified size]: :_numbers -u kBytes size \:k M G' \
   '--modify-window[specify max time delta to be considered the same]:duration [1ns]' \
   '--multi-thread-cutoff[use multi-threaded downloads for files above specified size]:size (250M)' \
   '--multi-thread-streams[specify max number of streams to use for multi-threaded downloads]:number (4)' \
diff --git a/Completion/Unix/Command/_rsync b/Completion/Unix/Command/_rsync
index b1a4f6046..eb906e974 100644
--- a/Completion/Unix/Command/_rsync
+++ b/Completion/Unix/Command/_rsync
@@ -99,7 +99,7 @@ _rsync() {
   _arguments -s \
     '*'{-v,--verbose}'[increase verbosity]' \
     {--no-v,--no-verbose}'[turn off --verbose]' \
-    '--bwlimit=[limit I/O bandwidth]:limit (KiB per second)' \
+    '--bwlimit=[limit I/O bandwidth]: :_numbers -f -u "KiB per second" -d 1g limit B K M G T P' \
     '--outbuf=[set output buffering]:buffering:(none line block)' \
     '--port=[specify alternate port number]:port:(873)' \
     '--address=[bind to the specified address]:bind address:_bind_addresses' \
@@ -181,7 +181,7 @@ _rsync() {
     {--no-W,--no-whole-file}'[turn off --whole-file]' \
     '(--cc --checksum-choice)'{--cc,--checksum-choice}'=[choose the checksum algorithms]:algorithm:_sequence -n 2 compadd - auto md4 md5 none' \
     '(-x --one-file-system)'{-x,--one-file-system}"[don't cross filesystem boundaries]" \
-    '(-B --block-size)'{-B+,--block-size=}'[force a fixed checksum block-size]:block size (bytes)' \
+    '(-B --block-size)'{-B+,--block-size=}'[force a fixed checksum block-size]: :_numbers -f -u bytes -d 1g "block size" B K M G T P' \
     '(-e --rsh)'{-e+,--rsh=}'[specify the remote shell to use]:remote-shell command:(rsh ssh)' \
     '--rsync-path=[specify path to rsync on the remote machine]:remote command' \
     '--ignore-existing[ignore files that already exist on receiving side]' \
@@ -199,10 +199,10 @@ _rsync() {
     '--force-change[affect user-/system-immutable files/dirs]' \
     '--force-uchange[affect user-immutable files/dirs]' \
     '--force-schange[affect system-immutable files/dirs]' \
-    '--max-delete=[do not delete more than NUM files]:number' \
-    '--max-size=[do not transfer any file larger than specified size]:number' \
+    "--max-delete=[don't delete more than NUM files]: :_numbers -f -u bytes size B K M G T P" \
+    "--max-size=[don't transfer any file larger than specified size]: :_numbers -f -u bytes size B K M G T P" \
     '--min-size=[do not transfer any file smaller than specified size]:number' \
-    '--max-alloc=[set limit to individual memory allocation]:size (bytes) [1g]' \
+    '--max-alloc=[set limit to individual memory allocation]: :_numbers -f -u bytes -d 1g size B K M G T P' \
     '(-P)--partial[keep partially transferred files]' \
     '--no-partial[turn off --partial]' \
     '--partial-dir=[put a partially transferred file into specified directory]:directory:_directories' \
diff --git a/Completion/Unix/Command/_stdbuf b/Completion/Unix/Command/_stdbuf
index a18938ee1..4b7d98ba0 100644
--- a/Completion/Unix/Command/_stdbuf
+++ b/Completion/Unix/Command/_stdbuf
@@ -7,7 +7,9 @@ short=( -e -i -o )
 long=( --error --input --output )
 buf=( err in out )
 
-opt='[set initial buffering for std${buf[i]}]:mode or size:((0\:unbuffered L\:line\ buffered'
+opt='[set initial buffering for std${buf[i]}]: : _alternative
+  "sizes\: \: _numbers -u bytes size k M G"
+  "modes\:mode\:((0\:unbuffered L\:line\ buffered'
 if _pick_variant gnu=GNU freebsd --version; then
   gnu=1
   args=(
@@ -17,7 +19,7 @@ if _pick_variant gnu=GNU freebsd --version; then
 else
   opt+=' B\:fully\ buffered'
 fi
-opt+='))'
+opt+='))"'
 
 for ((i=1;i<=3;i++)); do
   args+=( "(${long[i]})${short[i]}+${(e)opt}" )
diff --git a/Completion/Unix/Command/_tail b/Completion/Unix/Command/_tail
index 6d6e9b2d5..e54a0b06e 100644
--- a/Completion/Unix/Command/_tail
+++ b/Completion/Unix/Command/_tail
@@ -53,20 +53,14 @@ _arguments -C -s -S $opts : $args '*:file:_files' && return
 
 case $state in
   (number)
-    local mlt sign digit
-    mlt='multipliers:multiplier:((b\:512 K\:1024 KB\:1000 M\:1024\^2'
-    mlt+=' MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4))'
-    sign='signs:sign:((+\:"start at the specified byte/line"'
-    sign+=' -\:"output the last specified bytes/lines (default)"))'
-    digit='digits:digit:(0 1 2 3 4 5 6 7 8 9)'
-    if compset -P '(-|+|)[0-9]##'; then
-      _alternative $mlt $digit && ret=0
-    elif [[ -z $PREFIX ]]; then
-      _alternative $sign $digit && ret=0
-    elif compset -P '(+|-)'; then
-      _alternative $digit && ret=0
-    fi
-    ;;
+    local alts
+    [[ -z $PREFIX ]] && alts=(
+      'sign:sign:((-\:"print all but the last specified bytes/lines" +\:"print the first specified bytes/lines (default)"))'
+    )
+    compset -P '+'
+    alts+=( 'numbers: :_numbers -N $state_descr b\:512 K\:1024 KB\:1000 M\:1024\^2 MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4' )
+    _alternative $alts && ret=0
+  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_timeout b/Completion/Unix/Command/_timeout
index 2235fa5ec..c041283ac 100644
--- a/Completion/Unix/Command/_timeout
+++ b/Completion/Unix/Command/_timeout
@@ -16,5 +16,5 @@ _arguments -S -A "-" $args \
   "--foreground[don't propagate timeout to the command children]" \
   '(-s --signal)'{-s,--signal}'[specify the signal to send on timeout]:signal:_signals' \
   '(-k --kill-after)'{-k,--kill-after}'[followup first signal with SIGKILL if command persists after specified time]:time' \
-  '1: :_guard "[0-9.]#([smhd]|)" duration' \
+  '1: :_numbers -f -u seconds duration :s:seconds m:minutes h:hours d:days' \
   '*:::command:_normal'
diff --git a/Completion/Unix/Command/_zfs b/Completion/Unix/Command/_zfs
index 452e1160d..51da9170b 100644
--- a/Completion/Unix/Command/_zfs
+++ b/Completion/Unix/Command/_zfs
@@ -162,12 +162,20 @@ _zfs() {
 		"multilevel:value:(on off)"
 		"nbmand:value:(on off)"
 		"primarycache:value:(all none metadata)"
-		"quota:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == quota= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'quota' compadd none; fi}"
+		"quota: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(none)'"
 		"readonly:value:(on off)"
 		"recordsize:value:(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M)"
-		"refquota:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == refquota= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'refquota' compadd none; fi}"
-		"refreservation:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == refreservation= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'refreservation' compadd none; fi}"
-		"reservation:value:{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == reservation= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'reservation' compadd none; fi}"
+		"refquota: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(none)'"
+		"refreservation: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(auto none)'"
+		"reservation: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(none)'"
 		"rstchown:value:(on off)"
 		"secondarycache:value:(all none metadata)"
 		"setuid:value:(on off)"
@@ -238,8 +246,8 @@ _zfs() {
 			':filesystem:_zfs_dataset -t fs -e "parent dataset"' \
 			- set2 \
 			'-s[Create sparse volume]' \
-			'-b[Set volblocksize]:blocksize:' \
-			'-V[Set size]:size:' \
+			'-b+[set volblocksize]: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes blocksize :B {k,M,G,T,P,E,Z}{,B}' \
+			'-V+[set size]: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
 			':volume:_zfs_dataset -t fs -e "parent dataset"'
 		;;
 
diff --git a/Completion/X/Command/_xset b/Completion/X/Command/_xset
index b35a6466b..adda47f01 100644
--- a/Completion/X/Command/_xset
+++ b/Completion/X/Command/_xset
@@ -91,8 +91,8 @@ _regex_arguments _xset_parse \
     \( "/(blank|noblank|expose|noexpose|default|on|activate|reset)$nul/" \
        ':option-s:screen saver:(blank noblank expose noexpose default on activate reset off)' \
     \| "/off$nul/" \( "/off$nul/" ':option-s-off-period:period off:(off)' \| \) \
-    \| "/[0-9]##$nul/" ':option-s-timeout:length:' \
-      \( "/[0-9]##$nul/" ':option-s-period:period:' \
+    \| "/[0-9]##$nul/" ':option-s-timeout: :_numbers -u seconds length' \
+      \( "/[0-9]##$nul/" ':option-s-period: :_numbers -u seconds period' \
       \| \) \
     \| \) \
   \| "/-r$nul/" "$guard" \
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 8c6bf9c40..40238c4b9 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -4496,8 +4496,22 @@ not contain an explanation string to be displayed above the matches.
 If tt(_description) is called with more than three arguments,
 the additional var(spec)s should be of the form `var(char)tt(:)var(str)'.
 These supply escape sequence replacements for the tt(format) style:
-every appearance of `tt(%)var(char)' will be
-replaced by var(string).
+every appearance of `tt(%)var(char)' will be replaced by var(string).
+If no additional var(spec)s are given but the description in var(descr)
+conforms to a common form then further escape sequences are set for
+elements of that description.  These elements correspond to a default
+value (`tt(%o)'), the units (`tt(%m)') range of acceptable values
+(`tt(%r)') and the remaining initial part of the description (`tt(%h)').
+The form the description takes consists of specifying the units and
+range in parentheses and the default value in square brackets, for
+example:
+
+example(_description times expl 'timeout (seconds) (0-60) [20]')
+
+It is possible to use tt(zformat) conditional expressions when styling
+these elements. So, for example, to add `tt(default:)' as a tag but only
+when there is a default value to show, the tt(format) style might
+include `tt(%(o.default: %o.))'.
 
 If the tt(-x) option is given, the description will be passed to
 tt(compadd) using the tt(-x) option instead of the default tt(-X).  This
@@ -4773,6 +4787,69 @@ checked.  If it is set completion is terminated at that point even if
 no matches have been found.  This is the same effect as in the
 tt(-first-) context.
 )
+findex(_numbers)
+item(tt(_numbers) [ var(option) ... ] [ var(description) ] [ var(suffix) ... ])(
+This can be used where a number is followed by a suffix to indicate the units.
+The unit suffixes are completed and can also be included in the description
+used when completion is invoked for the preceding number.
+
+In addition to common tt(compadd) options, tt(_numbers) accepts the following
+options:
+
+startitem()
+item(tt(-t) var(tag))(
+Specify a tag to use instead of the default of tt(numbers).
+)
+item(tt(-u) var(units))(
+Indicate the default units for the number, e.g. tt(bytes).
+)
+item(tt(-l) var(min))(
+Specify the lowest possible value for the number.
+)
+item(tt(-m) var(max))(
+Specify the highest possible value for the number.
+)
+item(tt(-d) var(default))(
+Specify the default value.
+)
+item(tt(-N))(
+Allow negative numbers.  This is implied if the range includes a negative.
+)
+item(tt(-f))(
+Allow decimal numbers.
+)
+enditem()
+
+Where a particular suffix represents the default units for a number, it
+should be prefixed with a colon.  Additionally, suffixes can be followed
+by a colon and a description.  So for example, the following allows the
+age of something to be specified, either in seconds or with an optional
+suffix with a longer unit of time:
+
+example(_numbers -u seconds age :s:seconds m:minutes h:hours d:days)
+
+It is typically helpful for units to be presented in order of magnitude
+when completed.  To facilitate this, the order in which they are given
+is preserved.
+
+When the tt(format) style is looked up with the tt(descriptions) tag or
+the tag specified with tt(-t), the list of suffixes is available as a
+`tt(%x)' escape sequence. This is in addition to the usual sequences
+documented under the tt(format) style. The form this list takes can also
+be configured. To this end, the tt(format) style is first looked up with
+the tag tt(unit-suffixes). The retrieved format is applied to each
+suffix in turn and the results are then concatenated to form the
+completed list. For the tt(unit-suffixes) format, `tt(%x)' expands to
+the individual suffix and `tt(%X)' to its description. tt(%d)' indicates
+a default suffix and can be used in a condition. The index and reverse
+index are set in `tt(%i)' and `tt(%r)' respectively and are useful for
+text included only with the first and last suffixes in the list. So for
+example, the following joins the suffixes together as a comma-separated
+list:
+
+example(zstyle ':completion:*:unit-suffixes' format '%x%(r::,)')
+)
+
 findex(_options)
 item(tt(_options))(
 This can be used to complete the names of shell options.  It provides a


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-11-22 21:25 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-27 11:52 PATCH: list units in brackets at the end of completion group descriptions Oliver Kiddle
2021-08-27 12:10 ` Daniel Shahaf
2021-10-27 20:51   ` Oliver Kiddle
2021-11-02 12:08     ` Jun T
2021-11-07  1:16       ` Oliver Kiddle
2021-11-08 10:56         ` Jun T
2021-11-10 23:08           ` PATCH: zformat (was list units in brackets at the end of completion group descriptions) Oliver Kiddle
2021-11-22 21:24           ` PATCH: list units in brackets at the end of completion group descriptions Oliver Kiddle
2021-08-27 14:44 ` Mikael Magnusson
2021-08-27 16:14   ` Oliver Kiddle

Code repositories for project(s) associated with this inbox:

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).