Improve cli usage when command name is long (#10981)

This commit is contained in:
STeve (Xin) Huang 2022-03-23 15:08:42 -04:00 committed by GitHub
parent b2c5c8ecb0
commit 3d7de736e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 4 deletions

View file

@ -292,7 +292,55 @@ func InitCLIParser(appName, appHelp string) (app *kingpin.Application) {
app.HelpFlag.NoEnvar()
// set our own help template
return app.UsageTemplate(defaultUsageTemplate)
return app.UsageTemplate(createUsageTemplate())
}
// createUsageTemplate creates an usage template for kingpin applications.
func createUsageTemplate(opts ...func(*usageTemplateOptions)) string {
opt := &usageTemplateOptions{
commandPrintfWidth: defaultCommandPrintfWidth,
}
for _, optFunc := range opts {
optFunc(opt)
}
return fmt.Sprintf(defaultUsageTemplate, opt.commandPrintfWidth)
}
// UpdateAppUsageTemplate updates usage template for kingpin applications by
// pre-parsing the arguments then applying any changes to the usage template if
// necessary.
func UpdateAppUsageTemplate(app *kingpin.Application, args []string) {
// If ParseContext fails, kingpin will not show usage so there is no need
// to update anything here. See app.Parse for more details.
context, err := app.ParseContext(args)
if err != nil {
return
}
app.UsageTemplate(createUsageTemplate(
withCommandPrintfWidth(app, context),
))
}
// withCommandPrintfWidth returns an usage template option that
// updates command printf width if longer than default.
func withCommandPrintfWidth(app *kingpin.Application, context *kingpin.ParseContext) func(*usageTemplateOptions) {
return func(opt *usageTemplateOptions) {
var commands []*kingpin.CmdModel
if context.SelectedCommand != nil {
commands = context.SelectedCommand.Model().FlattenedCommands()
} else {
commands = app.Model().FlattenedCommands()
}
for _, command := range commands {
if !command.Hidden && len(command.FullCommand) > opt.commandPrintfWidth {
opt.commandPrintfWidth = len(command.FullCommand)
}
}
}
}
// SplitIdentifiers splits list of identifiers by commas/spaces/newlines. Helpful when
@ -380,8 +428,19 @@ func needsQuoting(text string) bool {
return false
}
// Usage template with compactly formatted commands.
var defaultUsageTemplate = `{{define "FormatCommand"}}\
// usageTemplateOptions defines options to format the usage template.
type usageTemplateOptions struct {
// commandPrintfWidth is the width of the command name with padding, for
// {{.FullCommand | printf "%%-%ds"}}
commandPrintfWidth int
}
// defaultCommandPrintfWidth is the default command printf width.
const defaultCommandPrintfWidth = 12
// defaultUsageTemplate is a fmt format that defines the usage template with
// compactly formatted commands. Should be only used in createUsageTemplate.
const defaultUsageTemplate = `{{define "FormatCommand"}}\
{{if .FlagSummary}} {{.FlagSummary}}{{end}}\
{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\
{{end}}\
@ -389,7 +448,7 @@ var defaultUsageTemplate = `{{define "FormatCommand"}}\
{{define "FormatCommands"}}\
{{range .FlattenedCommands}}\
{{if not .Hidden}}\
{{.FullCommand | printf "%-12s" }}{{if .Default}} (Default){{end}} {{ .Help }}
{{.FullCommand | printf "%%-%ds"}}{{if .Default}} (Default){{end}} {{ .Help }}
{{end}}\
{{end}}\
{{end}}\

View file

@ -71,6 +71,7 @@ func Run(args []string) error {
app := utils.InitCLIParser("tbot", "tbot: Teleport Machine ID").Interspersed(false)
app.Flag("debug", "Verbose logging to stdout").Short('d').BoolVar(&cf.Debug)
app.Flag("config", "Path to a configuration file. Defaults to `/etc/tbot.yaml` if unspecified.").Short('c').StringVar(&cf.ConfigPath)
app.HelpFlag.Short('h')
versionCmd := app.Command("version", "Print the version")
@ -97,6 +98,7 @@ func Run(args []string) error {
watchCmd := app.Command("watch", "Watch a destination directory for changes.").Hidden()
utils.UpdateAppUsageTemplate(app, args)
command, err := app.Parse(args)
if err != nil {
app.Usage(args)

View file

@ -137,6 +137,7 @@ func Run(commands []CLICommand) {
app.HelpFlag.Short('h')
// parse CLI commands+flags:
utils.UpdateAppUsageTemplate(app, os.Args[1:])
selectedCmd, err := app.Parse(os.Args[1:])
if err != nil {
app.Usage(os.Args[1:])

View file

@ -294,6 +294,7 @@ func Run(options Options) (app *kingpin.Application, executedCommand string, con
dump.Flag("key-file", "Path to a TLS key file for the proxy.").ExistingFileVar(&dumpFlags.KeyFile)
// parse CLI commands+flags:
utils.UpdateAppUsageTemplate(app, options.Args)
command, err := app.Parse(options.Args)
if err != nil {
app.Usage(options.Args)

View file

@ -638,6 +638,7 @@ func Run(args []string, opts ...cliOption) error {
}
// parse CLI commands+flags:
utils.UpdateAppUsageTemplate(app, args)
command, err := app.Parse(args)
if err != nil {
app.Usage(args)