Sort commands by their names

The slice of commands are sorted now automatically while Commands are called.
To turn off this feature, EnableCommandSorting variable is added.
This commit is contained in:
Andrey Kurilin 2016-06-14 17:04:53 +03:00
parent 1238ba19d2
commit ab97c7a63a
3 changed files with 62 additions and 1 deletions

View File

@ -41,6 +41,10 @@ var initializers []func()
// Set this to true to enable it // Set this to true to enable it
var EnablePrefixMatching = false var EnablePrefixMatching = false
//EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
//To disable sorting, set it to false.
var EnableCommandSorting = true
//AddTemplateFunc adds a template function that's available to Usage and Help //AddTemplateFunc adds a template function that's available to Usage and Help
//template generation. //template generation.
func AddTemplateFunc(name string, tmplFunc interface{}) { func AddTemplateFunc(name string, tmplFunc interface{}) {

View File

@ -21,6 +21,7 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
@ -103,6 +104,8 @@ type Command struct {
commandsMaxUseLen int commandsMaxUseLen int
commandsMaxCommandPathLen int commandsMaxCommandPathLen int
commandsMaxNameLen int commandsMaxNameLen int
// is commands slice are sorted or not
commandsAreSorted bool
flagErrorBuf *bytes.Buffer flagErrorBuf *bytes.Buffer
@ -721,8 +724,20 @@ func (c *Command) ResetCommands() {
c.helpCommand = nil c.helpCommand = nil
} }
//Commands returns a slice of child commands. // Sorts commands by their names
type commandSorterByName []*Command
func (c commandSorterByName) Len() int { return len(c) }
func (c commandSorterByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c commandSorterByName) Less(i, j int) bool { return c[i].Name() < c[j].Name() }
// Commands returns a sorted slice of child commands.
func (c *Command) Commands() []*Command { func (c *Command) Commands() []*Command {
// do not sort commands if it already sorted or sorting was disabled
if EnableCommandSorting && !c.commandsAreSorted{
sort.Sort(commandSorterByName(c.commands))
c.commandsAreSorted = true
}
return c.commands return c.commands
} }
@ -751,6 +766,7 @@ func (c *Command) AddCommand(cmds ...*Command) {
x.SetGlobalNormalizationFunc(c.globNormFunc) x.SetGlobalNormalizationFunc(c.globNormFunc)
} }
c.commands = append(c.commands, x) c.commands = append(c.commands, x)
c.commandsAreSorted = false
} }
} }

View File

@ -133,3 +133,44 @@ func Test_DisableFlagParsing(t *testing.T) {
t.Errorf("expected: %v, got: %v", as, targs) t.Errorf("expected: %v, got: %v", as, targs)
} }
} }
func TestCommandsAreSorted(t *testing.T) {
EnableCommandSorting = true
originalNames := []string{"middle", "zlast", "afirst"}
expectedNames := []string{"afirst", "middle", "zlast"}
var tmpCommand = &Command{Use: "tmp"}
for _, name := range(originalNames) {
tmpCommand.AddCommand(&Command{Use: name})
}
for i, c := range(tmpCommand.Commands()) {
if expectedNames[i] != c.Name() {
t.Errorf("expected: %s, got: %s", expectedNames[i], c.Name())
}
}
EnableCommandSorting = true
}
func TestEnableCommandSortingIsDisabled(t *testing.T) {
EnableCommandSorting = false
originalNames := []string{"middle", "zlast", "afirst"}
var tmpCommand = &Command{Use: "tmp"}
for _, name := range(originalNames) {
tmpCommand.AddCommand(&Command{Use: name})
}
for i, c := range(tmpCommand.Commands()) {
if originalNames[i] != c.Name() {
t.Errorf("expected: %s, got: %s", originalNames[i], c.Name())
}
}
EnableCommandSorting = true
}