From 206529730a2a132c548e41e7c1ba773adc49c930 Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 1 Jun 2016 20:15:24 +0200 Subject: [PATCH 1/7] Add Synopsis autogenerator Generates synopsis automagically License: MIT Signed-off-by: Jakub Sztandera --- commands/cli/helptext.go | 44 +++++++++++++++++++++++++++ commands/command.go | 7 +++-- test/sharness/t0010-basic-commands.sh | 2 +- test/sharness/t0040-add-and-cat.sh | 18 ++--------- 4 files changed, 52 insertions(+), 19 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index cceb3f90f..8ab06a11d 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -163,6 +163,9 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) if len(fields.Subcommands) == 0 { fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n") } + if len(fields.Synopsis) == 0 { + fields.Synopsis = generateSynopsis(cmd, pathStr) + } // trim the extra newlines (see TrimNewlines doc) fields.TrimNewlines() @@ -206,6 +209,9 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer if len(fields.Subcommands) == 0 { fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n") } + if len(fields.Synopsis) == 0 { + fields.Synopsis = generateSynopsis(cmd, pathStr) + } // trim the extra newlines (see TrimNewlines doc) fields.TrimNewlines() @@ -216,6 +222,44 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer return shortHelpTemplate.Execute(out, fields) } +func generateSynopsis(cmd *cmds.Command, path string) string { + res := path + for _, opt := range cmd.Options { + valopt, ok := cmd.Helptext.SynopsisOptionsValues[opt.Names()[0]] + if !ok { + valopt = opt.Names()[0] + } + sopt := "" + for i, n := range opt.Names() { + pre := "-" + if len(n) > 1 { + pre = "--" + } + if i == 0 { + sopt = fmt.Sprintf("%s%s=<%s>", pre, n, valopt) + } else { + sopt = fmt.Sprintf("%s | %s%s", sopt, pre, n) + } + } + res = fmt.Sprintf("%s [%s]", res, sopt) + } + if len(cmd.Arguments) > 0 { + res = fmt.Sprintf("%s [--]", res) + } + for _, arg := range cmd.Arguments { + sarg := fmt.Sprintf("<%s>", arg.Name) + if arg.Variadic { + sarg = sarg + "..." + } + + if !arg.Required { + sarg = fmt.Sprintf("[%s]", sarg) + } + res = fmt.Sprintf("%s %s", res, sarg) + } + return strings.Trim(res, " ") +} + func argumentText(cmd *cmds.Command) []string { lines := make([]string, len(cmd.Arguments)) diff --git a/commands/command.go b/commands/command.go index d0f452ebd..d172f53e7 100644 --- a/commands/command.go +++ b/commands/command.go @@ -36,9 +36,9 @@ type MarshalerMap map[EncodingType]Marshaler // text follows formats similar to man pages, but not exactly the same. type HelpText struct { // required - Tagline string // used in - ShortDescription string // used in DESCRIPTION - Synopsis string // showcasing the cmd + Tagline string // used in + ShortDescription string // used in DESCRIPTION + SynopsisOptionsValues map[string]string // mappings for synopsis generator // optional - whole section overrides Usage string // overrides USAGE section @@ -46,6 +46,7 @@ type HelpText struct { Options string // overrides OPTIONS section Arguments string // overrides ARGUMENTS section Subcommands string // overrides SUBCOMMANDS section + Synopsis string // overrides SYNOPSIS field } // Command is a runnable command, with input arguments and options (flags). diff --git a/test/sharness/t0010-basic-commands.sh b/test/sharness/t0010-basic-commands.sh index 69cccef6d..4b05c99b6 100755 --- a/test/sharness/t0010-basic-commands.sh +++ b/test/sharness/t0010-basic-commands.sh @@ -35,7 +35,7 @@ test_expect_success "ipfs help succeeds" ' test_expect_success "ipfs help output looks good" ' egrep -i "^Usage" help.txt >/dev/null && - egrep "ipfs .* " help.txt >/dev/null || + egrep "ipfs " help.txt >/dev/null || test_fsh cat help.txt ' diff --git a/test/sharness/t0040-add-and-cat.sh b/test/sharness/t0040-add-and-cat.sh index 1f972a00c..b7e28d4ec 100755 --- a/test/sharness/t0040-add-and-cat.sh +++ b/test/sharness/t0040-add-and-cat.sh @@ -8,19 +8,6 @@ test_description="Test add and cat commands" . lib/test-lib.sh -client_err_add() { - printf "$@\n\n" - echo 'USAGE - ipfs add ... - Add a file to ipfs. - - Adds contents of to ipfs. Use -r to add directories. - Note that directories are added recursively, to form the ipfs - MerkleDAG. - -Use '"'"'ipfs add --help'"'"' for more information about this command. -' -} - test_add_cat_file() { test_expect_success "ipfs add succeeds" ' echo "Hello Worlds!" >mountdir/hello.txt && @@ -176,9 +163,10 @@ test_add_named_pipe() { test_expect_success "useful error message when adding a named pipe" ' mkfifo named-pipe && test_expect_code 1 ipfs add named-pipe 2>actual && - client_err_add "Error: Unrecognized file type for named-pipe: $(generic_stat named-pipe)" >expected && rm named-pipe && - test_cmp expected actual + grep "Error: Unrecognized file type for named-pipe: $(generic_stat named-pipe)" actual && + grep USAGE actual && + grep "ipfs add" actual ' test_expect_success "useful error message when recursively adding a named pipe" ' From 6cafb46584cc88319ab422baaa5ad68fa27ca10a Mon Sep 17 00:00:00 2001 From: Jakub Sztandera Date: Wed, 1 Jun 2016 21:39:40 +0200 Subject: [PATCH 2/7] Add Synopsis generator test License: MIT Signed-off-by: Jakub Sztandera --- commands/cli/helptext_test.go | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 commands/cli/helptext_test.go diff --git a/commands/cli/helptext_test.go b/commands/cli/helptext_test.go new file mode 100644 index 000000000..aa8361f99 --- /dev/null +++ b/commands/cli/helptext_test.go @@ -0,0 +1,45 @@ +package cli + +import ( + "strings" + "testing" + + cmds "github.com/ipfs/go-ipfs/commands" +) + +func TestSynopsisGenerator(t *testing.T) { + command := &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.StringArg("required", true, false, ""), + cmds.StringArg("variadic", false, true, ""), + }, + Options: []cmds.Option{ + cmds.StringOption("opt", "o", "Option"), + }, + Helptext: cmds.HelpText{ + SynopsisOptionsValues: map[string]string{ + "opt": "OPTION", + }, + }, + } + syn := generateSynopsis(command, "cmd") + t.Logf("Synopsis is: %s", syn) + if !strings.HasPrefix(syn, "cmd ") { + t.Fatal("Synopsis should start with command name") + } + if !strings.Contains(syn, "[--opt=