From 99431669d05b813fd8ec3d922c0b440cf9c36705 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sun, 15 Mar 2015 03:11:52 -0700 Subject: [PATCH 1/3] New ``Hide`` variable for all Flags This is a way to provide hidden flags for app, command and subcommands For example: --generate-bash-completion global flag shouldn't be printed along with other flags as it might generally confuse people into thinking that 'generate' in-fact would generate a bash completion file for them to be used along with their app. Also in general one would want to hide some flags for their apps. --- flag.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ help.go | 30 +++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/flag.go b/flag.go index 42f6a61..d18df0a 100644 --- a/flag.go +++ b/flag.go @@ -13,6 +13,7 @@ import ( // This flag enables bash-completion for all commands and subcommands var BashCompletionFlag = BoolFlag{ Name: "generate-bash-completion", + Hide: true, } // This flag prints the version for the application @@ -37,6 +38,7 @@ type Flag interface { // Apply Flag settings to the given flag set Apply(*flag.FlagSet) GetName() string + isNotHidden() bool } func flagSet(name string, flags []Flag) *flag.FlagSet { @@ -68,6 +70,7 @@ type GenericFlag struct { Value Generic Usage string EnvVar string + Hide bool } // String returns the string representation of the generic flag to display the @@ -112,6 +115,10 @@ func (f GenericFlag) GetName() string { return f.Name } +func (f GenericFlag) isNotHidden() bool { + return !f.Hide +} + // StringSlice is an opaque type for []string to satisfy flag.Value type StringSlice []string @@ -138,6 +145,7 @@ type StringSliceFlag struct { Value *StringSlice Usage string EnvVar string + Hide bool } // String returns the usage @@ -177,6 +185,10 @@ func (f StringSliceFlag) GetName() string { return f.Name } +func (f StringSliceFlag) isNotHidden() bool { + return !f.Hide +} + // StringSlice is an opaque type for []int to satisfy flag.Value type IntSlice []int @@ -208,6 +220,7 @@ type IntSliceFlag struct { Value *IntSlice Usage string EnvVar string + Hide bool } // String returns the usage @@ -250,12 +263,17 @@ func (f IntSliceFlag) GetName() string { return f.Name } +func (f IntSliceFlag) isNotHidden() bool { + return !f.Hide +} + // BoolFlag is a switch that defaults to false type BoolFlag struct { Name string Usage string EnvVar string Destination *bool + Hide bool } // String returns a readable representation of this value (for usage defaults) @@ -293,6 +311,10 @@ func (f BoolFlag) GetName() string { return f.Name } +func (f BoolFlag) isNotHidden() bool { + return !f.Hide +} + // BoolTFlag this represents a boolean flag that is true by default, but can // still be set to false by --some-flag=false type BoolTFlag struct { @@ -300,6 +322,7 @@ type BoolTFlag struct { Usage string EnvVar string Destination *bool + Hide bool } // String returns a readable representation of this value (for usage defaults) @@ -337,6 +360,10 @@ func (f BoolTFlag) GetName() string { return f.Name } +func (f BoolTFlag) isNotHidden() bool { + return !f.Hide +} + // StringFlag represents a flag that takes as string value type StringFlag struct { Name string @@ -344,6 +371,7 @@ type StringFlag struct { Usage string EnvVar string Destination *string + Hide bool } // String returns the usage @@ -385,6 +413,10 @@ func (f StringFlag) GetName() string { return f.Name } +func (f StringFlag) isNotHidden() bool { + return !f.Hide +} + // IntFlag is a flag that takes an integer // Errors if the value provided cannot be parsed type IntFlag struct { @@ -393,6 +425,7 @@ type IntFlag struct { Usage string EnvVar string Destination *int + Hide bool } // String returns the usage @@ -429,6 +462,10 @@ func (f IntFlag) GetName() string { return f.Name } +func (f IntFlag) isNotHidden() bool { + return !f.Hide +} + // DurationFlag is a flag that takes a duration specified in Go's duration // format: https://golang.org/pkg/time/#ParseDuration type DurationFlag struct { @@ -437,6 +474,7 @@ type DurationFlag struct { Usage string EnvVar string Destination *time.Duration + Hide bool } // String returns a readable representation of this value (for usage defaults) @@ -473,6 +511,10 @@ func (f DurationFlag) GetName() string { return f.Name } +func (f DurationFlag) isNotHidden() bool { + return !f.Hide +} + // Float64Flag is a flag that takes an float value // Errors if the value provided cannot be parsed type Float64Flag struct { @@ -481,6 +523,7 @@ type Float64Flag struct { Usage string EnvVar string Destination *float64 + Hide bool } // String returns the usage @@ -516,6 +559,10 @@ func (f Float64Flag) GetName() string { return f.Name } +func (f Float64Flag) isNotHidden() bool { + return !f.Hide +} + func prefixFor(name string) (prefix string) { if len(name) == 1 { prefix = "-" diff --git a/help.go b/help.go index a895e6c..507fbe5 100644 --- a/help.go +++ b/help.go @@ -114,7 +114,15 @@ var HelpPrinter helpPrinter = printHelp var VersionPrinter = printVersion func ShowAppHelp(c *Context) { - HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) + // Make a copy of c.App context + app := *c.App + app.Flags = make([]Flag, 0) + for _, flag := range c.App.Flags { + if flag.isNotHidden() { + app.Flags = append(app.Flags, flag) + } + } + HelpPrinter(c.App.Writer, AppHelpTemplate, app) } // Prints the list of subcommands as the default app completion method @@ -130,13 +138,29 @@ func DefaultAppComplete(c *Context) { func ShowCommandHelp(ctx *Context, command string) { // show the subcommand help for a command with subcommands if command == "" { - HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) + // Make a copy of c.App context + app := *c.App + app.Flags = make([]Flag, 0) + for _, flag := range c.App.Flags { + if flag.isNotHidden() { + app.Flags = append(app.Flags, flag) + } + } + HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, app) return } for _, c := range ctx.App.Commands { if c.HasName(command) { - HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) + // Make a copy of command context + c0 := c + c0.Flags = make([]Flag, 0) + for _, flag := range c.Flags { + if flag.isNotHidden() { + c0.Flags = append(c0.Flags, flag) + } + } + HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c0) return } } From fed78b8bab633ee554cfa229e9975ae731d40f5d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2016 08:36:17 -0400 Subject: [PATCH 2/3] Rework of hidden flag impl plus some Action func corrections --- README.md | 16 ++++++--- altsrc/yaml_command_test.go | 12 ++++--- app.go | 5 +++ cli.go | 2 +- command.go | 5 +++ flag.go | 66 +++++++++++-------------------------- help.go | 48 +++++++-------------------- 7 files changed, 62 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 777b0a8..5db1b6f 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ Here is a more complete sample of a command using YAML support: Description: "testing", Action: func(c *cli.Context) error { // Action to run + return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), @@ -322,16 +323,18 @@ app.Commands = []cli.Command{ Name: "add", Aliases: []string{"a"}, Usage: "add a task to the list", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { fmt.Println("added task: ", c.Args().First()) + return nil }, }, { Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { fmt.Println("completed task: ", c.Args().First()) + return nil }, }, { @@ -342,15 +345,17 @@ app.Commands = []cli.Command{ { Name: "add", Usage: "add a new template", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { fmt.Println("new task template: ", c.Args().First()) + return nil }, }, { Name: "remove", Usage: "remove an existing template", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { fmt.Println("removed task template: ", c.Args().First()) + return nil }, }, }, @@ -450,8 +455,9 @@ app.Commands = []cli.Command{ Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { fmt.Println("completed task: ", c.Args().First()) + return nil }, BashComplete: func(c *cli.Context) { // This will complete if no args are passed diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 90d7823..519bd81 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -97,9 +97,10 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 10) + return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), @@ -161,9 +162,10 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 7) + return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test"}), @@ -225,9 +227,10 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 15) + return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), @@ -294,9 +297,10 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", - Action: func(c *cli.Context) { + Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 11) + return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), diff --git a/app.go b/app.go index 6917fbc..2b7c9c6 100644 --- a/app.go +++ b/app.go @@ -366,6 +366,11 @@ func (a *App) Categories() CommandCategories { return a.categories } +// VisibleFlags returns a slice of the Flags with Hidden=false +func (a *App) VisibleFlags() []Flag { + return visibleFlags(a.Flags) +} + func (a *App) hasFlag(flag Flag) bool { for _, f := range a.Flags { if flag == f { diff --git a/cli.go b/cli.go index b742545..f0440c5 100644 --- a/cli.go +++ b/cli.go @@ -10,7 +10,7 @@ // app := cli.NewApp() // app.Name = "greet" // app.Usage = "say a greeting" -// app.Action = func(c *cli.Context) { +// app.Action = func(c *cli.Context) error { // println("Greetings") // } // diff --git a/command.go b/command.go index 7f30932..9ca7e51 100644 --- a/command.go +++ b/command.go @@ -269,3 +269,8 @@ func (c Command) startApp(ctx *Context) error { return app.RunAsSubcommand(ctx) } + +// VisibleFlags returns a slice of the Flags with Hidden=false +func (c Command) VisibleFlags() []Flag { + return visibleFlags(c.Flags) +} diff --git a/flag.go b/flag.go index d18df0a..3c5c1b3 100644 --- a/flag.go +++ b/flag.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "os" + "reflect" "runtime" "strconv" "strings" @@ -12,8 +13,8 @@ import ( // This flag enables bash-completion for all commands and subcommands var BashCompletionFlag = BoolFlag{ - Name: "generate-bash-completion", - Hide: true, + Name: "generate-bash-completion", + Hidden: true, } // This flag prints the version for the application @@ -38,7 +39,6 @@ type Flag interface { // Apply Flag settings to the given flag set Apply(*flag.FlagSet) GetName() string - isNotHidden() bool } func flagSet(name string, flags []Flag) *flag.FlagSet { @@ -70,7 +70,7 @@ type GenericFlag struct { Value Generic Usage string EnvVar string - Hide bool + Hidden bool } // String returns the string representation of the generic flag to display the @@ -115,10 +115,6 @@ func (f GenericFlag) GetName() string { return f.Name } -func (f GenericFlag) isNotHidden() bool { - return !f.Hide -} - // StringSlice is an opaque type for []string to satisfy flag.Value type StringSlice []string @@ -145,7 +141,7 @@ type StringSliceFlag struct { Value *StringSlice Usage string EnvVar string - Hide bool + Hidden bool } // String returns the usage @@ -185,10 +181,6 @@ func (f StringSliceFlag) GetName() string { return f.Name } -func (f StringSliceFlag) isNotHidden() bool { - return !f.Hide -} - // StringSlice is an opaque type for []int to satisfy flag.Value type IntSlice []int @@ -220,7 +212,7 @@ type IntSliceFlag struct { Value *IntSlice Usage string EnvVar string - Hide bool + Hidden bool } // String returns the usage @@ -263,17 +255,13 @@ func (f IntSliceFlag) GetName() string { return f.Name } -func (f IntSliceFlag) isNotHidden() bool { - return !f.Hide -} - // BoolFlag is a switch that defaults to false type BoolFlag struct { Name string Usage string EnvVar string Destination *bool - Hide bool + Hidden bool } // String returns a readable representation of this value (for usage defaults) @@ -311,10 +299,6 @@ func (f BoolFlag) GetName() string { return f.Name } -func (f BoolFlag) isNotHidden() bool { - return !f.Hide -} - // BoolTFlag this represents a boolean flag that is true by default, but can // still be set to false by --some-flag=false type BoolTFlag struct { @@ -322,7 +306,7 @@ type BoolTFlag struct { Usage string EnvVar string Destination *bool - Hide bool + Hidden bool } // String returns a readable representation of this value (for usage defaults) @@ -360,10 +344,6 @@ func (f BoolTFlag) GetName() string { return f.Name } -func (f BoolTFlag) isNotHidden() bool { - return !f.Hide -} - // StringFlag represents a flag that takes as string value type StringFlag struct { Name string @@ -371,7 +351,7 @@ type StringFlag struct { Usage string EnvVar string Destination *string - Hide bool + Hidden bool } // String returns the usage @@ -413,10 +393,6 @@ func (f StringFlag) GetName() string { return f.Name } -func (f StringFlag) isNotHidden() bool { - return !f.Hide -} - // IntFlag is a flag that takes an integer // Errors if the value provided cannot be parsed type IntFlag struct { @@ -425,7 +401,7 @@ type IntFlag struct { Usage string EnvVar string Destination *int - Hide bool + Hidden bool } // String returns the usage @@ -462,10 +438,6 @@ func (f IntFlag) GetName() string { return f.Name } -func (f IntFlag) isNotHidden() bool { - return !f.Hide -} - // DurationFlag is a flag that takes a duration specified in Go's duration // format: https://golang.org/pkg/time/#ParseDuration type DurationFlag struct { @@ -474,7 +446,7 @@ type DurationFlag struct { Usage string EnvVar string Destination *time.Duration - Hide bool + Hidden bool } // String returns a readable representation of this value (for usage defaults) @@ -511,10 +483,6 @@ func (f DurationFlag) GetName() string { return f.Name } -func (f DurationFlag) isNotHidden() bool { - return !f.Hide -} - // Float64Flag is a flag that takes an float value // Errors if the value provided cannot be parsed type Float64Flag struct { @@ -523,7 +491,7 @@ type Float64Flag struct { Usage string EnvVar string Destination *float64 - Hide bool + Hidden bool } // String returns the usage @@ -559,8 +527,14 @@ func (f Float64Flag) GetName() string { return f.Name } -func (f Float64Flag) isNotHidden() bool { - return !f.Hide +func visibleFlags(fl []Flag) []Flag { + visible := []Flag{} + for _, flag := range fl { + if !reflect.ValueOf(flag).FieldByName("Hidden").Bool() { + visible = append(visible, flag) + } + } + return visible } func prefixFor(name string) (prefix string) { diff --git a/help.go b/help.go index 507fbe5..45e8603 100644 --- a/help.go +++ b/help.go @@ -15,7 +15,7 @@ var AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: - {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} + {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} {{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}} @@ -26,9 +26,9 @@ AUTHOR(S): COMMANDS:{{range .Categories}}{{if .Name}} {{.Name}}{{ ":" }}{{end}}{{range .Commands}} {{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}{{end}} -{{end}}{{end}}{{if .Flags}} +{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: - {{range .Flags}}{{.}} + {{range .VisibleFlags}}{{.}} {{end}}{{end}}{{if .Copyright }} COPYRIGHT: {{.Copyright}} @@ -42,16 +42,16 @@ var CommandHelpTemplate = `NAME: {{.HelpName}} - {{.Usage}} USAGE: - {{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}} + {{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: - {{.Description}}{{end}}{{if .Flags}} + {{.Description}}{{end}}{{if .VisibleFlags}} OPTIONS: - {{range .Flags}}{{.}} + {{range .VisibleFlags}}{{.}} {{end}}{{ end }} ` @@ -62,14 +62,14 @@ var SubcommandHelpTemplate = `NAME: {{.HelpName}} - {{.Usage}} USAGE: - {{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} + {{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} COMMANDS:{{range .Categories}}{{if .Name}} {{.Name}}{{ ":" }}{{end}}{{range .Commands}} {{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}{{end}} -{{end}}{{if .Flags}} +{{end}}{{if .VisibleFlags}} OPTIONS: - {{range .Flags}}{{.}} + {{range .VisibleFlags}}{{.}} {{end}}{{end}} ` @@ -114,15 +114,7 @@ var HelpPrinter helpPrinter = printHelp var VersionPrinter = printVersion func ShowAppHelp(c *Context) { - // Make a copy of c.App context - app := *c.App - app.Flags = make([]Flag, 0) - for _, flag := range c.App.Flags { - if flag.isNotHidden() { - app.Flags = append(app.Flags, flag) - } - } - HelpPrinter(c.App.Writer, AppHelpTemplate, app) + HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) } // Prints the list of subcommands as the default app completion method @@ -138,29 +130,13 @@ func DefaultAppComplete(c *Context) { func ShowCommandHelp(ctx *Context, command string) { // show the subcommand help for a command with subcommands if command == "" { - // Make a copy of c.App context - app := *c.App - app.Flags = make([]Flag, 0) - for _, flag := range c.App.Flags { - if flag.isNotHidden() { - app.Flags = append(app.Flags, flag) - } - } - HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, app) + HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) return } for _, c := range ctx.App.Commands { if c.HasName(command) { - // Make a copy of command context - c0 := c - c0.Flags = make([]Flag, 0) - for _, flag := range c.Flags { - if flag.isNotHidden() { - c0.Flags = append(c0.Flags, flag) - } - } - HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c0) + HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) return } } From 95845551505a09fe45cf0681455d3e8313920c78 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2016 08:44:01 -0400 Subject: [PATCH 3/3] Include details of hidden flag impl in CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5760983..7c865e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ **ATTN**: This project uses [semantic versioning](http://semver.org/). ## [Unreleased] +### Added +- `Hidden` field on all flag struct types to omit from generated help text + +### Changed +- `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from +generated help text via the `Hidden` field ## [1.15.0] - 2016-04-30 ### Added