Add HideHelpCommand

While `HideHelp` hides both `help` command and `--help` flag, `HideHelpCommand`
only hides `help` command and leave `--help` flag as-is.

The behavior of `HideHelp` is untouched in this commit.

Fix #523
Replace #636

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
Akihiro Suda 2020-03-05 16:17:18 +09:00
parent 1b7e4e00c7
commit 75e7c526bd
4 changed files with 159 additions and 15 deletions

19
app.go
View File

@ -43,8 +43,11 @@ type App struct {
Flags []Flag
// Boolean to enable bash completion commands
EnableBashCompletion bool
// Boolean to hide built-in help command
// Boolean to hide built-in help command and help flag
HideHelp bool
// Boolean to hide built-in help command but keep help flag.
// Ignored if HideHelp is true.
HideHelpCommand bool
// Boolean to hide built-in version flag and the VERSION section of help
HideVersion bool
// categories contains the categorized commands and is populated on app startup
@ -170,7 +173,9 @@ func (a *App) Setup() {
a.Commands = newCommands
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
if !a.HideHelpCommand {
a.appendCommand(helpCommand)
}
if HelpFlag != nil {
a.appendFlag(HelpFlag)
@ -328,19 +333,9 @@ func (a *App) RunAndExitOnError() {
// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
// generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// Setup also handles HideHelp and HideHelpCommand
a.Setup()
// append help to commands
if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.appendCommand(helpCommand)
if HelpFlag != nil {
a.appendFlag(HelpFlag)
}
}
}
var newCmds []*Command
for _, c := range a.Commands {
if c.HelpName == "" {

View File

@ -41,8 +41,11 @@ type Command struct {
Flags []Flag
// Treat all flags as normal arguments if true
SkipFlagParsing bool
// Boolean to hide built-in help command
// Boolean to hide built-in help command and help flag
HideHelp bool
// Boolean to hide built-in help command but keep help flag
// Ignored if HideHelp is true.
HideHelpCommand bool
// Boolean to hide this command from help or completion
Hidden bool
// Boolean to enable short-option handling so user can combine several
@ -236,6 +239,7 @@ func (c *Command) startApp(ctx *Context) error {
app.Commands = c.Subcommands
app.Flags = c.Flags
app.HideHelp = c.HideHelp
app.HideHelpCommand = c.HideHelpCommand
app.Version = ctx.App.Version
app.HideVersion = ctx.App.HideVersion

View File

@ -36,7 +36,7 @@ var VersionFlag Flag = &BoolFlag{
// HelpFlag prints the help for all commands and subcommands.
// Set to nil to disable the flag. The subcommand
// will still be added unless HideHelp is set to true.
// will still be added unless HideHelp or HideHelpCommand is set to true.
var HelpFlag Flag = &BoolFlag{
Name: "help",
Aliases: []string{"h"},

View File

@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"runtime"
"strings"
"testing"
@ -762,3 +763,147 @@ VERSION:
t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String())
}
}
func TestHideHelpCommand(t *testing.T) {
app := &App{
HideHelpCommand: true,
Writer: ioutil.Discard,
}
err := app.Run([]string{"foo", "help"})
if err == nil {
t.Fatalf("expected a non-nil error")
}
if !strings.Contains(err.Error(), "No help topic for 'help'") {
t.Errorf("Run returned unexpected error: %v", err)
}
err = app.Run([]string{"foo", "--help"})
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
}
func TestHideHelpCommand_False(t *testing.T) {
app := &App{
HideHelpCommand: false,
Writer: ioutil.Discard,
}
err := app.Run([]string{"foo", "help"})
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
err = app.Run([]string{"foo", "--help"})
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
}
func TestHideHelpCommand_WithHideHelp(t *testing.T) {
app := &App{
HideHelp: true, // effective (hides both command and flag)
HideHelpCommand: true, // ignored
Writer: ioutil.Discard,
}
err := app.Run([]string{"foo", "help"})
if err == nil {
t.Fatalf("expected a non-nil error")
}
if !strings.Contains(err.Error(), "No help topic for 'help'") {
t.Errorf("Run returned unexpected error: %v", err)
}
err = app.Run([]string{"foo", "--help"})
if err == nil {
t.Fatalf("expected a non-nil error")
}
if !strings.Contains(err.Error(), "flag: help requested") {
t.Errorf("Run returned unexpected error: %v", err)
}
}
func newContextFromStringSlice(ss []string) *Context {
set := flag.NewFlagSet("", flag.ContinueOnError)
_ = set.Parse(ss)
return &Context{flagSet: set}
}
func TestHideHelpCommand_RunAsSubcommand(t *testing.T) {
app := &App{
HideHelpCommand: true,
Writer: ioutil.Discard,
Commands: []*Command{
{
Name: "dummy",
},
},
}
err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "help"}))
if err == nil {
t.Fatalf("expected a non-nil error")
}
if !strings.Contains(err.Error(), "No help topic for 'help'") {
t.Errorf("Run returned unexpected error: %v", err)
}
err = app.RunAsSubcommand(newContextFromStringSlice([]string{"", "--help"}))
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
}
func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) {
app := &App{
HideHelpCommand: false,
Writer: ioutil.Discard,
Commands: []*Command{
{
Name: "dummy",
},
},
}
err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "help"}))
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
err = app.RunAsSubcommand(newContextFromStringSlice([]string{"", "--help"}))
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
}
func TestHideHelpCommand_WithSubcommands(t *testing.T) {
app := &App{
Writer: ioutil.Discard,
Commands: []*Command{
{
Name: "dummy",
Subcommands: []*Command{
{
Name: "dummy2",
},
},
HideHelpCommand: true,
},
},
}
err := app.Run([]string{"foo", "dummy", "help"})
if err == nil {
t.Fatalf("expected a non-nil error")
}
if !strings.Contains(err.Error(), "No help topic for 'help'") {
t.Errorf("Run returned unexpected error: %v", err)
}
err = app.Run([]string{"foo", "dummy", "--help"})
if err != nil {
t.Errorf("Run returned unexpected error: %v", err)
}
}