From fed78b8bab633ee554cfa229e9975ae731d40f5d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 1 May 2016 08:36:17 -0400 Subject: [PATCH] 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 } }