diff --git a/.github/ISSUE_TEMPLATE/v1-bug-report.md b/.github/ISSUE_TEMPLATE/v1-bug-report.md index b253666..20dc0b9 100644 --- a/.github/ISSUE_TEMPLATE/v1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/v1-bug-report.md @@ -7,12 +7,23 @@ assignees: lynncyrin --- +## my urfave/cli version is + +_**( Put the version of urfave/cli that you are using here )**_ + ## Checklist * [ ] Are you running the latest v1 release? The list of releases is [here](https://github.com/urfave/cli/releases). * [ ] Did you check the manual for your release? The v1 manual is [here](https://github.com/urfave/cli/blob/master/docs/v1/manual.md) * [ ] Did you perform a search about this problem? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching. +## Packaging Setup + +- [ ] My project is using go modules. +- [ ] My project is using vendoring. +- [ ] My project is automatically downloading the latest version. +- [ ] I am unsure of what my packaging setup is. + ## Describe the bug A clear and concise description of what the bug is. diff --git a/.github/ISSUE_TEMPLATE/v2-bug-report.md b/.github/ISSUE_TEMPLATE/v2-bug-report.md index f238e85..5c20104 100644 --- a/.github/ISSUE_TEMPLATE/v2-bug-report.md +++ b/.github/ISSUE_TEMPLATE/v2-bug-report.md @@ -7,12 +7,23 @@ assignees: '' --- +## my urfave/cli version is + +_**( Put the version of urfave/cli that you are using here )**_ + ## Checklist * [ ] Are you running the latest v2 release? The list of releases is [here](https://github.com/urfave/cli/releases). * [ ] Did you check the manual for your release? The v2 manual is [here](https://github.com/urfave/cli/blob/master/docs/v2/manual.md) * [ ] Did you perform a search about this problem? Here's the [Github guide](https://help.github.com/en/github/managing-your-work-on-github/using-search-to-filter-issues-and-pull-requests) about searching. +## Packaging Setup + +- [ ] My project is using go modules. +- [ ] My project is using vendoring. +- [ ] My project is automatically downloading the latest version. +- [ ] I am unsure of what my packaging setup is. + ## Describe the bug A clear and concise description of what the bug is. diff --git a/app.go b/app.go index 5e5c856..a62aa23 100644 --- a/app.go +++ b/app.go @@ -222,7 +222,7 @@ func (a *App) Run(arguments []string) (err error) { return err } - err = parseIter(set, a, arguments[1:]) + err = parseIter(set, a, arguments[1:], shellComplete) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { @@ -348,7 +348,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) { return err } - err = parseIter(set, a, ctx.Args().Tail()) + err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, ctx) diff --git a/command.go b/command.go index a579fdb..db6c802 100644 --- a/command.go +++ b/command.go @@ -100,7 +100,7 @@ func (c *Command) Run(ctx *Context) (err error) { c.UseShortOptionHandling = true } - set, err := c.parseFlags(ctx.Args()) + set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) context := NewContext(ctx.App, set, ctx) context.Command = c @@ -174,7 +174,7 @@ func (c *Command) useShortOptionHandling() bool { return c.UseShortOptionHandling } -func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { +func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) { set, err := c.newFlagSet() if err != nil { return nil, err @@ -184,7 +184,7 @@ func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) { return set, set.Parse(append([]string{"--"}, args.Tail()...)) } - err = parseIter(set, c, args.Tail()) + err = parseIter(set, c, args.Tail(), shellComplete) if err != nil { return nil, err } diff --git a/command_test.go b/command_test.go index 7c55f13..8eafe68 100644 --- a/command_test.go +++ b/command_test.go @@ -1,6 +1,7 @@ package cli import ( + "bytes" "errors" "flag" "fmt" @@ -329,3 +330,49 @@ func TestCommandSkipFlagParsing(t *testing.T) { expect(t, args, c.expectedArgs) } } + +func TestCommand_Run_CustomShellCompleteAcceptsMalformedFlags(t *testing.T) { + cases := []struct { + testArgs args + expectedOut string + }{ + {testArgs: args{"--undefined"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number", "fourty-two"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number", "42"}, expectedOut: "found 0 args"}, + {testArgs: args{"--number", "42", "newArg"}, expectedOut: "found 1 args"}, + } + + for _, c := range cases { + var outputBuffer bytes.Buffer + app := &App{ + Writer: &outputBuffer, + EnableBashCompletion: true, + Commands: []*Command{ + { + Name: "bar", + Usage: "this is for testing", + Flags: []Flag{ + &IntFlag{ + Name: "number", + Usage: "A number to parse", + }, + }, + BashComplete: func(c *Context) { + fmt.Fprintf(c.App.Writer, "found %d args", c.NArg()) + }, + }, + }, + } + + osArgs := args{"foo", "bar"} + osArgs = append(osArgs, c.testArgs...) + osArgs = append(osArgs, "--generate-bash-completion") + + err := app.Run(osArgs) + stdout := outputBuffer.String() + expect(t, err, nil) + expect(t, stdout, c.expectedOut) + } + +} diff --git a/parse.go b/parse.go index 660f538..7df1729 100644 --- a/parse.go +++ b/parse.go @@ -11,13 +11,18 @@ type iterativeParser interface { } // To enable short-option handling (e.g., "-it" vs "-i -t") we have to -// iteratively catch parsing errors. This way we achieve LR parsing without +// iteratively catch parsing errors. This way we achieve LR parsing without // transforming any arguments. Otherwise, there is no way we can discriminate // combined short options from common arguments that should be left untouched. -func parseIter(set *flag.FlagSet, ip iterativeParser, args []string) error { +// Pass `shellComplete` to continue parsing options on failure during shell +// completion when, the user-supplied options may be incomplete. +func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error { for { err := set.Parse(args) if !ip.useShortOptionHandling() || err == nil { + if shellComplete { + return nil + } return err }