Added a -v/--validate option to fish_opt

This new flag causes fish_opt to generrate an option spec with !
(e.g. "fish_opt -s s -rv some code" will output "s=!some code").

Such validation scripts are not particular useful (they are highly limited as
they cannot access the values for other options, and must be quoted
appropriately so they can be passed to argparse). I merely added the option to
fish_opt so that it can now generate any valid option spec.
This commit is contained in:
Isaac Oscar Gariano
2025-08-03 10:07:08 +10:00
parent 007edac145
commit 944cfd181e
12 changed files with 133 additions and 14 deletions

View File

@@ -51,6 +51,7 @@ Scripting improvements
- ``argparse`` now allows specifying options that take multiple optional values by using ``=*`` in the option spec, the parsing of the option is the same as ones with optional values (i.e. ``=?``), but each successive use accumulates more values (or an empty string if no value), instead of replacing the previous value (i.e. it behaves similarly to ``=+``) (:issue:`8432`). In addition, ``fish_opt`` has been modified to support such options by using the ``--multiple-vals`` together with ``-o`` / ``--optional-val``; ``-m`` is also now acceptable as an abbreviation for ``--multiple-vals``. - ``argparse`` now allows specifying options that take multiple optional values by using ``=*`` in the option spec, the parsing of the option is the same as ones with optional values (i.e. ``=?``), but each successive use accumulates more values (or an empty string if no value), instead of replacing the previous value (i.e. it behaves similarly to ``=+``) (:issue:`8432`). In addition, ``fish_opt`` has been modified to support such options by using the ``--multiple-vals`` together with ``-o`` / ``--optional-val``; ``-m`` is also now acceptable as an abbreviation for ``--multiple-vals``.
- ``fish_opt`` no longer requires you give a short flag name when defining options, provided you give it a long flag name with more than one character. - ``fish_opt`` no longer requires you give a short flag name when defining options, provided you give it a long flag name with more than one character.
- ``argparse`` option specifiers for long only options can now start with ``/``, allowing the definition of long options with a single letter (withouht the ``/``, an option with a single letter is always interpreted as a short flag). Due to this change, the ``--long-only`` option to ``fish_opt`` is now no longer necessary and is deprecated. - ``argparse`` option specifiers for long only options can now start with ``/``, allowing the definition of long options with a single letter (withouht the ``/``, an option with a single letter is always interpreted as a short flag). Due to this change, the ``--long-only`` option to ``fish_opt`` is now no longer necessary and is deprecated.
- ``fish_opt`` now has a ``-v`` / ``--validate`` option you can use to give a fish script to validate values of the option.
Interactive improvements Interactive improvements
------------------------ ------------------------

View File

