From b73fe687e6be798232343a6b7344e43e1d3e0f1b Mon Sep 17 00:00:00 2001 From: Sam Ghods Date: Thu, 25 Sep 2014 16:03:34 -0700 Subject: [PATCH] Replace prefix matching with aliases --- cobra_test.go | 36 +++++++++--------------------------- command.go | 38 +++++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/cobra_test.go b/cobra_test.go index 5f4af27..7b5ed62 100644 --- a/cobra_test.go +++ b/cobra_test.go @@ -26,9 +26,10 @@ var cmdPrint = &Command{ } var cmdEcho = &Command{ - Use: "echo [string to echo]", - Short: "Echo anything to the screen", - Long: `an utterly useless command for testing.`, + Use: "echo [string to echo]", + Aliases: []string{"say"}, + Short: "Echo anything to the screen", + Long: `an utterly useless command for testing.`, Run: func(cmd *Command, args []string) { te = args }, @@ -38,7 +39,9 @@ var cmdTimes = &Command{ Use: "times [# times] [string to echo]", Short: "Echo anything to the screen more times", Long: `an slightly useless command for testing.`, - Run: timesRunner, + Run: func(cmd *Command, args []string) { + tt = args + }, } var cmdRootNoRun = &Command{ @@ -62,10 +65,6 @@ var cmdRootWithRun = &Command{ }, } -func timesRunner(cmd *Command, args []string) { - tt = args -} - func flagInit() { cmdEcho.ResetFlags() cmdPrint.ResetFlags() @@ -195,8 +194,8 @@ func TestChildCommand(t *testing.T) { } } -func TestChildCommandPrefix(t *testing.T) { - noRRSetupTest("ech tim one two") +func TestCommandAlias(t *testing.T) { + noRRSetupTest("say times one two") if te != nil || tp != nil { t.Error("Wrong command called") @@ -226,23 +225,6 @@ func TestChildSameName(t *testing.T) { } } -func TestChildSameNamePrefix(t *testing.T) { - c := initializeWithSameName() - c.AddCommand(cmdPrint, cmdEcho) - c.SetArgs(strings.Split("pr one two", " ")) - c.Execute() - - if te != nil || tt != nil { - t.Error("Wrong command called") - } - if tp == nil { - t.Error("Wrong command called") - } - if strings.Join(tp, " ") != "one two" { - t.Error("Command didn't parse correctly") - } -} - func TestFlagLong(t *testing.T) { noRRSetupTest("echo --intone=13 something here") diff --git a/command.go b/command.go index d6de8c6..be4bd0c 100644 --- a/command.go +++ b/command.go @@ -35,6 +35,8 @@ type Command struct { name string // The one-line usage message. Use string + // An array of aliases that can be used instead of the first word in Use. + Aliases []string // The short description shown in the 'help' output. Short string // The long message shown in the 'help ' output. @@ -189,7 +191,10 @@ func (c *Command) UsageTemplate() string { return `{{ $cmd := . }} Usage: {{if .Runnable}} {{.UseLine}}{{if .HasFlags}} [flags]{{end}}{{end}}{{if .HasSubCommands}} - {{ .CommandPath}} [command]{{end}} + {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}} + +Aliases: + {{.NameAndAliases}}{{end}} {{ if .HasSubCommands}} Available Commands: {{range .Commands}}{{if .Runnable}} {{rpad .Use .UsagePadding }} {{.Short}}{{end}}{{end}} @@ -281,19 +286,11 @@ func (c *Command) Find(arrs []string) (*Command, []string, error) { if len(args) > 0 && c.HasSubCommands() { argsWOflags := stripFlags(args) if len(argsWOflags) > 0 { - matches := make([]*Command, 0) for _, cmd := range c.commands { - if cmd.Name() == argsWOflags[0] { // exact name match - return innerfind(cmd, argsMinusX(args, cmd.Name())) - } else if strings.HasPrefix(cmd.Name(), argsWOflags[0]) { // prefix match - matches = append(matches, cmd) + if cmd.Name() == argsWOflags[0] || cmd.HasAlias(argsWOflags[0]) { // exact name or alias match + return innerfind(cmd, argsMinusX(args, argsWOflags[0])) } } - - // only accept a single prefix match - multiple matches would be ambiguous - if len(matches) == 1 { - return innerfind(matches[0], argsMinusX(args, argsWOflags[0])) - } } } @@ -302,8 +299,9 @@ func (c *Command) Find(arrs []string) (*Command, []string, error) { commandFound, a := innerfind(c, arrs) - // if commander returned and not appropriately matched return nil & error - if commandFound.Name() == c.Name() && !strings.HasPrefix(commandFound.Name(), arrs[0]) { + // if commander returned and the first argument (if it exists) doesn't + // match the command name, return nil & error + if commandFound.Name() == c.Name() && len(arrs[0]) > 0 && commandFound.Name() != arrs[0] { return nil, a, fmt.Errorf("unknown command %q\nRun 'help' for usage.\n", a[0]) } @@ -614,6 +612,20 @@ func (c *Command) Name() string { return name } +// Determine if a given string is an alias of the command. +func (c *Command) HasAlias(s string) bool { + for _, a := range c.Aliases { + if a == s { + return true + } + } + return false +} + +func (c *Command) NameAndAliases() string { + return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ") +} + // Determine if the command is itself runnable func (c *Command) Runnable() bool { return c.Run != nil