implement fish_opt helper command

This implements a `fish_opt` command that provides a way for people
to create option specs for the `argparse` command as an alternative to
creating such strings by hand.

Fixes #4190
This commit is contained in:
Kurtis Rader 2017-07-09 20:03:26 -07:00
parent 9adf500cf3
commit a4dc2b872b
5 changed files with 193 additions and 0 deletions

54
doc_src/fish_opt.txt Normal file
View file

@ -0,0 +1,54 @@
\section fish_opt fish_opt - create an option spec for the argparse command
\subsection fish_opt-synopsis Synopsis
\fish{synopsis}
fish_opt [ -h | --help ]
fish_opt ( -s X | --short=X ) [ -l LONG | --long=LONG ] [ --long-only ] \
[ -o | --optional-val ] [ -r | --required-val ] [ --multiple-vals ]
\endfish
\subsection fish_opt-description Description
This command provides a way to produce option specifications suitable for use with the <a href="#argparse">`argparse`</a> command. You can, of course, write the option specs by hand without using this command. But you might prefer to use this for the clarity it provides.
The following `argparse` options are available:
- `-s` or `--short` takes a single letter that is used as the short flag in the option being defined. This option is mandatory.
- `-l` or `--long` takes a string that is used as the long flag in the option being defined. This option is optional and has no default. If no long flag is defined then only the short flag will be allowed when parsing arguments using the option spec.
- `--long-only` means the option spec being defined will only allow the long flag name to be used. The short flag name must still be defined (i.e., `--short` must be specified) but it cannot be used when parsing args using this option spec.
- `-o` or `--optional` means the option being defined can take a value but it is optional rather than required. If the option is seen more than once when parsing arguments only the last value seen is saved. This means the resulting flag variable created by `argparse` will zero elements if no value was given with the option else it will have exactly one element.
- `-r` or `--required` means the option being defined requires a value. If the option is seen more than once when parsing arguments only the last value seen is saved. This means the resulting flag variable created by `argparse` will have exactly one element.
- `--multiple-vals` means the option being defined requires a value each time it is seen. Each instance is stored. This means the resulting flag variable created by `argparse` will have one element for each instance of this option in the args.
- `-h` or `--help` displays help about using this command.
\subsection fish_opt-examples Examples
Define a single option spec for the boolean help flag:
\fish
set -l options (fish_opt -s h -l help)
argparse $options -- $argv
\endfish
Same as above but with a second flag that requires a value:
\fish
set -l options (fish_opt -s h -l help)
set options $options (fish_opt -s m -l max --required-val)
argparse $options -- $argv
\endfish
Same as above but with a third flag that can be given multiple times saving the value of each instance seen and only the long flag name (`--token`) can be used:
\fish
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=t --long=token --multiple-vals --long-only)
argparse $options -- $argv
\endfish

View file

@ -0,0 +1,47 @@
# 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 not set -q _flag_short
or test 1 -ne (string length -- $_flag_short)
printf (_ "%s: The --short flag is required and must be a single character\n") fish_opt >&2
return 1
end
return 0
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=' 'o/optional-val' 'r/required-val'
set options $options 'L-long-only' 'M-multiple-vals'
argparse -n fish_opt --max-args=0 --exclusive=r,o --exclusive=M,o $options -- $argv
or return
if set -q _flag_help
__fish_print_help nextd
return 0
end
__fish_opt_validate_args
or return
set -l opt_spec $_flag_short
if set -q _flag_long
if set -q _flag_long_only
set opt_spec "$opt_spec-"
else
set opt_spec "$opt_spec/"
end
set opt_spec "$opt_spec$_flag_long"
end
if set -q _flag_multiple_vals
set opt_spec "$opt_spec=+"
else if set -q _flag_required_val
set opt_spec "$opt_spec="
else if set -q _flag_optional_val
and set opt_spec "$opt_spec=?"
end
echo $opt_spec
end

11
tests/fish_opt.err Normal file
View file

@ -0,0 +1,11 @@
# no args is an error
fish_opt: The --short flag is required and must be a single character
# no short flag or an invalid short flag is an error
fish_opt: The --short flag is required and must be a single character
fish_opt: The --short flag is required and must be a single character
# a required and optional arg makes no sense
fish_opt: Mutually exclusive flags 'o/optional-val' and `r/required-val` seen
# a repeated and optional arg makes no sense
fish_opt: Mutually exclusive flags 'multiple-vals' and `o/optional-val` seen
# an unexpected arg not associated with a flag is an error
fish_opt: Expected at most 0 args, got 1

62
tests/fish_opt.in Normal file
View file

@ -0,0 +1,62 @@
# Start by testing a bunch of error conditions.
echo '# no args is an error' >&2
fish_opt
and echo unexpected status $status
echo '# no short flag or an invalid short flag is an error' >&2
fish_opt -l help
and echo unexpected status $status
fish_opt -s help
and echo unexpected status $status
echo '# a required and optional arg makes no sense' >&2
fish_opt -s h -l help -r --optional-val
and echo unexpected status $status
echo '# a repeated and optional arg makes no sense' >&2
fish_opt -s h -l help --multiple-vals --optional-val
and echo unexpected status $status
echo '# an unexpected arg not associated with a flag is an error' >&2
fish_opt -s h -l help hello
and echo unexpected status $status
# Now verify that valid combinations of options produces the correct output.
echo '# bool, short only'
fish_opt -s h
or echo unexpected status $status
echo '# bool, short and long'
fish_opt --short h --long help
or echo unexpected status $status
echo '# bool, short and long but the short var cannot be used'
fish_opt --short h --long help --long-only
echo '# required val, short and long but the short var cannot be used'
fish_opt --short h --long help -r --long-only
or echo unexpected status $status
echo '# optional val, short and long valid'
fish_opt --short h -l help --optional-val
or echo unexpected status $status
echo '# optional val, short and long but the short var cannot be used'
fish_opt --short h -l help --optional-val --long-only
or echo unexpected status $status
echo '# repeated val, short and long valid'
fish_opt --short h -l help --multiple-vals
or echo unexpected status $status
echo '# repeated val, short and long but short not valid'
fish_opt --short h -l help --multiple-vals --long-only
or echo unexpected status $status
echo '# repeated val, short only'
fish_opt -s h --multiple-vals
or echo unexpected status $status
fish_opt -s h --multiple-vals --long-only
or echo unexpected status $status

19
tests/fish_opt.out Normal file
View file

@ -0,0 +1,19 @@
# bool, short only
h
# bool, short and long
h/help
# bool, short and long but the short var cannot be used
h-help
# required val, short and long but the short var cannot be used
h-help=
# optional val, short and long valid
h/help=?
# optional val, short and long but the short var cannot be used
h-help=?
# repeated val, short and long valid
h/help=+
# repeated val, short and long but short not valid
h-help=+
# repeated val, short only
h=+
h=+