From 3d1ff4de8b95e509f66210ad971818ad2016b836 Mon Sep 17 00:00:00 2001 From: Kit Dallege Date: Thu, 26 Mar 2026 21:14:21 +0100 Subject: [PATCH] Exclude hidden and deprecated commands from usage padding calculation Hidden and deprecated commands are not shown in usage output, but their names were included when calculating the maximum column widths for formatting. This caused unnecessary padding when a hidden or deprecated command had a longer name than any visible command. Skip hidden and deprecated commands in both AddCommand and RemoveCommand when computing commandsMaxUseLen, commandsMaxCommandPathLen, and commandsMaxNameLen. Fixes #1272 --- command.go | 30 ++++++++++++++++++------------ command_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/command.go b/command.go index c05fed45a..cc412da46 100644 --- a/command.go +++ b/command.go @@ -1345,18 +1345,21 @@ func (c *Command) AddCommand(cmds ...*Command) { panic("Command can't be a child of itself") } cmds[i].parent = c - // update max lengths - usageLen := len(x.Use) - if usageLen > c.commandsMaxUseLen { - c.commandsMaxUseLen = usageLen - } - commandPathLen := len(x.CommandPath()) - if commandPathLen > c.commandsMaxCommandPathLen { - c.commandsMaxCommandPathLen = commandPathLen - } - nameLen := len(x.Name()) - if nameLen > c.commandsMaxNameLen { - c.commandsMaxNameLen = nameLen + // update max lengths, skipping hidden and deprecated commands + // since they are not shown in usage output + if !x.Hidden && len(x.Deprecated) == 0 { + usageLen := len(x.Use) + if usageLen > c.commandsMaxUseLen { + c.commandsMaxUseLen = usageLen + } + commandPathLen := len(x.CommandPath()) + if commandPathLen > c.commandsMaxCommandPathLen { + c.commandsMaxCommandPathLen = commandPathLen + } + nameLen := len(x.Name()) + if nameLen > c.commandsMaxNameLen { + c.commandsMaxNameLen = nameLen + } } // If global normalization function exists, update all children if c.globNormFunc != nil { @@ -1416,6 +1419,9 @@ main: c.commandsMaxCommandPathLen = 0 c.commandsMaxNameLen = 0 for _, command := range c.commands { + if command.Hidden || len(command.Deprecated) != 0 { + continue + } usageLen := len(command.Use) if usageLen > c.commandsMaxUseLen { c.commandsMaxUseLen = usageLen diff --git a/command_test.go b/command_test.go index a86e57f0a..c195ebbab 100644 --- a/command_test.go +++ b/command_test.go @@ -2952,3 +2952,27 @@ func TestHelpFuncExecuted(t *testing.T) { checkStringContains(t, output, helpText) } + +func TestHiddenCommandsDoNotInflatePadding(t *testing.T) { + rootCmd := &Command{Use: "root", Run: emptyRun} + childCmd := &Command{Use: "child", Run: emptyRun} + longChildCmd := &Command{Use: "long-name-child-abcdefghijklmnopqrstuvwxyz", Run: emptyRun} + hiddenChildCmd := &Command{Use: longChildCmd.Use + "-hidden", Hidden: true, Run: emptyRun} + deprecatedChildCmd := &Command{Use: longChildCmd.Use + "-deprecated", Deprecated: "deprecated", Run: emptyRun} + + rootCmd.AddCommand(childCmd, longChildCmd, hiddenChildCmd, deprecatedChildCmd) + + expectedUsePad := len(longChildCmd.Use) + expectedPathPad := len(longChildCmd.CommandPath()) + expectedNamePad := len(longChildCmd.Name()) + + if got := childCmd.UsagePadding(); got != expectedUsePad { + t.Errorf("UsagePadding() = %d, want %d", got, expectedUsePad) + } + if got := childCmd.CommandPathPadding(); got != expectedPathPad { + t.Errorf("CommandPathPadding() = %d, want %d", got, expectedPathPad) + } + if got := childCmd.NamePadding(); got != expectedNamePad { + t.Errorf("NamePadding() = %d, want %d", got, expectedNamePad) + } +}