From 944cfd181ea5e2e2678eb5c60ad1808dd3e034ca Mon Sep 17 00:00:00 2001 From: Isaac Oscar Gariano Date: Sun, 3 Aug 2025 10:07:08 +1000 Subject: [PATCH] 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. --- CHANGELOG.rst | 1 + doc_src/cmds/fish_opt.rst | 16 +++++++++++++--- po/de.po | 12 ++++++++++++ po/en.po | 12 ++++++++++++ po/fr.po | 12 ++++++++++++ po/pl.po | 12 ++++++++++++ po/pt_BR.po | 12 ++++++++++++ po/sv.po | 12 ++++++++++++ po/zh_CN.po | 12 ++++++++++++ share/completions/fish_opt.fish | 1 + share/functions/fish_opt.fish | 21 +++++++++++++++++---- tests/checks/argparse.fish | 24 +++++++++++++++++------- 12 files changed, 133 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ecb12c044..1741cf180 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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``. - ``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. +- ``fish_opt`` now has a ``-v`` / ``--validate`` option you can use to give a fish script to validate values of the option. Interactive improvements ------------------------ diff --git a/doc_src/cmds/fish_opt.rst b/doc_src/cmds/fish_opt.rst index b03f1b127..08624246a 100644 --- a/doc_src/cmds/fish_opt.rst +++ b/doc_src/cmds/fish_opt.rst @@ -8,7 +8,7 @@ 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 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`` (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** 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 options $options (fish_opt -s m -l max --required-val) + set options $options (fish_opt -s m -l max -r) 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: @@ -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 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) argparse $options -- $argv diff --git a/po/de.po b/po/de.po index 9321cece5..f292f4baa 100644 --- a/po/de.po +++ b/po/de.po @@ -1939,6 +1939,9 @@ msgstr "" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgstr "" +msgid "%s: Extra non-option arguments were provided\\n" +msgstr "" + msgid "%s: Invalid mask '%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" 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" msgstr "" @@ -25444,6 +25453,9 @@ msgstr "" msgid "First remove existing destination files" msgstr "" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "" diff --git a/po/en.po b/po/en.po index 7dcf84e2a..1d0fe74df 100644 --- a/po/en.po +++ b/po/en.po @@ -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" 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" msgstr "%s: Invalid mask “%s”\\n" @@ -1947,6 +1950,12 @@ msgstr "" msgid "%s: The --short flag must be a single character\\n" 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" 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" msgstr "" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "" diff --git a/po/fr.po b/po/fr.po index 9240de8c1..1da521b76 100644 --- a/po/fr.po +++ b/po/fr.po @@ -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" 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" msgstr "%s : Masque '%s' invalide\\n" @@ -2048,6 +2051,12 @@ msgstr "" msgid "%s: The --short flag must be a single character\\n" 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" msgstr "%s : Le nombre de positions à passer doit être un entier naturel\\n" @@ -25541,6 +25550,9 @@ msgstr "" msgid "First remove existing destination files" msgstr "" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "" diff --git a/po/pl.po b/po/pl.po index ca3b00cd6..1b2188634 100644 --- a/po/pl.po +++ b/po/pl.po @@ -1931,6 +1931,9 @@ msgstr "" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgstr "" +msgid "%s: Extra non-option arguments were provided\\n" +msgstr "" + msgid "%s: Invalid mask '%s'\\n" msgstr "" @@ -1943,6 +1946,12 @@ msgstr "" msgid "%s: The --short flag must be a single character\\n" 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" msgstr "" @@ -25436,6 +25445,9 @@ msgstr "" msgid "First remove existing destination files" msgstr "" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index 6c67fe1a3..5c4241f93 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1936,6 +1936,9 @@ msgstr "" msgid "%s: Expected exactly one argument, got %s.\\n\\nSynopsis:\\n\\t%svared%s VARIABLE\\n" msgstr "" +msgid "%s: Extra non-option arguments were provided\\n" +msgstr "" + msgid "%s: Invalid mask '%s'\\n" msgstr "" @@ -1948,6 +1951,12 @@ msgstr "" msgid "%s: The --short flag must be a single character\\n" 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" msgstr "" @@ -25451,6 +25460,9 @@ msgstr "" msgid "First remove existing destination files" msgstr "" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "" diff --git a/po/sv.po b/po/sv.po index 2000963ab..3d2f0d09b 100644 --- a/po/sv.po +++ b/po/sv.po @@ -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" 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" msgstr "%s: Ogiltigt mask '%s'\\n" @@ -1944,6 +1947,12 @@ msgstr "" msgid "%s: The --short flag must be a single character\\n" 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" 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" msgstr "" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 92408da2c..e0a371558 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -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" 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" msgstr "%s:无效的掩码'%s'\\n" @@ -1946,6 +1949,12 @@ msgstr "" msgid "%s: The --short flag must be a single character\\n" 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" msgstr "%s:要跳过的位置数必须是非负整数\\n" @@ -25439,6 +25448,9 @@ msgstr "要转换的第一个页面" msgid "First remove existing destination files" msgstr "首先删除已有目的文件" +msgid "Fish script to validate option values" +msgstr "" + msgid "Fish's release notes" msgstr "fish的放行记录" diff --git a/share/completions/fish_opt.fish b/share/completions/fish_opt.fish index 6a8e14d0a..d5313cee3 100644 --- a/share/completions/fish_opt.fish +++ b/share/completions/fish_opt.fish @@ -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 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 v --long-option validate --require-parameter --description 'Fish script to validate option values' diff --git a/share/functions/fish_opt.fish b/share/functions/fish_opt.fish index ac793a3a0..c99d3a28c 100644 --- a/share/functions/fish_opt.fish +++ b/share/functions/fish_opt.fish @@ -1,6 +1,15 @@ # This is a helper function for `fish_opt`. It does some basic validation of the arguments. 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 return 1 else if not set -q _flag_short && not set -q _flag_long @@ -17,8 +26,8 @@ end # The `fish_opt` command. 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 - argparse -n fish_opt --max-args=0 --exclusive=r,o $options -- $argv + 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 --stop-nonopt --exclusive=r,o $options -- $argv or return if set -q _flag_help @@ -26,7 +35,7 @@ function fish_opt -d 'Produce an option specification suitable for use with `arg return 0 end - __fish_opt_validate_args + __fish_opt_validate_args $argv or return 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&" end + if set -q _flag_validate + set opt_spec "$opt_spec!$argv" + end + echo $opt_spec end diff --git a/tests/checks/argparse.fish b/tests/checks/argparse.fish index 420236238..2d13f0294 100644 --- a/tests/checks/argparse.fish +++ b/tests/checks/argparse.fish @@ -572,7 +572,17 @@ and echo unexpected status $status # An unexpected arg not associated with a flag is an error fish_opt -s h -l help hello 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. @@ -615,20 +625,20 @@ fish_opt --short h -l help --optional-val --long-only or echo unexpected status $status #CHECK: h-help=? -# Repeated val, short and long valid -fish_opt --short h -l help --multiple-vals +# Repeated val, short and long valid, with validate +fish_opt --short h -l help --multiple-vals --validate _validate_int --max 500 or echo unexpected status $status -#CHECK: h/help=+ +#CHECK: h/help=+!_validate_int --max 500 # Repeated and optional val, short and long valid fish_opt --short h -l help --optional-val -m or echo unexpected status $status #CHECK: h/help=* -# Repeated val and short -fish_opt -ml help --long-only +# Repeated val and short, with validate +fish_opt -ml help --long-only -v test \$_flag_value != "' '" or echo unexpected status $status -#CHECK: /help=+ +#CHECK: /help=+!test $_flag_value != ' ' # Repeated and optional val, short and long but short not valid fish_opt --short h -l help --long-only -mo