Merge pull request #684 from baude/shortOptionHandling
Combine bool short names
This commit is contained in:
commit
7ace96b43d
21
README.md
21
README.md
@ -47,6 +47,7 @@ applications in an expressive way.
|
||||
* [Version Flag](#version-flag)
|
||||
+ [Customization](#customization-2)
|
||||
+ [Full API Example](#full-api-example)
|
||||
* [Combining short Bool options](#combining-short-bool-options)
|
||||
- [Contribution Guidelines](#contribution-guidelines)
|
||||
|
||||
<!-- tocstop -->
|
||||
@ -1410,6 +1411,26 @@ func wopAction(c *cli.Context) error {
|
||||
}
|
||||
```
|
||||
|
||||
### Combining short Bool options
|
||||
|
||||
Traditional use of boolean options using their shortnames look like this:
|
||||
```
|
||||
# cmd foobar -s -o
|
||||
```
|
||||
|
||||
Suppose you want users to be able to combine your bool options with their shortname. This
|
||||
can be done using the **UseShortOptionHandling** bool in your commands. Suppose your program
|
||||
has a two bool flags such as *serve* and *option* with the short options of *-o* and
|
||||
*-s* respectively. With **UseShortOptionHandling** set to *true*, a user can use a syntax
|
||||
like:
|
||||
```
|
||||
# cmd foobar -so
|
||||
```
|
||||
|
||||
If you enable the **UseShortOptionHandling*, then you must not use any flags that have a single
|
||||
leading *-* or this will result in failures. For example, **-option** can no longer be used. Flags
|
||||
with two leading dashes (such as **--options**) are still valid.
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
Feel free to put up a pull request to fix a bug or maybe add a feature. I will
|
||||
|
22
command.go
22
command.go
@ -55,6 +55,10 @@ type Command struct {
|
||||
HideHelp bool
|
||||
// Boolean to hide this command from help or completion
|
||||
Hidden bool
|
||||
// Boolean to enable short-option handling so user can combine several
|
||||
// single-character bool arguements into one
|
||||
// i.e. foobar -o -v -> foobar -ov
|
||||
UseShortOptionHandling bool
|
||||
|
||||
// Full name of command for help, defaults to full command name, including parent commands.
|
||||
HelpName string
|
||||
@ -141,8 +145,22 @@ func (c Command) Run(ctx *Context) (err error) {
|
||||
} else {
|
||||
flagArgs = args[firstFlagIndex:]
|
||||
}
|
||||
|
||||
err = set.Parse(append(flagArgs, regularArgs...))
|
||||
// separate combined flags
|
||||
if c.UseShortOptionHandling {
|
||||
var flagArgsSeparated []string
|
||||
for _, flagArg := range flagArgs {
|
||||
if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 {
|
||||
for _, flagChar := range flagArg[1:] {
|
||||
flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar))
|
||||
}
|
||||
} else {
|
||||
flagArgsSeparated = append(flagArgsSeparated, flagArg)
|
||||
}
|
||||
}
|
||||
err = set.Parse(append(flagArgsSeparated, regularArgs...))
|
||||
} else {
|
||||
err = set.Parse(append(flagArgs, regularArgs...))
|
||||
}
|
||||
} else {
|
||||
err = set.Parse(ctx.Args().Tail())
|
||||
}
|
||||
|
@ -11,20 +11,23 @@ import (
|
||||
|
||||
func TestCommandFlagParsing(t *testing.T) {
|
||||
cases := []struct {
|
||||
testArgs []string
|
||||
skipFlagParsing bool
|
||||
skipArgReorder bool
|
||||
expectedErr error
|
||||
testArgs []string
|
||||
skipFlagParsing bool
|
||||
skipArgReorder bool
|
||||
expectedErr error
|
||||
UseShortOptionHandling bool
|
||||
}{
|
||||
// Test normal "not ignoring flags" flow
|
||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")},
|
||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break"), false},
|
||||
|
||||
// Test no arg reorder
|
||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil},
|
||||
{[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false},
|
||||
|
||||
{[]string{"test-cmd", "blah", "blah"}, true, false, nil, false}, // Test SkipFlagParsing without any args that look like flags
|
||||
{[]string{"test-cmd", "blah", "-break"}, true, false, nil, false}, // Test SkipFlagParsing with random flag arg
|
||||
{[]string{"test-cmd", "blah", "-help"}, true, false, nil, false}, // Test SkipFlagParsing with "special" help flag arg
|
||||
{[]string{"test-cmd", "blah"}, false, false, nil, true}, // Test UseShortOptionHandling
|
||||
|
||||
{[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags
|
||||
{[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg
|
||||
{[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@ -36,13 +39,14 @@ func TestCommandFlagParsing(t *testing.T) {
|
||||
context := NewContext(app, set, nil)
|
||||
|
||||
command := Command{
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *Context) error { return nil },
|
||||
SkipFlagParsing: c.skipFlagParsing,
|
||||
SkipArgReorder: c.skipArgReorder,
|
||||
Name: "test-cmd",
|
||||
Aliases: []string{"tc"},
|
||||
Usage: "this is for testing",
|
||||
Description: "testing",
|
||||
Action: func(_ *Context) error { return nil },
|
||||
SkipFlagParsing: c.skipFlagParsing,
|
||||
SkipArgReorder: c.skipArgReorder,
|
||||
UseShortOptionHandling: c.UseShortOptionHandling,
|
||||
}
|
||||
|
||||
err := command.Run(context)
|
||||
|
25
flag_test.go
25
flag_test.go
@ -1048,6 +1048,31 @@ func TestParseMultiBool(t *testing.T) {
|
||||
a.Run([]string{"run", "--serve"})
|
||||
}
|
||||
|
||||
func TestParseBoolShortOptionHandle(t *testing.T) {
|
||||
a := App{
|
||||
Commands: []Command{
|
||||
{
|
||||
Name: "foobar",
|
||||
UseShortOptionHandling: true,
|
||||
Action: func(ctx *Context) error {
|
||||
if ctx.Bool("serve") != true {
|
||||
t.Errorf("main name not set")
|
||||
}
|
||||
if ctx.Bool("option") != true {
|
||||
t.Errorf("short name not set")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Flags: []Flag{
|
||||
BoolFlag{Name: "serve, s"},
|
||||
BoolFlag{Name: "option, o"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
a.Run([]string{"run", "foobar", "-so"})
|
||||
}
|
||||
|
||||
func TestParseDestinationBool(t *testing.T) {
|
||||
var dest bool
|
||||
a := App{
|
||||
|
Loading…
Reference in New Issue
Block a user