mirror of
https://github.com/spf13/cobra
synced 2024-12-28 07:17:06 +00:00
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
2 changed files with 31 additions and 94 deletions
72
cobra.go
72
cobra.go
|
@ -19,20 +19,14 @@ package cobra
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var templateFuncs = template.FuncMap{
|
||||
"trim": strings.TrimSpace,
|
||||
"trimRightSpace": trimRightSpace,
|
||||
"appendIfNotPresent": appendIfNotPresent,
|
||||
"rpad": rpad,
|
||||
"gt": Gt,
|
||||
"eq": Eq,
|
||||
"trimTrailingWhitespaces": trimTrailingWhitespaces,
|
||||
"rpad": rpad,
|
||||
}
|
||||
|
||||
var initializers []func()
|
||||
|
@ -65,64 +59,10 @@ func OnInitialize(y ...func()) {
|
|||
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,
|
||||
// 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 {
|
||||
func trimTrailingWhitespaces(s string) string {
|
||||
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.
|
||||
func rpad(s string, padding int) string {
|
||||
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.
|
||||
func tmpl(w io.Writer, text string, data interface{}) error {
|
||||
t := template.New("top")
|
||||
t.Funcs(templateFuncs)
|
||||
template.Must(t.Parse(text))
|
||||
return t.Execute(w, data)
|
||||
t := template.New("top").Funcs(templateFuncs)
|
||||
return template.Must(t.Parse(text)).Execute(w, data)
|
||||
}
|
||||
|
||||
// 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 `Usage:{{if .Runnable}}
|
||||
{{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
|
||||
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||
|
||||
Aliases:
|
||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||
|
||||
Examples:
|
||||
{{ .Example }}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
|
||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
|
||||
Flags:
|
||||
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
|
||||
Global Flags:
|
||||
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
|
||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
|
||||
|
||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
|
@ -364,7 +364,7 @@ func (c *Command) HelpTemplate() string {
|
|||
if c.HasParent() {
|
||||
return c.parent.HelpTemplate()
|
||||
}
|
||||
return `{{with or .Long .Short }}{{. | trim}}
|
||||
return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}}
|
||||
|
||||
{{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.
|
||||
func (c *Command) Println(i ...interface{}) {
|
||||
str := fmt.Sprintln(i...)
|
||||
c.Print(str)
|
||||
c.Print(fmt.Sprintln(i...))
|
||||
}
|
||||
|
||||
// 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{}) {
|
||||
str := fmt.Sprintf(format, i...)
|
||||
c.Print(str)
|
||||
c.Print(fmt.Sprintf(format, i...))
|
||||
}
|
||||
|
||||
// CommandPath returns the full path to this command.
|
||||
func (c *Command) CommandPath() string {
|
||||
str := c.Name()
|
||||
x := c
|
||||
for x.HasParent() {
|
||||
str = x.parent.Name() + " " + str
|
||||
x = x.parent
|
||||
if c.HasParent() {
|
||||
return c.Parent().CommandPath() + " " + c.Name()
|
||||
}
|
||||
return str
|
||||
return c.Name()
|
||||
}
|
||||
|
||||
// UseLine puts out the full usage for a given command (including parents).
|
||||
func (c *Command) UseLine() string {
|
||||
str := ""
|
||||
var useline string
|
||||
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
|
||||
|
@ -936,15 +936,14 @@ func (c *Command) DebugFlags() {
|
|||
|
||||
// Name returns the command's name: the first word in the use line.
|
||||
func (c *Command) Name() string {
|
||||
if c.name != "" {
|
||||
return c.name
|
||||
if 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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue