Skip to content

Commit

Permalink
sub-command-help (#72)
Browse files Browse the repository at this point in the history
* Saving

* Saving

* Updated to use help first in subcommands

* Updated tests for more coverage

* More testing updates

* Updated tests

Co-authored-by: jackh <jackh@pop-os.localdomain>
  • Loading branch information
jhughes1153 and jackh authored Aug 25, 2020
1 parent 7061ca3 commit 635daa4
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 26 deletions.
54 changes: 42 additions & 12 deletions argparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
// DisableDescription can be assigned as a command or arguments description to hide it from the Usage output
const DisableDescription = "DISABLEDDESCRIPTIONWILLNOTSHOWUP"

//disable help can be invoked from the parse and then needs to be propogated to subcommands
var disableHelp = false

// Command is a basic type for this package. It represents top level Parser as well as any commands and sub-commands
// Command MUST NOT ever be created manually. Instead one should call NewCommand method of Parser or Command,
// which will setup appropriate fields and call methods that have to be called when creating new command.
Expand Down Expand Up @@ -130,7 +133,11 @@ func (o *Command) NewCommand(name string, description string) *Command {
c.description = description
c.parsed = false
c.parent = o
c.exitOnHelp = o.exitOnHelp
if !disableHelp {
c.help("h", "help")
c.exitOnHelp = true
c.HelpFunc = (*Command).Usage
}

if o.commands == nil {
o.commands = make([]*Command, 0)
Expand All @@ -144,11 +151,19 @@ func (o *Command) NewCommand(name string, description string) *Command {
// DisableHelp removes any help arguments from the commands list of arguments
// This prevents prevents help from being parsed or invoked from the argument list
func (o *Parser) DisableHelp() {
disableHelp = true
for i, arg := range o.args {
if _, ok := arg.result.(*help); ok {
o.args = append(o.args[:i], o.args[i+1:]...)
}
}
for _, com := range o.commands {
for i, comArg := range com.args {
if _, ok := comArg.result.(*help); ok {
com.args = append(com.args[:i], com.args[i+1:]...)
}
}
}
}

// ExitOnHelp sets the exitOnHelp variable of Parser
Expand Down Expand Up @@ -469,15 +484,16 @@ func message2String(msg interface{}) (string, bool) {
// getPrecedingCommands - collects info on command chain from root to current (o *Command) and all arguments in this chain
func (o *Command) getPrecedingCommands(chain *[]string, arguments *[]*arg) {
current := o
// Also add arguments
// Get line of commands until root
for current != nil {
*chain = append(*chain, current.name)
// Also add arguments
if current.args != nil {
*arguments = append(*arguments, current.args...)
}
current = current.parent
}

// Reverse the slice
last := len(*chain) - 1
for i := 0; i < len(*chain)/2; i++ {
Expand All @@ -503,6 +519,7 @@ func (o *Command) getSubCommands(chain *[]string) []Command {

// precedingCommands2Result - puts info about command chain from root to current (o *Command) into result string buffer
func (o *Command) precedingCommands2Result(result string, chain []string, arguments []*arg, maxWidth int) string {
usedHelp := false
leftPadding := len("usage: " + chain[0] + "")
// Add preceding commands
for _, v := range chain {
Expand All @@ -514,7 +531,13 @@ func (o *Command) precedingCommands2Result(result string, chain []string, argume
if v.opts.Help == DisableDescription {
continue
}
result = addToLastLine(result, v.usage(), maxWidth, leftPadding, true)
if v.lname == "help" && usedHelp {
} else {
result = addToLastLine(result, v.usage(), maxWidth, leftPadding, true)
}
if v.lname == "help" || v.sname == "h" {
usedHelp = true
}
}
// Add program/Command description to the result
result = result + "\n\n" + strings.Repeat(" ", leftPadding)
Expand Down Expand Up @@ -556,6 +579,7 @@ func subCommands2Result(result string, commands []Command, maxWidth int) string

// arguments2Result - puts info about all arguments of current command into result string buffer
func arguments2Result(result string, arguments []*arg, maxWidth int) string {
usedHelp := false
if len(arguments) > 0 {
argContent := "Arguments:\n\n"
// Get biggest padding
Expand All @@ -574,18 +598,24 @@ func arguments2Result(result string, arguments []*arg, maxWidth int) string {
if argument.opts.Help == DisableDescription {
continue
}
arg := " "
if argument.sname != "" {
arg = arg + "-" + argument.sname + " "
if argument.lname == "help" && usedHelp {
} else {
arg = arg + " "
arg := " "
if argument.sname != "" {
arg = arg + "-" + argument.sname + " "
} else {
arg = arg + " "
}
arg = arg + "--" + argument.lname
arg = arg + strings.Repeat(" ", argPadding-len(arg))
if argument.opts != nil && argument.opts.Help != "" {
arg = addToLastLine(arg, argument.getHelpMessage(), maxWidth, argPadding, true)
}
argContent = argContent + arg + "\n"
}
arg = arg + "--" + argument.lname
arg = arg + strings.Repeat(" ", argPadding-len(arg))
if argument.opts != nil && argument.opts.Help != "" {
arg = addToLastLine(arg, argument.getHelpMessage(), maxWidth, argPadding, true)
if argument.lname == "help" || argument.sname == "h" {
usedHelp = true
}
argContent = argContent + arg + "\n"
}
result = result + argContent + "\n"
}
Expand Down
48 changes: 39 additions & 9 deletions argparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1782,18 +1782,17 @@ Arguments:
`

var cmd1Usage = `usage: verylongprogname veryverylongcmd1 [-f|--verylongflag1]
-a|--verylongflagA [-h|--help]
[-s|--verylongstring-flag1 "<value>"]
[-i|--integer-flag1 <integer>]
var cmd1Usage = `usage: verylongprogname veryverylongcmd1 [-h|--help] [-f|--verylongflag1]
-a|--verylongflagA [-s|--verylongstring-flag1
"<value>"] [-i|--integer-flag1 <integer>]
cmd1 description
Arguments:
-h --help Print help information
-f --verylongflag1 flag1 description
-a --verylongflagA flag1 description
-h --help Print help information
-s --verylongstring-flag1 string1 description
-i --integer-flag1 integer1 description
Expand Down Expand Up @@ -2482,20 +2481,20 @@ func TestParserHelpFuncDefault(t *testing.T) {
func TestCommandHelpFuncDefault(t *testing.T) {
parser := NewParser("parser", "")
command := parser.NewCommand("command", "")
if command.HelpFunc != nil {
if command.HelpFunc == nil || command.Help(nil) != command.Usage(nil) {
t.Errorf("HelpFunc should default to Usage function")
}
}

func TestCommandHelpFuncDefaultToParent(t *testing.T) {
func TestCommandHelpFuncOwnFunc(t *testing.T) {
parser := NewParser("parser", "")
command := parser.NewCommand("command", "")

parser.HelpFunc = func(c *Command, msg interface{}) string {
return "testing"
}

if command.Help(nil) == command.Usage(nil) || command.Help(nil) != parser.Help(nil) {
if command.Help(nil) != command.Usage(nil) || command.Help(nil) == parser.Help(nil) {
t.Errorf("command HelpFunc should default to parent function")
}
}
Expand Down Expand Up @@ -2548,11 +2547,42 @@ func TestParserDisableHelp(t *testing.T) {
t.Errorf("Parser should not have any arguments")
}

if err := parser.Parse([]string{"parser", "-h"}); err == nil {
t.Errorf("Parsing should fail, help argument shouldn't exist")
}
}

func TestDisableHelpCommands(t *testing.T) {
parser := NewParser("parser", "")
cmd1 := parser.NewCommand("cmd1", "Cmd1 description")
cmd2 := parser.NewCommand("cmd2", "Cmd2 description")
parser.DisableHelp()
if len(cmd1.args) > 0 {
t.Errorf("Sub command cmd1 should not have any arguments")
}
if len(cmd2.args) > 0 {
t.Errorf("Sub Command cmd2 should not have any arguments")
}

if err := parser.Parse([]string{"cmd1", "-h"}); err == nil {
t.Errorf("Parsing should fail, help argument shouldn't exist")
}
}

func TestDisableHelpCommandsBeforeCommand(t *testing.T) {
parser := NewParser("parser", "")
parser.DisableHelp()

cmd1 := parser.NewCommand("cmd1", "Cmd1 description")
if len(cmd1.args) > 0 {
t.Errorf("Parser should not have any arguments")
}

print = func(...interface{}) (int, error) {
return 0, nil
}

if err := parser.Parse([]string{"parser", "-h"}); err == nil {
if err := parser.Parse([]string{"cmd1", "-h"}); err == nil {
t.Errorf("Parsing should fail, help argument shouldn't exist")
}
}
Expand Down
12 changes: 7 additions & 5 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ func (o *Command) addArg(a *arg) error {
for current != nil {
if current.args != nil {
for _, v := range current.args {
if a.sname != "" && a.sname == v.sname {
return fmt.Errorf("short name %s occurs more than once", a.sname)
}
if a.lname == v.lname {
return fmt.Errorf("long name %s occurs more than once", a.lname)
if a.lname != "help" || a.sname != "h" {
if a.sname != "" && a.sname == v.sname {
return fmt.Errorf("short name %s occurs more than once", a.sname)
}
if a.lname == v.lname {
return fmt.Errorf("long name %s occurs more than once", a.lname)
}
}
}
}
Expand Down

0 comments on commit 635daa4

Please sign in to comment.