@@ -8,7 +8,7 @@ Synopsis
.. synopsis:: .. synopsis::
fish_opt [-s ALPHANUM] [-l LONG-NAME] [-ormd] [--long-only] fish_opt [-s ALPHANUM] [-l LONG-NAME] [-ormd] [--long-only] [-v COMMAND OPTIONS ... ]
fish_opt --help fish_opt --help
Description Description
@@ -40,6 +40,9 @@ The following ``argparse`` options are available:
The option and any values will be deleted from the ``$argv_opts`` variables set by ``argparse`` The option and any values will be deleted from the ``$argv_opts`` variables set by ``argparse``
(as with other options, it will also be deleted from ``$argv``). (as with other options, it will also be deleted from ``$argv``).
**-v** or **--validate** *COMMAND* *OPTION...*
This option must be the last one, and requires one of ``-o``, ``-r``, or ``-m``. All the remaining arguments are interpreted a fish script to run to validate the value of the argument, see ``argparse`` documentation for more details. Note that the interpretation of *COMMAND* *OPTION...* is similar to ``eval``, so you may need to quote or escape special characters *twice* if you want them to be interpreted literally when the validate script is run.
**-h** or **--help** **-h** or **--help**
Displays help about using this command. Displays help about using this command.
@@ -63,9 +66,16 @@ Same as above but with a second flag that requires a value:
:: ::
set -l options (fish_opt -s h -l help) set -l options (fish_opt -s h -l help)
set options $options (fish_opt -s m -l max --required-val) set options $options (fish_opt -s m -l max -r)
argparse $options -- $argv argparse $options -- $argv
Same as above but the value of the second flag cannot be the empty string:
::
set -l options (fish_opt -s h -l help)
set options $options (fish_opt -s m -l max -rv test \$_flag_valu != "''")
argparse $options -- $argv
Same as above but with a third flag that can be given multiple times saving the value of each instance seen and only a long flag name (``--token``) is defined: Same as above but with a third flag that can be given multiple times saving the value of each instance seen and only a long flag name (``--token``) is defined:
@@ -74,7 +84,7 @@ Same as above but with a third flag that can be given multiple times saving the
:: ::
set -l options (fish_opt --short=h --long=help) set -l options (fish_opt --short=h --long=help)
set options $options (fish_opt --short=m --long=max --required-val) set options $options (fish_opt --short=m --long=max --required-val --validate test \$_flag_valu != "''")
set options $options (fish_opt --long=token --multiple-vals) set options $options (fish_opt --long=token --multiple-vals)
argparse $options -- $argv argparse $options -- $argv

View File

@@ -1939,6 +1939,9 @@ msgstr ""
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "" msgstr ""
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "%s: Ungültige Maske '%s'\\n" msgstr "%s: Ungültige Maske '%s'\\n"
@@ -1951,6 +1954,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "" msgstr ""
@@ -25444,6 +25453,9 @@ msgstr ""
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "" msgstr ""
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "" msgstr ""

View File

@@ -1935,6 +1935,9 @@ msgstr "%s: Expected 1, 2 or 3 arguments, got %d\\n"
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgstr "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "%s: Invalid mask “%s”\\n" msgstr "%s: Invalid mask “%s”\\n"
@@ -1947,6 +1950,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "%s: The number of positions to skip must be a non-negative integer\\n" msgstr "%s: The number of positions to skip must be a non-negative integer\\n"
@@ -25440,6 +25449,9 @@ msgstr ""
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "" msgstr ""
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "" msgstr ""

View File

@@ -2036,6 +2036,9 @@ msgstr "%s : 1, 2 ou 3 arguments attendus, %d reçu(s)\\n"
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "%s : Un argument attendu, %s reçu(s).\\n\\nRésumé :\\n\\t%svared%s VARIABLE\\n" msgstr "%s : Un argument attendu, %s reçu(s).\\n\\nRésumé :\\n\\t%svared%s VARIABLE\\n"
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "%s : Masque '%s' invalide\\n" msgstr "%s : Masque '%s' invalide\\n"
@@ -2048,6 +2051,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "%s : Le nombre de positions à passer doit être un entier naturel\\n" msgstr "%s : Le nombre de positions à passer doit être un entier naturel\\n"
@@ -25541,6 +25550,9 @@ msgstr ""
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "" msgstr ""
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "" msgstr ""

View File

@@ -1931,6 +1931,9 @@ msgstr ""
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "" msgstr ""
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "" msgstr ""
@@ -1943,6 +1946,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "" msgstr ""
@@ -25436,6 +25445,9 @@ msgstr ""
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "" msgstr ""
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "" msgstr ""

View File

@@ -1936,6 +1936,9 @@ msgstr ""
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "" msgstr ""
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "" msgstr ""
@@ -1948,6 +1951,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "" msgstr ""
@@ -25451,6 +25460,9 @@ msgstr ""
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "" msgstr ""
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "" msgstr ""

View File

