This commit is contained in:
JulesD 2022-03-16 09:08:20 -07:00 committed by GitHub
commit e1674bc9cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 3 deletions

View file

@ -31,6 +31,34 @@ import (
// FParseErrWhitelist configures Flag parse errors to be ignored // FParseErrWhitelist configures Flag parse errors to be ignored
type FParseErrWhitelist flag.ParseErrorsWhitelist type FParseErrWhitelist flag.ParseErrorsWhitelist
// TerminalColor is a type used for the names of the different
// colors in the terminal
type TerminalColor int
// Colors represents the different colors one can use in the terminal
const (
ColorBlack TerminalColor = iota + 30
ColorRed
ColorGreen
ColorYellow
ColorBlue
ColorMagenta
ColorCyan
ColorLightGray
)
// This sequence starts at 90, so we reset iota
const (
ColorDarkGray TerminalColor = iota + 90
ColorLightRed
ColorLightGreen
ColorLightYellow
ColorLightBlue
ColorLightMagenta
ColorLightCyan
ColorWhite
)
// Command is just that, a command for your application. // Command is just that, a command for your application.
// E.g. 'go run ...' - 'run' is the command. Cobra requires // E.g. 'go run ...' - 'run' is the command. Cobra requires
// you to define the usage and description as part of your command // you to define the usage and description as part of your command
@ -47,6 +75,12 @@ type Command struct {
// Example: add [-F file | -D dir]... [-f format] profile // Example: add [-F file | -D dir]... [-f format] profile
Use string Use string
// DisableColors is a boolean used to disable the coloring in the command line
DisableColors bool
// Color represents the color to use to print the command in the terminal
Color TerminalColor
// Aliases is an array of aliases that can be used instead of the first word in Use. // Aliases is an array of aliases that can be used instead of the first word in Use.
Aliases []string Aliases []string
@ -473,10 +507,18 @@ var minNamePadding = 11
// NamePadding returns padding for the name. // NamePadding returns padding for the name.
func (c *Command) NamePadding() int { func (c *Command) NamePadding() int {
additionalPadding := c.additionalNamePadding()
if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen { if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen {
return minNamePadding return minNamePadding + additionalPadding
} }
return c.parent.commandsMaxNameLen return c.parent.commandsMaxNameLen + additionalPadding
}
func (c *Command) additionalNamePadding() int {
// additionalPadding is used to pad non visible characters
// This happens for example when using colors, where \033[31m isn't seen
// but still is counted towards the padding
return len(c.ColoredName()) - len(c.Name())
} }
// UsageTemplate returns usage template for the command. // UsageTemplate returns usage template for the command.
@ -499,7 +541,7 @@ 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 .ColoredName .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
Flags: Flags:
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
@ -1309,6 +1351,25 @@ func (c *Command) Name() string {
return name return name
} }
// isColoringEnabled will be queried to know whether or not we should enable
// the coloring on a command. This will usually be called on the Root command
// and applied for every command.
func (c *Command) isColoringEnabled() bool {
_, noColorEnv := os.LookupEnv("NO_COLOR")
if c.DisableColors || noColorEnv {
return false
}
return true
}
// ColoredName returns the command's Name in the correct color if specified
func (c *Command) ColoredName() string {
if c.Color != 0 && c.Root().isColoringEnabled() {
return fmt.Sprintf("\033[%dm%s\033[0m", c.Color, c.Name())
}
return c.Name()
}
// HasAlias determines if a given string is an alias of the command. // HasAlias determines if a given string is an alias of the command.
func (c *Command) HasAlias(s string) bool { func (c *Command) HasAlias(s string) bool {
for _, a := range c.Aliases { for _, a := range c.Aliases {

View file

@ -3,6 +3,7 @@ package cobra
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -2058,3 +2059,76 @@ func TestFParseErrWhitelistSiblingCommand(t *testing.T) {
} }
checkStringContains(t, output, "unknown flag: --unknown") checkStringContains(t, output, "unknown flag: --unknown")
} }
func commandIsColoredRed(c *Command) error {
if c.Name() != "cmd" {
return fmt.Errorf("Unexpected name with Colored Command: %s", c.Name())
}
// If a color is specified, the ColoredName and the Name should be different
if c.Name() == c.ColoredName() {
return errors.New("Name and ColoredName should not give the same result")
}
if c.ColoredName() != "\033[31m"+c.Name()+"\033[0m" {
return errors.New("ColoredName should only add color to the name")
}
if c.additionalNamePadding() == 0 {
return errors.New("With a color, the additionalNamePadding should be more than 0")
}
return nil
}
func commandIsNotColored(c *Command) error {
if c.Name() != "cmd" {
return errors.New("Unexpected name with simple Command")
}
// If no color is specified, the ColoredName should equal the Name
if c.Name() != c.ColoredName() {
return errors.New("Name and ColoredName should give the same result")
}
if c.additionalNamePadding() != 0 {
return errors.New("With no color, the additionalNamePadding should be 0")
}
return nil
}
func TestColoredName(t *testing.T) {
c := &Command{
Use: "cmd",
}
err := commandIsNotColored(c)
if err != nil {
t.Error(err)
}
c = &Command{
Use: "cmd",
Color: ColorRed,
}
err = commandIsColoredRed(c)
if err != nil {
t.Error(err)
}
}
func TestColoredNameWithNoColorSetup(t *testing.T) {
c := &Command{
Use: "cmd",
Color: ColorRed,
}
err := commandIsColoredRed(c)
if err != nil {
t.Error(err)
}
os.Setenv("NO_COLOR", "true")
err = commandIsNotColored(c)
if err != nil {
t.Error(err)
}
os.Unsetenv("NO_COLOR")
c.DisableColors = true
err = commandIsNotColored(c)
if err != nil {
t.Error(err)
}
}