Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 60 additions & 3 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ type Command struct {
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
namePadding int
usagePadding int
commandPathPadding int

// TraverseChildren parses flags on all parents before executing child command.
TraverseChildren bool
Expand Down Expand Up @@ -377,6 +380,24 @@ func (c *Command) SetErrPrefix(s string) {
c.errPrefix = s
}

// SetNamePadding sets the padding width used when the command lists its subcommands.
// A padding value of zero clears the override and reverts to the automatic calculation.
func (c *Command) SetNamePadding(padding int) {
c.namePadding = padding
}

// SetUsagePadding sets the padding width used for aligning the usage column.
// A padding value of zero clears the override and reverts to the automatic calculation.
func (c *Command) SetUsagePadding(padding int) {
c.usagePadding = padding
}

// SetCommandPathPadding sets the padding width used for aligning command paths.
// A padding value of zero clears the override and reverts to the automatic calculation.
func (c *Command) SetCommandPathPadding(padding int) {
c.commandPathPadding = padding
}

// SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands.
// The user should not have a cyclic dependency on commands.
func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) {
Expand Down Expand Up @@ -561,29 +582,65 @@ const minUsagePadding = 25

// UsagePadding return padding for the usage.
func (c *Command) UsagePadding() int {
if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen {
if c.parent == nil {
if c.usagePadding > 0 {
return c.usagePadding
}
return minUsagePadding
}

if c.parent.usagePadding > 0 {
return c.parent.usagePadding
}

if minUsagePadding > c.parent.commandsMaxUseLen {
return minUsagePadding
}

return c.parent.commandsMaxUseLen
}

const minCommandPathPadding = 11

// CommandPathPadding return padding for the command path.
func (c *Command) CommandPathPadding() int {
if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen {
if c.parent == nil {
if c.commandPathPadding > 0 {
return c.commandPathPadding
}
return minCommandPathPadding
}

if c.parent.commandPathPadding > 0 {
return c.parent.commandPathPadding
}

if minCommandPathPadding > c.parent.commandsMaxCommandPathLen {
return minCommandPathPadding
}

return c.parent.commandsMaxCommandPathLen
}

const minNamePadding = 11

// NamePadding returns padding for the name.
func (c *Command) NamePadding() int {
if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen {
if c.parent == nil {
if c.namePadding > 0 {
return c.namePadding
}
return minNamePadding
}

if c.parent.namePadding > 0 {
return c.parent.namePadding
}

if minNamePadding > c.parent.commandsMaxNameLen {
return minNamePadding
}

return c.parent.commandsMaxNameLen
}

Expand Down
111 changes: 111 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,117 @@ func TestSetUsageTemplate(t *testing.T) {
}
}

func TestSetNamePadding(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
shortCmd := &Command{Use: "short", Run: emptyRun}
longCmd := &Command{Use: "a-very-long-command", Run: emptyRun}
rootCmd.AddCommand(shortCmd, longCmd)

defaultPadding := shortCmd.NamePadding()
expectedDefault := len(longCmd.Name())
if defaultPadding != expectedDefault {
t.Fatalf("expected default padding %d, got %d", expectedDefault, defaultPadding)
}

rootCmd.SetNamePadding(42)
if got := shortCmd.NamePadding(); got != 42 {
t.Fatalf("expected override padding 42, got %d", got)
}

if got := rootCmd.NamePadding(); got != 42 {
t.Fatalf("expected root NamePadding to honor override, got %d", got)
}

rootCmd.SetNamePadding(0)
if got := shortCmd.NamePadding(); got != expectedDefault {
t.Fatalf("expected clearing override to restore default %d, got %d", expectedDefault, got)
}

nestedParent := &Command{Use: "nested-parent", Run: emptyRun}
child := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(nestedParent)
nestedParent.AddCommand(child)

nestedParent.SetNamePadding(25)
if got := child.NamePadding(); got != 25 {
t.Fatalf("expected nested override 25, got %d", got)
}
}

func TestSetUsagePadding(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
shortCmd := &Command{Use: "short", Run: emptyRun}
longCmd := &Command{Use: "this-command-has-a-long-use-line", Run: emptyRun}
rootCmd.AddCommand(shortCmd, longCmd)

defaultPadding := shortCmd.UsagePadding()
expectedDefault := len(longCmd.Use)
if defaultPadding != expectedDefault {
t.Fatalf("expected default usage padding %d, got %d", expectedDefault, defaultPadding)
}

rootCmd.SetUsagePadding(64)
if got := shortCmd.UsagePadding(); got != 64 {
t.Fatalf("expected override usage padding 64, got %d", got)
}

if got := rootCmd.UsagePadding(); got != 64 {
t.Fatalf("expected root usage padding to honor override, got %d", got)
}

rootCmd.SetUsagePadding(0)
if got := shortCmd.UsagePadding(); got != expectedDefault {
t.Fatalf("expected clearing usage padding override to restore %d, got %d", expectedDefault, got)
}

nestedParent := &Command{Use: "nested-parent", Run: emptyRun}
child := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(nestedParent)
nestedParent.AddCommand(child)

nestedParent.SetUsagePadding(33)
if got := child.UsagePadding(); got != 33 {
t.Fatalf("expected nested usage padding override 33, got %d", got)
}
}

func TestSetCommandPathPadding(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun}
shortCmd := &Command{Use: "short", Run: emptyRun}
longCmd := &Command{Use: "long-child-command", Run: emptyRun}
rootCmd.AddCommand(shortCmd, longCmd)

defaultPadding := shortCmd.CommandPathPadding()
expectedDefault := len(longCmd.CommandPath())
if defaultPadding != expectedDefault {
t.Fatalf("expected default command path padding %d, got %d", expectedDefault, defaultPadding)
}

rootCmd.SetCommandPathPadding(58)
if got := shortCmd.CommandPathPadding(); got != 58 {
t.Fatalf("expected override command path padding 58, got %d", got)
}

if got := rootCmd.CommandPathPadding(); got != 58 {
t.Fatalf("expected root command path padding to honor override, got %d", got)
}

rootCmd.SetCommandPathPadding(0)
if got := shortCmd.CommandPathPadding(); got != expectedDefault {
t.Fatalf("expected clearing command path padding override to restore %d, got %d", expectedDefault, got)
}

nestedParent := &Command{Use: "nested-parent", Run: emptyRun}
child := &Command{Use: "child", Run: emptyRun}
rootCmd.AddCommand(nestedParent)
nestedParent.AddCommand(child)

nestedParent.SetCommandPathPadding(29)
if got := child.CommandPathPadding(); got != 29 {
t.Fatalf("expected nested command path padding override 29, got %d", got)
}
}

func TestVersionFlagExecuted(t *testing.T) {
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}

Expand Down
7 changes: 7 additions & 0 deletions site/content/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,13 +617,20 @@ with the following functions:
cmd.SetHelpCommand(cmd *Command)
cmd.SetHelpFunc(f func(*Command, []string))
cmd.SetHelpTemplate(s string)
cmd.SetNamePadding(padding int)
cmd.SetUsagePadding(padding int)
cmd.SetCommandPathPadding(padding int)
```

The latter two will also apply to any children commands.

Note that templates specified with `SetHelpTemplate` are evaluated using
`text/template` which can increase the size of the compiled executable.

The `SetNamePadding` helper lets you explicitly control the alignment of the command names shown under "Available Commands". Call it on the parent command to override the automatically calculated padding width. Pass `0` to clear your override and return to the default behavior.

Similarly, `SetUsagePadding` and `SetCommandPathPadding` allow you to pin the widths used for the usage column and command path listings (such as those shown under "Additional help topics"). Set these on the parent command to cascade the override to its children, or pass `0` to fall back to Cobra's automatic sizing.

## Usage Message

When the user provides an invalid flag or invalid command, Cobra responds by
Expand Down