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:
		
							
								
								
									
										70
									
								
								cobra.go
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								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,
 | 
					 | 
				
			||||||
	"appendIfNotPresent": appendIfNotPresent,
 | 
					 | 
				
			||||||
	"rpad": rpad,
 | 
						"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.
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										41
									
								
								command.go
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								command.go
									
									
									
									
									
								
							@ -330,8 +330,8 @@ 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}}
 | 
				
			||||||
@ -343,10 +343,10 @@ Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "he
 | 
				
			|||||||
  {{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
 | 
							name := c.Use
 | 
				
			||||||
		i := strings.Index(name, " ")
 | 
							i := strings.Index(name, " ")
 | 
				
			||||||
		if i >= 0 {
 | 
							if i >= 0 {
 | 
				
			||||||
			name = name[:i]
 | 
								name = name[:i]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		c.name = name
 | 
							c.name = name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return c.name
 | 
						return c.name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user