From 3e145076abdbaf5c6e47e311b5e659251604a49b Mon Sep 17 00:00:00 2001 From: Christopher Waldon Date: Fri, 12 Oct 2018 11:30:46 -0400 Subject: [PATCH 01/15] Clarify that altsrc supports both TOML and JSON --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f2baef4..e2df4ec 100644 --- a/README.md +++ b/README.md @@ -679,6 +679,7 @@ from other file input sources. Currently supported input source formats: * YAML +* JSON * TOML In order to get values for a flag from an alternate input source the following @@ -701,7 +702,7 @@ the yaml input source for any flags that are defined on that command. As a note the "load" flag used would also have to be defined on the command flags in order for this code snipped to work. -Currently only YAML and JSON files are supported but developers can add support +Currently only YAML, JSON, and TOML files are supported but developers can add support for other input sources by implementing the altsrc.InputSourceContext for their given sources. From 58a072d5733d4bb2dc61ffbc3557ec9592e34adc Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Wed, 20 Mar 2019 20:28:51 +0530 Subject: [PATCH 02/15] Add bash completion support for flags --- autocomplete/bash_autocomplete | 9 +++- help.go | 79 ++++++++++++++++++++++++++++++---- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index 37d9c14..d9305db 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -6,7 +6,14 @@ _cli_bash_autocomplete() { local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + if [[ $cur == -* ]]; then + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) + else + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + fi + if [[ "$opts1" == "$cur1" ]]; then + return 0 + fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 } diff --git a/help.go b/help.go index 65874fa..be79e88 100644 --- a/help.go +++ b/help.go @@ -4,9 +4,11 @@ import ( "fmt" "io" "os" + "regexp" "strings" "text/tabwriter" "text/template" + "unicode/utf8" ) // AppHelpTemplate is the text template for the Default help topic. @@ -152,19 +154,80 @@ func ShowAppHelp(c *Context) (err error) { return nil } +var shortFlagRegex = regexp.MustCompile(`^-`) + // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { - for _, command := range c.App.Commands { - if command.Hidden { - continue + DefaultAppCompleteWithFlags(nil)(c) +} + +func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { + return func(c *Context) { + if len(os.Args) > 2 { + lastArg := os.Args[len(os.Args)-2] + if strings.HasPrefix(lastArg, "-") { + lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") + lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") + for _, flag := range c.App.Flags { + for _, name := range strings.Split(flag.GetName(), ",") { + name = strings.Trim(name, " ") + if strings.HasPrefix(name, lastArg) && lastArg != name { + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + } + } + } + if cmd != nil { + for _, flag := range cmd.Flags { + for _, name := range strings.Split(flag.GetName(), ",") { + name = strings.Trim(name, " ") + if strings.HasPrefix(name, lastArg) && lastArg != name { + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + } + } + } + } + return + } } - if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + if cmd != nil { + for _, command := range cmd.Subcommands { + if command.Hidden { + continue + } + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + if name != "h" { + fmt.Fprintf(c.App.Writer, "%s\n", name) + } + } + } } } else { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s\n", name) + for _, command := range c.App.Commands { + if command.Hidden { + continue + } + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + fmt.Fprintf(c.App.Writer, "%s\n", name) + } + } } } } From fb1421d9031313c5e0f3c4a92625ed9cf5739b0d Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Wed, 20 Mar 2019 21:34:56 +0530 Subject: [PATCH 03/15] Fix duplicate completion of existing flag --- autocomplete/bash_autocomplete | 3 --- help.go | 42 ++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index d9305db..303d126 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -11,9 +11,6 @@ _cli_bash_autocomplete() { else opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) fi - if [[ "$opts1" == "$cur1" ]]; then - return 0 - fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 } diff --git a/help.go b/help.go index be79e88..bd68295 100644 --- a/help.go +++ b/help.go @@ -162,6 +162,22 @@ func DefaultAppComplete(c *Context) { } func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { + cliArgContains := func(flagName string) bool { + for _, name := range strings.Split(flagName, ",") { + name = strings.Trim(name, " ") + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + for _, a := range os.Args { + if a == flag { + return true + } + } + } + return false + } return func(c *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] @@ -171,12 +187,13 @@ func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { for _, flag := range c.App.Flags { for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") - if strings.HasPrefix(name, lastArg) && lastArg != name { - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { + fmt.Fprintln(c.App.Writer, flagCompletion) } } } @@ -184,12 +201,13 @@ func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { for _, flag := range cmd.Flags { for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") - if strings.HasPrefix(name, lastArg) && lastArg != name { - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - fmt.Fprintf(c.App.Writer, "%s%s\n", strings.Repeat("-", count), name) + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { + fmt.Fprintln(c.App.Writer, flagCompletion) } } } From 1d7a2b08d6f8e9764e2f2b911b1bb9fa49596f92 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Thu, 21 Mar 2019 13:01:48 +0530 Subject: [PATCH 04/15] Add default completion on commands, test cases, refactor code --- app_test.go | 54 ++++++++++++++++++++++ help.go | 130 ++++++++++++++++++++++++---------------------------- 2 files changed, 114 insertions(+), 70 deletions(-) diff --git a/app_test.go b/app_test.go index 629681e..e7841e6 100644 --- a/app_test.go +++ b/app_test.go @@ -221,6 +221,60 @@ func ExampleApp_Run_subcommandNoAction() { } +func ExampleApp_Run_bashComplete_withShortFlag() { + os.Args = []string{"greet", "-", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Flags = []Flag{ + IntFlag{ + Name: "other,o", + }, + StringFlag{ + Name: "xyz,x", + }, + } + + app.Run(os.Args) + // Output: + // --other + // -o + // --xyz + // -x + // --help + // -h + // --version + // -v +} + +func ExampleApp_Run_bashComplete_withLongFlag() { + os.Args = []string{"greet", "--s", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Flags = []Flag{ + IntFlag{ + Name: "other,o", + }, + StringFlag{ + Name: "xyz,x", + }, + StringFlag{ + Name: "some-flag,s", + }, + StringFlag{ + Name: "similar-flag", + }, + } + + app.Run(os.Args) + // Output: + // --some-flag + // --similar-flag +} + func ExampleApp_Run_bashComplete() { // set args for examples sake os.Args = []string{"greet", "--generate-bash-completion"} diff --git a/help.go b/help.go index bd68295..c20f5a4 100644 --- a/help.go +++ b/help.go @@ -158,95 +158,80 @@ var shortFlagRegex = regexp.MustCompile(`^-`) // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { - DefaultAppCompleteWithFlags(nil)(c) + DefaultCompleteWithFlags(nil)(c) } -func DefaultAppCompleteWithFlags(cmd *Command) func(c *Context) { - cliArgContains := func(flagName string) bool { - for _, name := range strings.Split(flagName, ",") { +func printCommandSuggestions(commands []Command, writer io.Writer) { + for _, command := range commands { + if command.Hidden { + continue + } + if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { + for _, name := range command.Names() { + fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) + } + } else { + for _, name := range command.Names() { + fmt.Fprintf(writer, "%s\n", name) + } + } + } +} + +func cliArgContains(flagName string) bool { + for _, name := range strings.Split(flagName, ",") { + name = strings.Trim(name, " ") + count := utf8.RuneCountInString(name) + if count > 2 { + count = 2 + } + flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + for _, a := range os.Args { + if a == flag { + return true + } + } + } + return false +} + +func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { + cur := shortFlagRegex.ReplaceAllString(lastArg, "") + cur = shortFlagRegex.ReplaceAllString(cur, "") + for _, flag := range flags { + for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") count := utf8.RuneCountInString(name) if count > 2 { count = 2 } - flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - for _, a := range os.Args { - if a == flag { - return true - } + if strings.HasPrefix(lastArg, "--") && count == 1 { + continue + } + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) { + fmt.Fprintln(writer, flagCompletion) } } - return false } +} + +func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { return func(c *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] if strings.HasPrefix(lastArg, "-") { - lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") - lastArg = shortFlagRegex.ReplaceAllString(lastArg, "") - for _, flag := range c.App.Flags { - for _, name := range strings.Split(flag.GetName(), ",") { - name = strings.Trim(name, " ") - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { - fmt.Fprintln(c.App.Writer, flagCompletion) - } - } - } + printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer) if cmd != nil { - for _, flag := range cmd.Flags { - for _, name := range strings.Split(flag.GetName(), ",") { - name = strings.Trim(name, " ") - count := utf8.RuneCountInString(name) - if count > 2 { - count = 2 - } - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) - if strings.HasPrefix(name, lastArg) && lastArg != name && !cliArgContains(flag.GetName()) { - fmt.Fprintln(c.App.Writer, flagCompletion) - } - } - } + printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer) } return } } if cmd != nil { - for _, command := range cmd.Subcommands { - if command.Hidden { - continue - } - if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) - } - } else { - for _, name := range command.Names() { - if name != "h" { - fmt.Fprintf(c.App.Writer, "%s\n", name) - } - } - } - } + printCommandSuggestions(cmd.Subcommands, c.App.Writer) } else { - for _, command := range c.App.Commands { - if command.Hidden { - continue - } - if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s:%s\n", name, command.Usage) - } - } else { - for _, name := range command.Names() { - fmt.Fprintf(c.App.Writer, "%s\n", name) - } - } - } + printCommandSuggestions(c.App.Commands, c.App.Writer) } } } @@ -309,9 +294,14 @@ func ShowCompletions(c *Context) { // ShowCommandCompletions prints the custom completions for a given command func ShowCommandCompletions(ctx *Context, command string) { c := ctx.App.Command(command) - if c != nil && c.BashComplete != nil { - c.BashComplete(ctx) + if c != nil { + if c.BashComplete != nil { + c.BashComplete(ctx) + } else { + DefaultCompleteWithFlags(c)(ctx) + } } + } func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) { From 62f02f21ef0b5c3c0aa67d3240aee15bc8a53457 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Thu, 11 Apr 2019 10:57:58 +0530 Subject: [PATCH 05/15] Don't complete hidden flags --- help.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/help.go b/help.go index c20f5a4..72c59b1 100644 --- a/help.go +++ b/help.go @@ -199,6 +199,9 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { cur := shortFlagRegex.ReplaceAllString(lastArg, "") cur = shortFlagRegex.ReplaceAllString(cur, "") for _, flag := range flags { + if bflag, ok := flag.(BoolFlag); ok && bflag.Hidden { + continue + } for _, name := range strings.Split(flag.GetName(), ",") { name = strings.Trim(name, " ") count := utf8.RuneCountInString(name) From d79d2a04242b21441061e00475287f4b826614f8 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Wed, 24 Jul 2019 16:08:47 +0200 Subject: [PATCH 06/15] Fix issue with source command completion Avoid competion for bash builtin `source` and fallback to default implementation as it throws below error ``` -bash: source: --: invalid option source: usage: source filename [arguments] ``` --- autocomplete/bash_autocomplete | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index 303d126..f5a8d1f 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -3,6 +3,7 @@ : ${PROG:=$(basename ${BASH_SOURCE})} _cli_bash_autocomplete() { + if [[ "${COMP_WORDS[@]:0:$COMP_CWORD}" != "source" ]]; then local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" @@ -13,8 +14,8 @@ _cli_bash_autocomplete() { fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 + fi } -complete -F _cli_bash_autocomplete $PROG - +complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG unset PROG From c75a689f629137700e8a30651f95cc41cf12a6d1 Mon Sep 17 00:00:00 2001 From: Jordan Christiansen Date: Fri, 2 Aug 2019 14:28:57 -0500 Subject: [PATCH 07/15] Make exit code example more clear The purpose of this example is to show that you can exit with an error code if a flag is unspecified, but with the code as it is, the only way to cause a non-zero exit is by adding the flag `--ginger-crouton=false`, which is not explained in the example. In this new version of the example, running the command with no flag will exit with an error, and running it with the flag will exit normally. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6eb2996..0a9cd32 100644 --- a/README.md +++ b/README.md @@ -901,14 +901,14 @@ import ( func main() { app := cli.NewApp() app.Flags = []cli.Flag{ - cli.BoolTFlag{ + cli.BoolFlag{ Name: "ginger-crouton", - Usage: "is it in the soup?", + Usage: "Add ginger croutons to the soup", }, } app.Action = func(ctx *cli.Context) error { if !ctx.Bool("ginger-crouton") { - return cli.NewExitError("it is not in the soup", 86) + return cli.NewExitError("Ginger croutons are not in the soup", 86) } return nil } From e8eac43d9d73e96f6b034f89770ab2fdbec2cb5b Mon Sep 17 00:00:00 2001 From: Lynn Cyrin Date: Fri, 2 Aug 2019 18:26:41 -0700 Subject: [PATCH 08/15] Update CHANGELOG.md --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401eae5..f7c4de0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ ## [Unreleased] +## 1.21.0 - 2019-08-02 + +### Fixed + +* + +### Changed + +* + +### Added + +* + ## 1.20.0 - 2017-08-10 ### Fixed @@ -407,7 +421,10 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[Unreleased]: https://github.com/urfave/cli/compare/v1.18.0...HEAD +[Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD +[1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 +[1.20.0]: https://github.com/urfave/cli/compare/v1.19.0...v1.20.0 +[1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0 [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 [1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 From 8a7f65e05215a76a2246b7b42cb9c082b5eae483 Mon Sep 17 00:00:00 2001 From: "Lynn Cyrin (they/them)" Date: Fri, 2 Aug 2019 19:30:41 -0700 Subject: [PATCH 09/15] Update CHANGELOG.md --- CHANGELOG.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c4de0..1e49d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,21 +4,28 @@ ## [Unreleased] -## 1.21.0 - 2019-08-02 +## [1.21.0] - 2019-08-02 ### Fixed -* +* Fix using "slice" flag types with `EnvVar` in [urfave/cli/pull/687](https://github.com/urfave/cli/pull/687) via [@joshuarubin](https://github.com/joshuarubin) +* Fix regression of `SkipFlagParsing` behavior in [urfave/cli/pull/697](https://github.com/urfave/cli/pull/697) via [@jszwedko](https://github.com/jszwedko) +* Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude) +* Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler) +* Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg) -### Changed - -* - -### Added +### Added / Changed -* +* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) +* Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro) +* Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz) +* Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko) +* Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz) +* Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus) +* Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann) +* Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude) -## 1.20.0 - 2017-08-10 +## [1.20.0] - 2017-08-10 ### Fixed @@ -423,7 +430,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. [Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 -[1.20.0]: https://github.com/urfave/cli/compare/v1.19.0...v1.20.0 +[1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 +[1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1 [1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0 [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 From 97dbddb32db290fdc6392e6a669a92acbadef9ff Mon Sep 17 00:00:00 2001 From: "[[ BOT ]] Lynn Cyrin" Date: Sat, 3 Aug 2019 10:23:29 -0700 Subject: [PATCH 10/15] use codeowners instead of maintainers --- .github/CODEOWNERS | 4 ++++ MAINTAINERS.md | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 .github/CODEOWNERS delete mode 100644 MAINTAINERS.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..4f138e8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# See https://help.github.com/articles/about-codeowners/ +# for more info about CODEOWNERS file + +* @urfave/cli diff --git a/MAINTAINERS.md b/MAINTAINERS.md deleted file mode 100644 index 5b7a6ea..0000000 --- a/MAINTAINERS.md +++ /dev/null @@ -1,4 +0,0 @@ -- @meatballhat -- @lynncyrin -- @AudriusButkevicius -- @asahasrabuddhe From 9938dec695d6a0ba5a4d84b703766333cd7d10e8 Mon Sep 17 00:00:00 2001 From: "[[ BOT ]] Lynn Cyrin" Date: Sat, 3 Aug 2019 10:26:07 -0700 Subject: [PATCH 11/15] update contributing docs --- CONTRIBUTING.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 329195e..9a4640a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,10 @@ ## Contributing -**NOTE**: the primary maintainer(s) may be found in -[./MAINTAINERS.md](./MAINTAINERS.md). +Use @urfave/cli to ping the maintainers. -Feel free to put up a pull request to fix a bug or maybe add a feature. I will +Feel free to put up a pull request to fix a bug or maybe add a feature. We will give it a code review and make sure that it does not break backwards -compatibility. If I or any other collaborators agree that it is in line with +compatibility. If collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. @@ -15,5 +14,5 @@ to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you feel like you have contributed to the project but have not yet been added -as a collaborator, we probably forgot to add you :sweat_smile:. Please open an +as a collaborator, we probably forgot to add you :sweat_smile:. Please open an issue! From c5612e8cd21e0cd99f73d23103df99a9af70f853 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 16:58:04 +0200 Subject: [PATCH 12/15] Fix review comments --- autocomplete/bash_autocomplete | 2 +- help.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index f5a8d1f..a118bda 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -7,7 +7,7 @@ _cli_bash_autocomplete() { local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - if [[ $cur == -* ]]; then + if [[ "$cur" == "-"* ]]; then opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) else opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) diff --git a/help.go b/help.go index 8e74ec4..efc3889 100644 --- a/help.go +++ b/help.go @@ -183,7 +183,7 @@ func printCommandSuggestions(commands []Command, writer io.Writer) { func cliArgContains(flagName string) bool { for _, name := range strings.Split(flagName, ",") { - name = strings.Trim(name, " ") + name = strings.TrimSpace(name) count := utf8.RuneCountInString(name) if count > 2 { count = 2 From c3f51bed6fffdf84227c5b59bd3f2e90683314df Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 17:07:46 +0200 Subject: [PATCH 13/15] Fix SC2199: Arrays implicitly concatenate in --- autocomplete/bash_autocomplete | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autocomplete/bash_autocomplete b/autocomplete/bash_autocomplete index a118bda..f0f6241 100755 --- a/autocomplete/bash_autocomplete +++ b/autocomplete/bash_autocomplete @@ -3,7 +3,7 @@ : ${PROG:=$(basename ${BASH_SOURCE})} _cli_bash_autocomplete() { - if [[ "${COMP_WORDS[@]:0:$COMP_CWORD}" != "source" ]]; then + if [[ "${COMP_WORDS[0]}" != "source" ]]; then local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" From 2be2bc755e4634d34136769a426a7ca52e698cc0 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 20:18:08 +0200 Subject: [PATCH 14/15] Add additional test for log flag completion and comments --- app_test.go | 29 +++++++++++++++++++++++++++++ help.go | 14 +++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/app_test.go b/app_test.go index 6d5ccd0..3fc27b5 100644 --- a/app_test.go +++ b/app_test.go @@ -274,6 +274,35 @@ func ExampleApp_Run_bashComplete_withLongFlag() { // --some-flag // --similar-flag } +func ExampleApp_Run_bashComplete_withMultipleLongFlag() { + os.Args = []string{"greet", "--st", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Flags = []Flag{ + IntFlag{ + Name: "int-flag,i", + }, + StringFlag{ + Name: "string,s", + }, + StringFlag{ + Name: "string-flag-2", + }, + StringFlag{ + Name: "similar-flag", + }, + StringFlag{ + Name: "some-flag", + }, + } + + app.Run(os.Args) + // Output: + // --string + // --string-flag-2 +} func ExampleApp_Run_bashComplete() { // set args for examples sake diff --git a/help.go b/help.go index efc3889..d057a03 100644 --- a/help.go +++ b/help.go @@ -199,23 +199,27 @@ func cliArgContains(flagName string) bool { } func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { - cur := shortFlagRegex.ReplaceAllString(lastArg, "") - cur = shortFlagRegex.ReplaceAllString(cur, "") + cur := strings.TrimPrefix(lastArg, "-") + cur = strings.TrimPrefix(cur, "-") for _, flag := range flags { if bflag, ok := flag.(BoolFlag); ok && bflag.Hidden { continue } for _, name := range strings.Split(flag.GetName(), ",") { - name = strings.Trim(name, " ") + name = strings.TrimSpace(name) + // this will get total count utf8 letters in flag name count := utf8.RuneCountInString(name) if count > 2 { - count = 2 + count = 2 // resuse this count to generate single - or -- in flag completion } + // if flag name has more than one utf8 letter and last argument in cli has -- prefix then + // skip flag completion for short flags example -v or -x if strings.HasPrefix(lastArg, "--") && count == 1 { continue } - flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) + // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) { + flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) fmt.Fprintln(writer, flagCompletion) } } From 1db049685ac49e11b2e27285e1287793cfe0ea84 Mon Sep 17 00:00:00 2001 From: Yogesh Lonkar Date: Mon, 5 Aug 2019 20:22:52 +0200 Subject: [PATCH 15/15] Fix unused regex --- help.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/help.go b/help.go index d057a03..e504fc2 100644 --- a/help.go +++ b/help.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "os" - "regexp" "strings" "text/tabwriter" "text/template" @@ -157,8 +156,6 @@ func ShowAppHelp(c *Context) (err error) { return nil } -var shortFlagRegex = regexp.MustCompile(`^-`) - // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { DefaultCompleteWithFlags(nil)(c)