@@ -1932,6 +1932,9 @@ msgstr "%s: Förväntade 1, 2 eller 3 argument, fick %d\\n"
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "%s: Förväntade exakt ett argument, fick %s\\n\\nSynopsis:\\n\\t%svared%s VARIABEL\\n" msgstr "%s: Förväntade exakt ett argument, fick %s\\n\\nSynopsis:\\n\\t%svared%s VARIABEL\\n"
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "%s: Ogiltigt mask '%s'\\n" msgstr "%s: Ogiltigt mask '%s'\\n"
@@ -1944,6 +1947,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "%s: Antalet positioner att hoppa över måste vara ett positivt heltal\\n" msgstr "%s: Antalet positioner att hoppa över måste vara ett positivt heltal\\n"
@@ -25439,6 +25448,9 @@ msgstr ""
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "" msgstr ""
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "" msgstr ""

View File

@@ -1934,6 +1934,9 @@ msgstr "%s: 预期参数 1, 2 或 3, 获得 %d\\n"
msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgstr "%s: 需要精确的一条参数, 获得 %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgstr "%s: 需要精确的一条参数, 获得 %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n"
msgid "%s: Extra non-option arguments were provided\\n"
msgstr ""
msgid "%s: Invalid mask '%s'\\n" msgid "%s: Invalid mask '%s'\\n"
msgstr "%s:无效的掩码'%s'\\n" msgstr "%s:无效的掩码'%s'\\n"
@@ -1946,6 +1949,12 @@ msgstr ""
msgid "%s: The --short flag must be a single character\\n" msgid "%s: The --short flag must be a single character\\n"
msgstr "" msgstr ""
msgid "%s: The --validate flag requires subsequent arguments\\n"
msgstr ""
msgid "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\\n"
msgstr ""
msgid "%s: The number of positions to skip must be a non-negative integer\\n" msgid "%s: The number of positions to skip must be a non-negative integer\\n"
msgstr "%s:要跳过的位置数必须是非负整数\\n" msgstr "%s:要跳过的位置数必须是非负整数\\n"
@@ -25439,6 +25448,9 @@ msgstr "要转换的第一个页面"
msgid "First remove existing destination files" msgid "First remove existing destination files"
msgstr "首先删除已有目的文件" msgstr "首先删除已有目的文件"
msgid "Fish script to validate option values"
msgstr ""
msgid "Fish's release notes" msgid "Fish's release notes"
msgstr "fish的放行记录" msgstr "fish的放行记录"

View File

@@ -11,3 +11,4 @@ complete --command fish_opt --short-option o --long-option optional-val -n $COND
complete --command fish_opt --short-option r --long-option required-val -n $CONDITION --description 'Require value' complete --command fish_opt --short-option r --long-option required-val -n $CONDITION --description 'Require value'
complete --command fish_opt --short-option m --long-option multiple-vals --description 'Store all values' complete --command fish_opt --short-option m --long-option multiple-vals --description 'Store all values'
complete --command fish_opt --short-option d --long-option delete --description 'Delete option from argv_opts' complete --command fish_opt --short-option d --long-option delete --description 'Delete option from argv_opts'
complete --command fish_opt --short-option v --long-option validate --require-parameter --description 'Fish script to validate option values'

View File

