diff --git a/args.go b/args.go index a5d8a927..57b3c667 100644 --- a/args.go +++ b/args.go @@ -35,7 +35,14 @@ func NoArgs(cmd *Command, args []string) error { func OnlyValidArgs(cmd *Command, args []string) error { if len(cmd.ValidArgs) > 0 { for _, v := range args { - if !stringInSlice(v, cmd.ValidArgs) { + if !func() bool { + for _, cmdargs := range cmd.ValidArgs { + if v == cmdargs.Name { + return true + } + } + return false + }() { return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0])) } } diff --git a/args_test.go b/args_test.go index d797b6f5..8139b774 100644 --- a/args_test.go +++ b/args_test.go @@ -34,10 +34,13 @@ func TestNoArgsWithArgs(t *testing.T) { func TestOnlyValidArgs(t *testing.T) { c := &Command{ - Use: "c", - Args: OnlyValidArgs, - ValidArgs: []string{"one", "two"}, - Run: emptyRun, + Use: "c", + Args: OnlyValidArgs, + ValidArgs: []ValidArgs{ + {"one", "one thing"}, + {"two", "two things"}, + }, + Run: emptyRun, } output, err := executeCommand(c, "one", "two") @@ -51,10 +54,13 @@ func TestOnlyValidArgs(t *testing.T) { func TestOnlyValidArgsWithInvalidArgs(t *testing.T) { c := &Command{ - Use: "c", - Args: OnlyValidArgs, - ValidArgs: []string{"one", "two"}, - Run: emptyRun, + Use: "c", + Args: OnlyValidArgs, + ValidArgs: []ValidArgs{ + {"one", "one thing"}, + {"two", "two things"}, + }, + Run: emptyRun, } _, err := executeCommand(c, "three") diff --git a/bash_completions.go b/bash_completions.go index c19fe7a0..c8e20f0f 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -428,9 +428,11 @@ func writeRequiredFlag(buf *bytes.Buffer, cmd *Command) { func writeRequiredNouns(buf *bytes.Buffer, cmd *Command) { buf.WriteString(" must_have_one_noun=()\n") - sort.Sort(sort.StringSlice(cmd.ValidArgs)) + sort.Slice(cmd.ValidArgs, func(i, j int) bool { + return cmd.ValidArgs[i].Name < cmd.ValidArgs[j].Name + }) for _, value := range cmd.ValidArgs { - buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) + buf.WriteString(fmt.Sprintf(" must_have_one_noun+=(%q)\n", value.Name)) } } diff --git a/bash_completions.md b/bash_completions.md index 52bd39dd..9c60772b 100644 --- a/bash_completions.md +++ b/bash_completions.md @@ -80,7 +80,12 @@ The `BashCompletionFunction` option is really only valid/useful on the root comm In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like: ```go -validArgs []string = { "pod", "node", "service", "replicationcontroller" } +validArgs []ValidArgs = { + {"pod", ""}, + {"node", ""}, + {"service", ""}, + {"replicationcontroller", ""}, +} cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", diff --git a/bash_completions_test.go b/bash_completions_test.go index a0da8714..6014fc47 100644 --- a/bash_completions_test.go +++ b/bash_completions_test.go @@ -48,9 +48,14 @@ const bashCompletionFunc = `__custom_func() { func TestBashCompletions(t *testing.T) { rootCmd := &Command{ - Use: "root", - ArgAliases: []string{"pods", "nodes", "services", "replicationcontrollers", "po", "no", "svc", "rc"}, - ValidArgs: []string{"pod", "node", "service", "replicationcontroller"}, + Use: "root", + ArgAliases: []string{"pods", "nodes", "services", "replicationcontrollers", "po", "no", "svc", "rc"}, + ValidArgs: []ValidArgs{ + {"pod", ""}, + {"node", ""}, + {"service", ""}, + {"replicationcontroller", ""}, + }, BashCompletionFunction: bashCompletionFunc, Run: emptyRun, } @@ -111,10 +116,15 @@ func TestBashCompletions(t *testing.T) { Use: "times [# times] [string to echo]", SuggestFor: []string{"counts"}, Args: OnlyValidArgs, - ValidArgs: []string{"one", "two", "three", "four"}, - Short: "Echo anything to the screen more times", - Long: "a slightly useless command for testing.", - Run: emptyRun, + ValidArgs: []ValidArgs{ + {"one", ""}, + {"two", ""}, + {"three", ""}, + {"four", ""}, + }, + Short: "Echo anything to the screen more times", + Long: "a slightly useless command for testing.", + Run: emptyRun, } echoCmd.AddCommand(timesCmd) diff --git a/cobra.go b/cobra.go index e4b910c5..0b232279 100644 --- a/cobra.go +++ b/cobra.go @@ -1,4 +1,4 @@ -// Copyright © 2013 Steve Francia . +// Copyright © 2013 Steve Francia . // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/command.go b/command.go index 6cb64264..3de13542 100644 --- a/command.go +++ b/command.go @@ -1,4 +1,3 @@ -// Copyright © 2013 Steve Francia . // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +26,17 @@ import ( flag "github.com/spf13/pflag" ) +// This structure has been created to replace +// ValidArgs []string in Command object to prevent +// wasting memory when new command objects are added simply +// to add new subsubcommands. +type ValidArgs struct { + // Name of subcommand + Name string + // Short description + Short string +} + // Command is just that, a command for your application. // E.g. 'go run ...' - 'run' is the command. Cobra requires // you to define the usage and description as part of your command @@ -52,7 +62,9 @@ type Command struct { Example string // ValidArgs is list of all valid non-flag arguments that are accepted in bash completions - ValidArgs []string + // We can use ValidArgs instead of adding new commands to list. This is to prevent + // wasting memory with new Command objects. + ValidArgs []ValidArgs // Expected arguments Args PositionalArgs @@ -372,7 +384,10 @@ func (c *Command) UsageTemplate() string { {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: - {{.NameAndAliases}}{{end}}{{if .HasExample}} + {{.NameAndAliases}}{{end}}{{if .HasValidArgs}} + +Valid Args:{{range .ValidArgs}} + {{.Name}} {{.Short}}{{end}}{{end}}{{if .HasExample}} Examples: {{.Example}}{{end}}{{if .HasAvailableSubCommands}} @@ -393,6 +408,13 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e ` } +func (c *Command) HasValidArgs() bool { + if len(c.ValidArgs) > 0 { + return true + } + return false +} + // HelpTemplate return help template for the command. func (c *Command) HelpTemplate() string { if c.helpTemplate != "" {