From c64442a48722aa9103d863150b0aeed76a2a0e2b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 20 Mar 2015 13:59:32 -0400 Subject: [PATCH 1/2] fix usage template conditional I wrote https://github.com/eparis/readable-golang-template which converts golang templates into something structured around the conditionals. Obviously you can't just USE the output, but you can SEE the problems. In this case the output shows something like: {{if .HasParent}} {{if and (gt .Commands 0) (gt .Parent.Commands 1) }} Additional help topics: {{if gt .Commands 0 }} {{range .Commands}} {{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}} {{end}} {{end}} {{end}} {{if gt .Parent.Commands 1 }} {{range .Parent.Commands}} {{if .Runnable}} {{if not (eq .Name $cmd.Name) }} {{end}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}} {{end}} {{end}} {{end}} {{end}} {{end}} We have a completely unused "{{if not (eq .Name $cmd.Name) }}" Move the {{end}} after the {{rpad...}} --- command.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command.go b/command.go index a7d9088..048502a 100644 --- a/command.go +++ b/command.go @@ -232,8 +232,8 @@ Available Commands: {{range .Commands}}{{if .Runnable}} {{.LocalFlags.FlagUsages}}{{end}} {{ if .HasInheritedFlags}}Global Flags: {{.InheritedFlags.FlagUsages}}{{end}}{{if .HasParent}}{{if and (gt .Commands 0) (gt .Parent.Commands 1) }} -Additional help topics: {{if gt .Commands 0 }}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if gt .Parent.Commands 1 }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }}{{end}} - {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}} +Additional help topics: {{if gt .Commands 0 }}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if gt .Parent.Commands 1 }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}} {{end}}{{ if .HasSubCommands }} Use "{{.Root.Name}} help [command]" for more information about a command. {{end}}` From fd105488302a4b5ead2361f9613f6281ffd33dbf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 20 Mar 2015 14:47:56 -0400 Subject: [PATCH 2/2] Fix additional help topics template The additional help topics were really hard to ever get to show. The required conditionals were difficult to meet and did not seem to really be logical. Problems I see: 1) the top level command could never have additional topics. 2) you must have at least one sibling command AND one subcommand 3) we had the AND above, but then test both conditionals a second time 4) if the sub command was runnable we wouldn't print anything 5) if the sibling commands were not runnable we wouldn't print anything 4+5) it's possible that we printed "Additional help topics:" with nothing following it 6) We always printed ourselves as a sibling in the additional info Whew, I think I fixed all of those! Again, using https://github.com/eparis/readable-golang-template I'm actually able to visualize the template and see this craziness. The conditionals BEFORE this change: {{if .HasParent}} {{if and (gt .Commands 0) (gt .Parent.Commands 1) }} Additional help topics: {{if gt .Commands 0 }} {{range .Commands}} {{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}} {{end}} {{end}} {{end}} {{if gt .Parent.Commands 1 }} {{range .Parent.Commands}} {{if .Runnable}} {{if not (eq .Name $cmd.Name) }} {{end}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}} {{end}} {{end}} {{end}} {{end}} {{end}} The conditionals AFTER this change: {{if or (.HasHelpSubCommands) (.HasRunnableSiblings)}} Additional help topics: {{if .HasHelpSubCommands}} {{range .Commands}} {{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}} {{end}} {{end}} {{end}} {{if .HasRunnableSiblings }} {{range .Parent.Commands}} {{if .Runnable}} {{if not (eq .Name $cmd.Name) }} {{rpad .CommandPath .CommandPathPadding}} {{.Short}} {{end}} {{end}} {{end}} {{end}} {{end}} --- command.go | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/command.go b/command.go index 048502a..0d76feb 100644 --- a/command.go +++ b/command.go @@ -223,7 +223,7 @@ Aliases: Examples: {{ .Example }} -{{end}}{{ if .HasSubCommands}} +{{end}}{{ if .HasRunnableSubCommands}} Available Commands: {{range .Commands}}{{if .Runnable}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}} @@ -231,9 +231,9 @@ Available Commands: {{range .Commands}}{{if .Runnable}} {{ if .HasLocalFlags}}Flags: {{.LocalFlags.FlagUsages}}{{end}} {{ if .HasInheritedFlags}}Global Flags: -{{.InheritedFlags.FlagUsages}}{{end}}{{if .HasParent}}{{if and (gt .Commands 0) (gt .Parent.Commands 1) }} -Additional help topics: {{if gt .Commands 0 }}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if gt .Parent.Commands 1 }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }} - {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}} +{{.InheritedFlags.FlagUsages}}{{end}}{{if or (.HasHelpSubCommands) (.HasRunnableSiblings)}} +Additional help topics: {{if .HasHelpSubCommands}}{{range .Commands}}{{if not .Runnable}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasRunnableSiblings }}{{range .Parent.Commands}}{{if .Runnable}}{{if not (eq .Name $cmd.Name) }} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{end}} {{end}}{{ if .HasSubCommands }} Use "{{.Root.Name}} help [command]" for more information about a command. {{end}}` @@ -787,6 +787,37 @@ func (c *Command) HasSubCommands() bool { return len(c.commands) > 0 } +func (c *Command) HasRunnableSiblings() bool { + if !c.HasParent() { + return false + } + for _, sub := range c.parent.commands { + if sub.Runnable() { + return true + } + } + return false +} + +func (c *Command) HasHelpSubCommands() bool { + for _, sub := range c.commands { + if !sub.Runnable() { + return true + } + } + return false +} + +// Determine if the command has runnable children commands +func (c *Command) HasRunnableSubCommands() bool { + for _, sub := range c.commands { + if sub.Runnable() { + return true + } + } + return false +} + // Determine if the command is a child command func (c *Command) HasParent() bool { return c.parent != nil