@@ -1,6 +1,15 @@
# This is a helper function for `fish_opt`. It does some basic validation of the arguments. # This is a helper function for `fish_opt`. It does some basic validation of the arguments.
function __fish_opt_validate_args --no-scope-shadowing function __fish_opt_validate_args --no-scope-shadowing
if set -q _flag_short && test 1 -ne (string length -- $_flag_short) if not set -q _flag_validate && test (count $argv) -ne 0
printf (_ "%s: Extra non-option arguments were provided\n") fish_opt >&2
return 1
else if set -q _flag_validate && test (count $argv) -eq 0
printf (_ "%s: The --validate flag requires subsequent arguments\n") fish_opt >&2
return 1
else if set -q _flag_validate && not set -q _flag_multiple_vals && not set -q _flag_optional_val && not set -q _flag_required_val
printf (_ "%s: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag\n") fish_opt >&2
return 1
else if set -q _flag_short && test 1 -ne (string length -- $_flag_short)
printf (_ "%s: The --short flag must be a single character\n") fish_opt >&2 printf (_ "%s: The --short flag must be a single character\n") fish_opt >&2
return 1 return 1
else if not set -q _flag_short && not set -q _flag_long else if not set -q _flag_short && not set -q _flag_long
@@ -17,8 +26,8 @@ end
# The `fish_opt` command. # The `fish_opt` command.
function fish_opt -d 'Produce an option specification suitable for use with `argparse`.' function fish_opt -d 'Produce an option specification suitable for use with `argparse`.'
set -l options h/help 's/short=' 'l/long=' d/delete o/optional-val r/required-val m/multiple-vals long-only set -l options h/help 's/short=' 'l/long=' d/delete o/optional-val r/required-val m/multiple-vals long-only v/validate
argparse -n fish_opt --max-args=0 --exclusive=r,o $options -- $argv argparse -n fish_opt --stop-nonopt --exclusive=r,o $options -- $argv
or return or return
if set -q _flag_help if set -q _flag_help
@@ -26,7 +35,7 @@ function fish_opt -d 'Produce an option specification suitable for use with `arg
return 0 return 0
end end
__fish_opt_validate_args __fish_opt_validate_args $argv
or return or return
if not set -q _flag_short if not set -q _flag_short
@@ -53,5 +62,9 @@ function fish_opt -d 'Produce an option specification suitable for use with `arg
set opt_spec "$opt_spec&" set opt_spec "$opt_spec&"
end end
if set -q _flag_validate
set opt_spec "$opt_spec!$argv"
end
echo $opt_spec echo $opt_spec
end end

View File

@@ -572,7 +572,17 @@ and echo unexpected status $status
# An unexpected arg not associated with a flag is an error # An unexpected arg not associated with a flag is an error
fish_opt -s h -l help hello fish_opt -s h -l help hello
and echo unexpected status $status and echo unexpected status $status
#CHECKERR: fish_opt: expected <= 0 arguments; got 1 #CHECKERR: fish_opt: Extra non-option arguments were provided
# A -v / --validate without any arguments is an error
fish_opt -s h -l help -rv
and echo unexpected status $status
#CHECKERR: fish_opt: The --validate flag requires subsequent arguments
# A -v / --validate for boolean options is an error
fish_opt -s h -l help -v echo hello
and echo unexpected status $status
#CHECKERR: fish_opt: The --validate flag requires the --required-val, --optional-value, or --multiple-vals flag
# Now verify that valid combinations of options produces the correct output. # Now verify that valid combinations of options produces the correct output.
@@ -615,20 +625,20 @@ fish_opt --short h -l help --optional-val --long-only
or echo unexpected status $status or echo unexpected status $status
#CHECK: h-help=? #CHECK: h-help=?
# Repeated val, short and long valid # Repeated val, short and long valid, with validate
fish_opt --short h -l help --multiple-vals fish_opt --short h -l help --multiple-vals --validate _validate_int --max 500
or echo unexpected status $status or echo unexpected status $status
#CHECK: h/help=+ #CHECK: h/help=+!_validate_int --max 500
# Repeated and optional val, short and long valid # Repeated and optional val, short and long valid
fish_opt --short h -l help --optional-val -m fish_opt --short h -l help --optional-val -m
or echo unexpected status $status or echo unexpected status $status
#CHECK: h/help=* #CHECK: h/help=*
# Repeated val and short # Repeated val and short, with validate
fish_opt -ml help --long-only fish_opt -ml help --long-only -v test \$_flag_value != "' '"
or echo unexpected status $status or echo unexpected status $status
#CHECK: /help=+ #CHECK: /help=+!test $_flag_value != ' '
# Repeated and optional val, short and long but short not valid # Repeated and optional val, short and long but short not valid
fish_opt --short h -l help --long-only -mo fish_opt --short h -l help --long-only -mo