Merge pull request #586 from minio/extra-info
Add support for custom help templates.
This commit is contained in:
commit
cf33a9befe
6
app.go
6
app.go
@ -85,6 +85,12 @@ type App struct {
|
|||||||
ErrWriter io.Writer
|
ErrWriter io.Writer
|
||||||
// Other custom info
|
// Other custom info
|
||||||
Metadata map[string]interface{}
|
Metadata map[string]interface{}
|
||||||
|
// Carries a function which returns app specific info.
|
||||||
|
ExtraInfo func() map[string]string
|
||||||
|
// CustomAppHelpTemplate the text template for app help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
CustomAppHelpTemplate string
|
||||||
|
|
||||||
didSetup bool
|
didSetup bool
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,11 @@ type Command struct {
|
|||||||
// Full name of command for help, defaults to full command name, including parent commands.
|
// Full name of command for help, defaults to full command name, including parent commands.
|
||||||
HelpName string
|
HelpName string
|
||||||
commandNamePath []string
|
commandNamePath []string
|
||||||
|
|
||||||
|
// CustomHelpTemplate the text template for the command help topic.
|
||||||
|
// cli.go uses text/template to render templates. You can
|
||||||
|
// render custom help text by setting this variable.
|
||||||
|
CustomHelpTemplate string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandsByName []Command
|
type CommandsByName []Command
|
||||||
@ -250,6 +255,7 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
|
|
||||||
// set CommandNotFound
|
// set CommandNotFound
|
||||||
app.CommandNotFound = ctx.App.CommandNotFound
|
app.CommandNotFound = ctx.App.CommandNotFound
|
||||||
|
app.CustomAppHelpTemplate = c.CustomHelpTemplate
|
||||||
|
|
||||||
// set the flags and commands
|
// set the flags and commands
|
||||||
app.Commands = c.Subcommands
|
app.Commands = c.Subcommands
|
||||||
|
52
help.go
52
help.go
@ -112,17 +112,42 @@ var helpSubcommand = Command{
|
|||||||
// Prints help for the App or Command
|
// Prints help for the App or Command
|
||||||
type helpPrinter func(w io.Writer, templ string, data interface{})
|
type helpPrinter func(w io.Writer, templ string, data interface{})
|
||||||
|
|
||||||
|
// Prints help for the App or Command with custom template function.
|
||||||
|
type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{})
|
||||||
|
|
||||||
// HelpPrinter is a function that writes the help output. If not set a default
|
// HelpPrinter is a function that writes the help output. If not set a default
|
||||||
// is used. The function signature is:
|
// is used. The function signature is:
|
||||||
// func(w io.Writer, templ string, data interface{})
|
// func(w io.Writer, templ string, data interface{})
|
||||||
var HelpPrinter helpPrinter = printHelp
|
var HelpPrinter helpPrinter = printHelp
|
||||||
|
|
||||||
|
// HelpPrinterCustom is same as HelpPrinter but
|
||||||
|
// takes a custom function for template function map.
|
||||||
|
var HelpPrinterCustom helpPrinterCustom = printHelpCustom
|
||||||
|
|
||||||
// VersionPrinter prints the version for the App
|
// VersionPrinter prints the version for the App
|
||||||
var VersionPrinter = printVersion
|
var VersionPrinter = printVersion
|
||||||
|
|
||||||
|
// ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code.
|
||||||
|
func ShowAppHelpAndExit(c *Context, exitCode int) {
|
||||||
|
ShowAppHelp(c)
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
// ShowAppHelp is an action that displays the help.
|
// ShowAppHelp is an action that displays the help.
|
||||||
func ShowAppHelp(c *Context) error {
|
func ShowAppHelp(c *Context) (err error) {
|
||||||
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
if c.App.CustomAppHelpTemplate == "" {
|
||||||
|
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
customAppData := func() map[string]interface{} {
|
||||||
|
if c.App.ExtraInfo == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return map[string]interface{}{
|
||||||
|
"ExtraInfo": c.App.ExtraInfo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HelpPrinterCustom(c.App.Writer, c.App.CustomAppHelpTemplate, c.App, customAppData())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +163,12 @@ func DefaultAppComplete(c *Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowCommandHelpAndExit - exits with code after showing help
|
||||||
|
func ShowCommandHelpAndExit(c *Context, command string, code int) {
|
||||||
|
ShowCommandHelp(c, command)
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
// ShowCommandHelp prints help for the given command
|
// ShowCommandHelp prints help for the given command
|
||||||
func ShowCommandHelp(ctx *Context, command string) error {
|
func ShowCommandHelp(ctx *Context, command string) error {
|
||||||
// show the subcommand help for a command with subcommands
|
// show the subcommand help for a command with subcommands
|
||||||
@ -148,7 +179,11 @@ func ShowCommandHelp(ctx *Context, command string) error {
|
|||||||
|
|
||||||
for _, c := range ctx.App.Commands {
|
for _, c := range ctx.App.Commands {
|
||||||
if c.HasName(command) {
|
if c.HasName(command) {
|
||||||
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
if c.CustomHelpTemplate != "" {
|
||||||
|
HelpPrinterCustom(ctx.App.Writer, c.CustomHelpTemplate, c, nil)
|
||||||
|
} else {
|
||||||
|
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,10 +226,15 @@ func ShowCommandCompletions(ctx *Context, command string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printHelp(out io.Writer, templ string, data interface{}) {
|
func printHelpCustom(out io.Writer, templ string, data interface{}, customFunc map[string]interface{}) {
|
||||||
funcMap := template.FuncMap{
|
funcMap := template.FuncMap{
|
||||||
"join": strings.Join,
|
"join": strings.Join,
|
||||||
}
|
}
|
||||||
|
if customFunc != nil {
|
||||||
|
for key, value := range customFunc {
|
||||||
|
funcMap[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0)
|
||||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||||
@ -210,6 +250,10 @@ func printHelp(out io.Writer, templ string, data interface{}) {
|
|||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printHelp(out io.Writer, templ string, data interface{}) {
|
||||||
|
printHelpCustom(out, templ, data, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func checkVersion(c *Context) bool {
|
func checkVersion(c *Context) bool {
|
||||||
found := false
|
found := false
|
||||||
if VersionFlag.GetName() != "" {
|
if VersionFlag.GetName() != "" {
|
||||||
|
120
help_test.go
120
help_test.go
@ -3,6 +3,8 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -256,6 +258,49 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShowCommandHelp_Customtemplate(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []Command{
|
||||||
|
{
|
||||||
|
Name: "frobbly",
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
HelpName: "foo frobbly",
|
||||||
|
CustomHelpTemplate: `NAME:
|
||||||
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.HelpName}} [FLAGS] TARGET [TARGET ...]
|
||||||
|
|
||||||
|
FLAGS:
|
||||||
|
{{range .VisibleFlags}}{{.}}
|
||||||
|
{{end}}
|
||||||
|
EXAMPLES:
|
||||||
|
1. Frobbly runs with this param locally.
|
||||||
|
$ {{.HelpName}} wobbly
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app.Writer = output
|
||||||
|
app.Run([]string{"foo", "help", "frobbly"})
|
||||||
|
|
||||||
|
if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") {
|
||||||
|
t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") {
|
||||||
|
t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "$ foo frobbly wobbly") {
|
||||||
|
t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
||||||
app := &App{
|
app := &App{
|
||||||
Commands: []Command{
|
Commands: []Command{
|
||||||
@ -287,3 +332,78 @@ func TestShowAppHelp_HiddenCommand(t *testing.T) {
|
|||||||
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShowAppHelp_CustomAppTemplate(t *testing.T) {
|
||||||
|
app := &App{
|
||||||
|
Commands: []Command{
|
||||||
|
{
|
||||||
|
Name: "frobbly",
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "secretfrob",
|
||||||
|
Hidden: true,
|
||||||
|
Action: func(ctx *Context) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ExtraInfo: func() map[string]string {
|
||||||
|
platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU())
|
||||||
|
return map[string]string{
|
||||||
|
"PLATFORM": platform,
|
||||||
|
"RUNTIME": goruntime,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CustomAppHelpTemplate: `NAME:
|
||||||
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...]
|
||||||
|
|
||||||
|
COMMANDS:
|
||||||
|
{{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||||
|
{{end}}{{if .VisibleFlags}}
|
||||||
|
GLOBAL FLAGS:
|
||||||
|
{{range .VisibleFlags}}{{.}}
|
||||||
|
{{end}}{{end}}
|
||||||
|
VERSION:
|
||||||
|
2.0.0
|
||||||
|
{{"\n"}}{{range $key, $value := ExtraInfo}}
|
||||||
|
{{$key}}:
|
||||||
|
{{$value}}
|
||||||
|
{{end}}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app.Writer = output
|
||||||
|
app.Run([]string{"app", "--help"})
|
||||||
|
|
||||||
|
if strings.Contains(output.String(), "secretfrob") {
|
||||||
|
t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "frobbly") {
|
||||||
|
t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "PLATFORM:") ||
|
||||||
|
!strings.Contains(output.String(), "OS:") ||
|
||||||
|
!strings.Contains(output.String(), "Arch:") {
|
||||||
|
t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "RUNTIME:") ||
|
||||||
|
!strings.Contains(output.String(), "Version:") ||
|
||||||
|
!strings.Contains(output.String(), "CPUs:") {
|
||||||
|
t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(output.String(), "VERSION:") ||
|
||||||
|
!strings.Contains(output.String(), "2.0.0") {
|
||||||
|
t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user