Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] _description, _message: escape % in format specs
- X-seq: zsh-workers 54573
- From: dana <dana@xxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: [PATCH] _description, _message: escape % in format specs
- Date: Fri, 15 May 2026 17:22:04 -0500
- Archived-at: <https://zsh.org/workers/54573>
- Feedback-id: i9be146f9:Fastmail
- List-id: <zsh-workers.zsh.org>
% zstyle ':completion:*:descriptions' format 'completing %d:'
% compdef _foo foo
% _foo() { _arguments :%2Fbar%f }
% foo <TAB>
completing bar: # bar is green
is there a good reason that users of _description and _message, or of
higher-level helpers like _arguments and _describe, should be able to
pass arbitrary % sequences down to compadd -[xX] via zformat specs?
i'd suggested to oliver that there isn't. he didn't seem fully
convinced, but didn't shoot it down either
the only thing that gives me pause is that several functions currently
pre-escape % to work around this and it's possible i might've missed
some
then again maybe there are some functions that currently have broken
descriptions because they *don't* pre-escape -- especially since this
expectation doesn't seem to be documented anywhere
dana
diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description
index 368b41ee2..dfa2e0860 100644
--- a/Completion/Base/Core/_description
+++ b/Completion/Base/Core/_description
@@ -86,7 +86,7 @@ elif [[ -n "$format" ]]; then
[[ -n $match[3] ]] && argv+=( o:$match[3] )
fi
- 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 dbeed4a88..ce9ef4af7 100644
--- a/Completion/Base/Core/_message
+++ b/Completion/Base/Core/_message
@@ -39,7 +39,8 @@ 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/Completion/Base/Utility/_numbers b/Completion/Base/Utility/_numbers
index 069fc75a4..5be5344f3 100644
--- a/Completion/Base/Utility/_numbers
+++ b/Completion/Base/Utility/_numbers
@@ -73,7 +73,7 @@ elif [[ -prefix $~pat || $PREFIX = $~partial ]]; then
zformat -f suffix "$suffixfmt" "x:${${argv[i+1]#:}%%:*}" \
"X:${${argv[i+1]#:}#*:}" "d:${#${argv[i+1]}[1]#:}" \
i:i r:$(( $# - i - 1))
- suffixes+="${suffix//\%/%%}"
+ suffixes+=$suffix
done
[[ -n $suffixes ]] && formats+=( x:$suffixes )
diff --git a/Completion/Debian/Command/_git-buildpackage b/Completion/Debian/Command/_git-buildpackage
index 81bf7dac9..7c81e0ff2 100644
--- a/Completion/Debian/Command/_git-buildpackage
+++ b/Completion/Debian/Command/_git-buildpackage
@@ -13,8 +13,8 @@ _arguments \
'--git-sign-tags[sign tags]' \
'--git-no-sign-tags[negates --git-sign-tags]' \
'--git-keyid=-[GPG keyid to sign tags with]:GPG key:' \
- '--git-debian-tag=-[format string for debian tags]:format string [debian/%%(version)s]' \
- '--git-upstream-tag=-[format string for upstream tags]:format string [upstream/%%(version)s]' \
+ '--git-debian-tag=-[format string for debian tags]:format string [debian/%(version)s]' \
+ '--git-upstream-tag=-[format string for upstream tags]:format string [upstream/%(version)s]' \
'--git-pristine-tar[use pristine-tar to create .orig.tar.gz]' \
'--git-no-pristine-tar[negates --git-pristine-tar]' \
'--git-force-create[force creation of orig.tar.gz]' \
diff --git a/Completion/Linux/Command/_valgrind b/Completion/Linux/Command/_valgrind
index 47fced1fd..638b29cf5 100644
--- a/Completion/Linux/Command/_valgrind
+++ b/Completion/Linux/Command/_valgrind
@@ -20,7 +20,7 @@ common_own_malloc=(
'--alignment=-[set minimum alignment of heap allocations]:number [16]'
'--redzone-size=-[set minimum size of redzones added before/after heap blocks]:size (bytes) [16]'
'--xtree-memory=-[profile heap memory in an xtree [none]:(none allocs full)'
- '--xtree-memory-file=-[specify xtree memory report file]:file [xtmemory.kcg.%%p]:_files'
+ '--xtree-memory-file=-[specify xtree memory report file]:file [xtmemory.kcg.%p]:_files'
)
common_read_varinfo=(
@@ -67,7 +67,7 @@ args_addrcheck=(
'(--show-leak-kinds)--show-reachable=-[show reachable blocks in leak check]:enable:(yes no)'
'(--show-leak-kinds)--show-possibly-lost=-:enable:(yes no)'
'--xtree-leak=-[output leak result in xtree format]:enable [no]:(yes no)'
- '--xtree-leak-file=-[specify xtree leak report file]:file [xtleak.kcg.%%p]:_files'
+ '--xtree-leak-file=-[specify xtree leak report file]:file [xtleak.kcg.%p]:_files'
'--undef-value-errors=-[check for undefined value errors]:enable [yes]:(yes no)'
'--track-origins=-[show origins of undefined values]:enable [no]:(yes no)'
$common_partial
@@ -124,11 +124,11 @@ args_cachegrind=(
'--LL=-[set LL cache manually]:size,assoc,line_size'
'--cache-sim=-[collect cache stats]:enable [yes]:(yes no)'
'--branch-sim=-[collect branch prediction stats]:enable [no]:(yes no)'
- '--cachegrind-out-file=-[specify output file name]:file name [cachegrind.out.%%p]:_files'
+ '--cachegrind-out-file=-[specify output file name]:file name [cachegrind.out.%p]:_files'
)
args_callgrind=(
- '--callgrind-out-file=-[specify output file name]:file name [callgrind.out.%%p]:_files'
+ '--callgrind-out-file=-[specify output file name]:file name [callgrind.out.%p]:_files'
'--dump-line=-[perform event counting at source line granularity]:enable [yes]:(yes no)'
'--dump-instr=-[perform event counting at instruction granularity]:enable [no]:(yes no)'
'--compress-strings=-[identify file and function names by numbers]:enable [yes]:(yes no)'
@@ -202,7 +202,7 @@ args_massif=(
))"
'--detailed-freq=-[every Nth snapshot should be detailed]:snapshot interval [10]'
'--max-snapshots=-[specofy maximum number of snapshots recorded]:maximum [100]'
- '--massif-out-file=-[specify output file name]:filename [massif.out.%%p]:_files'
+ '--massif-out-file=-[specify output file name]:filename [massif.out.%p]:_files'
)
args_exp_bbv=(
diff --git a/Completion/Unix/Command/_csplit b/Completion/Unix/Command/_csplit
index b0ba1d7f4..eb354bd83 100644
--- a/Completion/Unix/Command/_csplit
+++ b/Completion/Unix/Command/_csplit
@@ -17,7 +17,7 @@ specs=(
if _pick_variant gnu='(GNU|uutils)' unix --version; then
# GNU coreutils 8.32
specs+=(
- '(hv -b --suffix-format -n --digits)'{-b+,--suffix-format=}'[specify format for numbers in output file names]:format [%%02d]: '
+ '(hv -b --suffix-format -n --digits)'{-b+,--suffix-format=}'[specify format for numbers in output file names]:format [%02d]: '
'(hv)--suppress-matched[suppress the lines matching the pattern]'
'(hv -z --elide-empty)'{-z,--elide-empty-files}'[remove empty output files]'
+ hv
@@ -44,7 +44,7 @@ case $state in
elif compset -P '[0-9]*'; then
_message 'line number' && ret=0
elif [[ ${words[CURRENT]} != -* ]] then
- _message "line_number, '/regex/[offset]', '%%regex%%[offset]', or '{count}'" && ret=0
+ _message "line_number, '/regex/[offset]', '%regex%[offset]', or '{count}'" && ret=0
fi
esac
diff --git a/Completion/Unix/Command/_gcore b/Completion/Unix/Command/_gcore
index ef3afd919..4e5041f3b 100644
--- a/Completion/Unix/Command/_gcore
+++ b/Completion/Unix/Command/_gcore
@@ -56,7 +56,7 @@ case $OSTYPE in
'-v[report progress on the dump as it proceeds]' \
'-b+[specify maximum size of core file]:size (MiB): ' \
'(-c)-o+[write core file to specified file]:file:_files' \
- '(-o)-c+[specify format of core file name]:format (%%N\:program name, %%U\:uid, %%P\:pid, %%T\:time stamp)' \
+ '(-o)-c+[specify format of core file name]:format (%N\:program name, %U\:uid, %P\:pid, %T\:time stamp)' \
'1:pid:_pids'
;;
*)
diff --git a/Completion/Unix/Command/_mpc b/Completion/Unix/Command/_mpc
index 45b181f55..83700d828 100644
--- a/Completion/Unix/Command/_mpc
+++ b/Completion/Unix/Command/_mpc
@@ -234,7 +234,7 @@ _mpc_play() {
}
_mpc_seek() {
- _message -e position 'position ([+-][HH:MM:SS]|<0-100>%%)'
+ _message -e position 'position ([+-][HH:MM:SS]|<0-100>%)'
}
_mpc_seekthrough() {
diff --git a/Completion/Unix/Command/_nano b/Completion/Unix/Command/_nano
index e2333b9f4..4f6bd8b0d 100644
--- a/Completion/Unix/Command/_nano
+++ b/Completion/Unix/Command/_nano
@@ -20,7 +20,7 @@ args=(
'(-N --noconvert -u --unix)'{-N,--noconvert}"[don't convert files from DOS/Mac format]"
'(-O --bookstyle)'{-O,--bookstyle}'[when justifying, leading whitespace means new paragraph]'
'(-P --positionlog)'{-P,--positionlog}'[remember cursor position]'
- '(-Q --quotestr)'{-Q+,--quotestr=}'[specify regular expression to match quoting]:quoting regex [^([ \t]*([!#%%\:;>|}]|//))+]'
+ '(-Q --quotestr)'{-Q+,--quotestr=}'[specify regular expression to match quoting]:quoting regex [^([ \t]*([!#%\:;>|}]|//))+]'
'(-R --restricted)'{-R,--restricted}'[restrict access to the file system]'
'(-S --softwrap)'{-S,--softwrap}'[soft-wrap long lines]'
'(-T --tabsize)'{-T+,--tabsize=}'[specify tab width]: :_numbers -u columns -l1 -d8 "tab width"'
diff --git a/Completion/Unix/Command/_opustools b/Completion/Unix/Command/_opustools
index 1fd97798a..c860a170f 100644
--- a/Completion/Unix/Command/_opustools
+++ b/Completion/Unix/Command/_opustools
@@ -14,7 +14,7 @@ case $service in
'--cvbr[use constrained variable bitrate encoding]' \
'--downmix-mono[downmix to mono]' \
'--downmix-stereo[downmix to stereo (if >2 channels)]' \
- '--expect-loss[set expected packet loss]:expected packet loss (%%) (0-100) [0]' \
+ '--expect-loss[set expected packet loss]:expected packet loss (%) (0-100) [0]' \
'--framesize[set maximum frame size]:maximum frame size (milliseconds) [20]:(2.5 5 10 20 40 60)' \
'--hard-cbr[use hard constant bitrate encoding]' \
'--max-delay[set maximum container delay]:maximum container delay (milliseconds) (0-1000) [1000]' \
@@ -57,7 +57,7 @@ case $service in
'--no-dither[do not dither 16-bit output]' \
'--float[output 32-bit floating-point samples]' \
'--force-wav[force RIFF wav header on output]' \
- '--packet-loss[simulate random packet loss]:packet loss probability (%%) (0-100)' \
+ '--packet-loss[simulate random packet loss]:packet loss probability (%) (0-100)' \
'--save-range[save check values for every frame to a file]:output for check values:_files'
;;
opusinfo)
diff --git a/Completion/Unix/Type/_date_formats b/Completion/Unix/Type/_date_formats
index 0527d706c..2b7139492 100644
--- a/Completion/Unix/Type/_date_formats
+++ b/Completion/Unix/Type/_date_formats
@@ -107,6 +107,6 @@ done
_describe -t date-format-specifier 'date format specifier' specs \
-p "${(Q)PREFIX:-%}" -S '' && ret=0
-[[ $1 == zsh ]] && _message -e date-format-precision 'precision for %%. (1-9)'
+[[ $1 == zsh ]] && _message -e date-format-precision 'precision for %. (1-9)'
return ret
diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst
index 200c83e8c..cb1787079 100644
--- a/Test/Y03arguments.ztst
+++ b/Test/Y03arguments.ztst
@@ -796,6 +796,17 @@ F:shouldn't offer -t in the first case (with stacked options)
>DESCRIPTION:{option}
>NO:{-b}
+ tst_arguments : '-x+[%2Fxdesc%f]:%2Fxargdesc%f' -y '1:%2F1desc%f'
+ comptest $'tst -\t'
+ comptest $'tst -x\t'
+0:% in descriptions
+>line: {tst -}{}
+>DESCRIPTION:{%2F1desc%f}
+>DESCRIPTION:{option}
+>NO:{-x -- %2Fxdesc%f}
+>NO:{-y}
+>line: {tst -x}{}
+>DESCRIPTION:{%2Fxargdesc%f}
%clean
diff --git a/Test/Y05describe.ztst b/Test/Y05describe.ztst
index ee8717a24..72953af7c 100644
--- a/Test/Y05describe.ztst
+++ b/Test/Y05describe.ztst
@@ -92,6 +92,14 @@
>NO:{x}
>line: {tst cx}{}
+ tst_describe %2Fdesc%f '(a:adesc b:%2Fbdesc%f)'
+ comptest $'tst \t'
+0:% in descriptions
+>line: {tst }{}
+>DESCRIPTION:{%2Fdesc%f}
+>NO:{a -- adesc}
+>NO:{b -- %2Fbdesc%f}
+
%clean
zmodload -ui zsh/zpty
diff --git a/Test/Y06values.ztst b/Test/Y06values.ztst
index b80cc2927..720f990a5 100644
--- a/Test/Y06values.ztst
+++ b/Test/Y06values.ztst
@@ -233,6 +233,14 @@
>NO:{a}
>NO:{d}
+ tst_values %2Fdesc%f 'a[adesc]' 'b[%2Fbdesc%f]'
+ comptest $'tst \t'
+0:% in descriptions
+>line: {tst }{}
+>DESCRIPTION:{%2Fdesc%f}
+>NO:{a -- adesc}
+>NO:{b -- %2Fbdesc%f}
+
%clean
zmodload -ui zsh/zpty
diff --git a/Test/Y07message.ztst b/Test/Y07message.ztst
new file mode 100644
index 000000000..d56a149a9
--- /dev/null
+++ b/Test/Y07message.ztst
@@ -0,0 +1,50 @@
+# tests for _message
+
+%prep
+
+ if ( zmodload -s zsh/zpty ); then
+ source $ZTST_srcdir/comptest
+ mkdir comp.tmp
+ cd comp.tmp
+ comptestinit -z $ZTST_testdir/../Src/zsh && {
+ comptesteval 'compdef _tst tst'
+ tst_message() { comptesteval "_tst() { _message ${${(@q+)@}} }" }
+ }
+ else
+ ZTST_unimplemented='the zsh/zpty module is not available'
+ fi
+
+%test
+
+ tst_message msg
+ comptest $'tst \t'
+0:basic message
+>line: {tst }{}
+>MESSAGE:{msg}
+
+# @todo not a very good test
+ tst_message -e tests msg1
+ comptest $'tst \t'
+ tst_message -e msg2
+ comptest $'tst \t'
+0:-e with and without tag
+>line: {tst }{}
+>DESCRIPTION:{msg1}
+>line: {tst }{}
+>DESCRIPTION:{msg2}
+
+# @todo not a very good test
+ tst_message -r msg
+ comptest $'tst \t'
+0:-r
+>line: {tst }{}
+
+ tst_message %2Fmsg%f
+ comptest $'tst \t'
+0:% in message
+>line: {tst }{}
+>MESSAGE:{%2Fmsg%f}
+
+%clean
+
+ zmodload -ui zsh/zpty
Messages sorted by:
Reverse Date,
Date,
Thread,
Author