67fc4837d2
new variable MousetrapDisplayDuration allows to modify the default display duration of 5s, or to completely disable the timeout and wait for the user to press the return key.
208 lines
6.2 KiB
Go
208 lines
6.2 KiB
Go
// Copyright © 2013 Steve Francia <spf@spf13.com>.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Commands similar to git, go tools and other modern CLI tools
|
|
// inspired by go, go-Commander, gh and subcommand
|
|
|
|
package cobra
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
"unicode"
|
|
)
|
|
|
|
var templateFuncs = template.FuncMap{
|
|
"trim": strings.TrimSpace,
|
|
"trimRightSpace": trimRightSpace,
|
|
"trimTrailingWhitespaces": trimRightSpace,
|
|
"appendIfNotPresent": appendIfNotPresent,
|
|
"rpad": rpad,
|
|
"gt": Gt,
|
|
"eq": Eq,
|
|
}
|
|
|
|
var initializers []func()
|
|
|
|
// EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing
|
|
// to automatically enable in CLI tools.
|
|
// Set this to true to enable it.
|
|
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
|
|
|
|
// MousetrapHelpText enables an information splash screen on Windows
|
|
// if the CLI is started from explorer.exe.
|
|
// To disable the mousetrap, just set this variable to blank string ("").
|
|
// Works only on Microsoft Windows.
|
|
var MousetrapHelpText string = `This is a command line tool.
|
|
|
|
You need to open cmd.exe and run it from there.
|
|
`
|
|
|
|
// MousetrapDisplayDuration controls how long the MousetrapHelpText message is displayed on Windows
|
|
// if the CLI is started from explorer.exe. Set to 0 to wait for the return key to be pressed.
|
|
// To disable the mousetrap, just set MousetrapHelpText to blank string ("").
|
|
// Works only on Microsoft Windows.
|
|
var MousetrapDisplayDuration time.Duration = 5 * time.Second
|
|
|
|
// AddTemplateFunc adds a template function that's available to Usage and Help
|
|
// template generation.
|
|
func AddTemplateFunc(name string, tmplFunc interface{}) {
|
|
templateFuncs[name] = tmplFunc
|
|
}
|
|
|
|
// AddTemplateFuncs adds multiple template functions that are available to Usage and
|
|
// Help template generation.
|
|
func AddTemplateFuncs(tmplFuncs template.FuncMap) {
|
|
for k, v := range tmplFuncs {
|
|
templateFuncs[k] = v
|
|
}
|
|
}
|
|
|
|
// OnInitialize sets the passed functions to be run when each command's
|
|
// Execute method is called.
|
|
func OnInitialize(y ...func()) {
|
|
initializers = append(initializers, y...)
|
|
}
|
|
|
|
// FIXME Gt is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra.
|
|
|
|
// 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
|
|
}
|
|
|
|
// FIXME Eq is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra.
|
|
|
|
// 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)
|
|
}
|
|
|
|
// FIXME appendIfNotPresent is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra.
|
|
|
|
// 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)
|
|
return fmt.Sprintf(template, s)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// ld compares two strings and returns the levenshtein distance between them.
|
|
func ld(s, t string, ignoreCase bool) int {
|
|
if ignoreCase {
|
|
s = strings.ToLower(s)
|
|
t = strings.ToLower(t)
|
|
}
|
|
d := make([][]int, len(s)+1)
|
|
for i := range d {
|
|
d[i] = make([]int, len(t)+1)
|
|
}
|
|
for i := range d {
|
|
d[i][0] = i
|
|
}
|
|
for j := range d[0] {
|
|
d[0][j] = j
|
|
}
|
|
for j := 1; j <= len(t); j++ {
|
|
for i := 1; i <= len(s); i++ {
|
|
if s[i-1] == t[j-1] {
|
|
d[i][j] = d[i-1][j-1]
|
|
} else {
|
|
min := d[i-1][j]
|
|
if d[i][j-1] < min {
|
|
min = d[i][j-1]
|
|
}
|
|
if d[i-1][j-1] < min {
|
|
min = d[i-1][j-1]
|
|
}
|
|
d[i][j] = min + 1
|
|
}
|
|
}
|
|
|
|
}
|
|
return d[len(s)][len(t)]
|
|
}
|
|
|
|
func stringInSlice(a string, list []string) bool {
|
|
for _, b := range list {
|
|
if b == a {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|