Improve template mechanisms
* Delete Eq, Gt, appendIfNotPresent and trim functions * Add "[flags]" in UseLine * Simplify other functions * Simplify templates Minor performance improvement. Benchmark for command with 4 flags and one child command: benchmark old ns/op new ns/op delta BenchmarkCmdUsageFunc-4 335860 319290 -4.93% benchmark old allocs new allocs delta BenchmarkCmdUsageFunc-4 562 543 -3.38% benchmark old bytes new bytes delta BenchmarkCmdUsageFunc-4 21623 21037 -2.71%
This commit is contained in:
parent
d83a1d7ccd
commit
4d2c4afa04
72
cobra.go
72
cobra.go
@ -19,20 +19,14 @@ package cobra
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var templateFuncs = template.FuncMap{
|
var templateFuncs = template.FuncMap{
|
||||||
"trim": strings.TrimSpace,
|
"trimTrailingWhitespaces": trimTrailingWhitespaces,
|
||||||
"trimRightSpace": trimRightSpace,
|
"rpad": rpad,
|
||||||
"appendIfNotPresent": appendIfNotPresent,
|
|
||||||
"rpad": rpad,
|
|
||||||
"gt": Gt,
|
|
||||||
"eq": Eq,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var initializers []func()
|
var initializers []func()
|
||||||
@ -65,64 +59,10 @@ func OnInitialize(y ...func()) {
|
|||||||
initializers = append(initializers, y...)
|
initializers = append(initializers, y...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans,
|
func trimTrailingWhitespaces(s string) string {
|
||||||
// Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as
|
|
||||||
// ints and then compared.
|
|
||||||
func Gt(a interface{}, b interface{}) bool {
|
|
||||||
var left, right int64
|
|
||||||
av := reflect.ValueOf(a)
|
|
||||||
|
|
||||||
switch av.Kind() {
|
|
||||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
|
||||||
left = int64(av.Len())
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
left = av.Int()
|
|
||||||
case reflect.String:
|
|
||||||
left, _ = strconv.ParseInt(av.String(), 10, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
bv := reflect.ValueOf(b)
|
|
||||||
|
|
||||||
switch bv.Kind() {
|
|
||||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
|
||||||
right = int64(bv.Len())
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
right = bv.Int()
|
|
||||||
case reflect.String:
|
|
||||||
right, _ = strconv.ParseInt(bv.String(), 10, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
return left > right
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic.
|
|
||||||
func Eq(a interface{}, b interface{}) bool {
|
|
||||||
av := reflect.ValueOf(a)
|
|
||||||
bv := reflect.ValueOf(b)
|
|
||||||
|
|
||||||
switch av.Kind() {
|
|
||||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
|
||||||
panic("Eq called on unsupported type")
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return av.Int() == bv.Int()
|
|
||||||
case reflect.String:
|
|
||||||
return av.String() == bv.String()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimRightSpace(s string) string {
|
|
||||||
return strings.TrimRightFunc(s, unicode.IsSpace)
|
return strings.TrimRightFunc(s, unicode.IsSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s.
|
|
||||||
func appendIfNotPresent(s, stringToAppend string) string {
|
|
||||||
if strings.Contains(s, stringToAppend) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return s + " " + stringToAppend
|
|
||||||
}
|
|
||||||
|
|
||||||
// rpad adds padding to the right of a string.
|
// rpad adds padding to the right of a string.
|
||||||
func rpad(s string, padding int) string {
|
func rpad(s string, padding int) string {
|
||||||
template := fmt.Sprintf("%%-%ds", padding)
|
template := fmt.Sprintf("%%-%ds", padding)
|
||||||
@ -131,10 +71,8 @@ func rpad(s string, padding int) string {
|
|||||||
|
|
||||||
// tmpl executes the given template text on data, writing the result to w.
|
// tmpl executes the given template text on data, writing the result to w.
|
||||||
func tmpl(w io.Writer, text string, data interface{}) error {
|
func tmpl(w io.Writer, text string, data interface{}) error {
|
||||||
t := template.New("top")
|
t := template.New("top").Funcs(templateFuncs)
|
||||||
t.Funcs(templateFuncs)
|
return template.Must(t.Parse(text)).Execute(w, data)
|
||||||
template.Must(t.Parse(text))
|
|
||||||
return t.Execute(w, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ld compares two strings and returns the levenshtein distance between them.
|
// ld compares two strings and returns the levenshtein distance between them.
|
||||||
|
53
command.go
53
command.go
@ -330,23 +330,23 @@ func (c *Command) UsageTemplate() string {
|
|||||||
return c.parent.UsageTemplate()
|
return c.parent.UsageTemplate()
|
||||||
}
|
}
|
||||||
return `Usage:{{if .Runnable}}
|
return `Usage:{{if .Runnable}}
|
||||||
{{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
|
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||||
|
|
||||||
Aliases:
|
Aliases:
|
||||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
{{ .Example }}{{end}}{{if .HasAvailableSubCommands}}
|
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
|
|
||||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||||
|
|
||||||
Flags:
|
Flags:
|
||||||
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasAvailableInheritedFlags}}
|
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||||
|
|
||||||
Global Flags:
|
Global Flags:
|
||||||
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
|
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
||||||
|
|
||||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
@ -364,7 +364,7 @@ func (c *Command) HelpTemplate() string {
|
|||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
return c.parent.HelpTemplate()
|
return c.parent.HelpTemplate()
|
||||||
}
|
}
|
||||||
return `{{with or .Long .Short }}{{. | trim}}
|
return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
|
||||||
|
|
||||||
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
||||||
}
|
}
|
||||||
@ -863,34 +863,34 @@ func (c *Command) Print(i ...interface{}) {
|
|||||||
|
|
||||||
// Println is a convenience method to Println to the defined output, fallback to Stderr if not set.
|
// Println is a convenience method to Println to the defined output, fallback to Stderr if not set.
|
||||||
func (c *Command) Println(i ...interface{}) {
|
func (c *Command) Println(i ...interface{}) {
|
||||||
str := fmt.Sprintln(i...)
|
c.Print(fmt.Sprintln(i...))
|
||||||
c.Print(str)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set.
|
// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set.
|
||||||
func (c *Command) Printf(format string, i ...interface{}) {
|
func (c *Command) Printf(format string, i ...interface{}) {
|
||||||
str := fmt.Sprintf(format, i...)
|
c.Print(fmt.Sprintf(format, i...))
|
||||||
c.Print(str)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommandPath returns the full path to this command.
|
// CommandPath returns the full path to this command.
|
||||||
func (c *Command) CommandPath() string {
|
func (c *Command) CommandPath() string {
|
||||||
str := c.Name()
|
if c.HasParent() {
|
||||||
x := c
|
return c.Parent().CommandPath() + " " + c.Name()
|
||||||
for x.HasParent() {
|
|
||||||
str = x.parent.Name() + " " + str
|
|
||||||
x = x.parent
|
|
||||||
}
|
}
|
||||||
return str
|
return c.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseLine puts out the full usage for a given command (including parents).
|
// UseLine puts out the full usage for a given command (including parents).
|
||||||
func (c *Command) UseLine() string {
|
func (c *Command) UseLine() string {
|
||||||
str := ""
|
var useline string
|
||||||
if c.HasParent() {
|
if c.HasParent() {
|
||||||
str = c.parent.CommandPath() + " "
|
useline = c.parent.CommandPath() + " " + c.Use
|
||||||
|
} else {
|
||||||
|
useline = c.Use
|
||||||
}
|
}
|
||||||
return str + c.Use
|
if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") {
|
||||||
|
useline += " [flags]"
|
||||||
|
}
|
||||||
|
return useline
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebugFlags used to determine which flags have been assigned to which commands
|
// DebugFlags used to determine which flags have been assigned to which commands
|
||||||
@ -936,15 +936,14 @@ func (c *Command) DebugFlags() {
|
|||||||
|
|
||||||
// Name returns the command's name: the first word in the use line.
|
// Name returns the command's name: the first word in the use line.
|
||||||
func (c *Command) Name() string {
|
func (c *Command) Name() string {
|
||||||
if c.name != "" {
|
if c.name == "" {
|
||||||
return c.name
|
name := c.Use
|
||||||
|
i := strings.Index(name, " ")
|
||||||
|
if i >= 0 {
|
||||||
|
name = name[:i]
|
||||||
|
}
|
||||||
|
c.name = name
|
||||||
}
|
}
|
||||||
name := c.Use
|
|
||||||
i := strings.Index(name, " ")
|
|
||||||
if i >= 0 {
|
|
||||||
name = name[:i]
|
|
||||||
}
|
|
||||||
c.name = name
|
|
||||||
return c.name
|
return c.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user