From ba5d4e28707c765c3ee12f55f0c2bf18512a6931 Mon Sep 17 00:00:00 2001 From: Toni Kangas Date: Thu, 6 Oct 2022 21:41:59 +0300 Subject: [PATCH] Improve accessibility of markdown docs generation result Present available options and commands in tables. Also adds aliases of a command to markdown docs and omits short command description if long description is defined (similarly than in command usage template used for --help output). --- doc/md_docs.go | 79 ++++++++++++++++++++++++++++++++++----------- doc/md_docs_test.go | 6 ++-- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/doc/md_docs.go b/doc/md_docs.go index bab4b496..80b19515 100644 --- a/doc/md_docs.go +++ b/doc/md_docs.go @@ -25,25 +25,60 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) -func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error { +// formatFlags formats options and aliases as list of code elements. E.g., ["--help", "-h"] becomes "`--help`, `-h`". +func formatFlags(input []string) string { + return fmt.Sprintf("`%s`", strings.Join(input, "`, `")) +} + +// formatDescription replaces line breaks in descriptions with
elements to avoid breaking table formatting. +func formatDescription(input string) string { + return strings.Replace(input, "\n", "
", -1) +} + +// sprintOptionsTable renders options in table to make them more accessible. +func sprintOptionsTable(flags *pflag.FlagSet) string { + output := "| Option | Description |\n" + output += "| ------ | ----------- |\n" + + flags.VisitAll(func(flag *pflag.Flag) { + flags := []string{"--" + flag.Name} + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + flags = append(flags, "-"+flag.Shorthand) + } + + description := flag.Usage + if flag.DefValue != "" { + description += fmt.Sprintf("\nDefault: `%s`", flag.DefValue) + } + + row := fmt.Sprintf("| %s | %s |\n", formatFlags(flags), formatDescription(description)) + output += row + }) + + return output +} + +func sprintOptions(cmd *cobra.Command, name string) string { + output := "" + flags := cmd.NonInheritedFlags() - flags.SetOutput(buf) if flags.HasAvailableFlags() { - buf.WriteString("### Options\n\n```\n") - flags.PrintDefaults() - buf.WriteString("```\n\n") + output += "### Options\n\n" + output += sprintOptionsTable(flags) + output += "\n" } parentFlags := cmd.InheritedFlags() - parentFlags.SetOutput(buf) if parentFlags.HasAvailableFlags() { - buf.WriteString("### Options inherited from parent commands\n\n```\n") - parentFlags.PrintDefaults() - buf.WriteString("```\n\n") + output += "### Global options\n\n" + output += sprintOptionsTable(parentFlags) + output += "\n" } - return nil + + return output } // GenMarkdown creates markdown output. @@ -60,32 +95,39 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) name := cmd.CommandPath() buf.WriteString("## " + name + "\n\n") - buf.WriteString(cmd.Short + "\n\n") if len(cmd.Long) > 0 { - buf.WriteString("### Synopsis\n\n") buf.WriteString(cmd.Long + "\n\n") + } else { + buf.WriteString(cmd.Short + "\n\n") } if cmd.Runnable() { buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.UseLine())) } + if len(cmd.Aliases) > 0 { + buf.WriteString("### Aliases\n\n") + buf.WriteString(fmt.Sprintf("%s\n\n", formatFlags(cmd.Aliases))) + } + if len(cmd.Example) > 0 { buf.WriteString("### Examples\n\n") buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example)) } - if err := printOptions(buf, cmd, name); err != nil { - return err - } + buf.WriteString(sprintOptions(cmd, name)) + if hasSeeAlso(cmd) { - buf.WriteString("### SEE ALSO\n\n") + buf.WriteString("### Related commands\n\n") + buf.WriteString("| Command | Description |\n") + buf.WriteString("| ------- | ----------- |\n") + if cmd.HasParent() { parent := cmd.Parent() pname := parent.CommandPath() link := pname + ".md" link = strings.ReplaceAll(link, " ", "_") - buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short)) + buf.WriteString(fmt.Sprintf("| [%s](%s) | %s |\n", pname, linkHandler(link), formatDescription(parent.Short))) cmd.VisitParents(func(c *cobra.Command) { if c.DisableAutoGenTag { cmd.DisableAutoGenTag = c.DisableAutoGenTag @@ -103,10 +145,11 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) cname := name + " " + child.Name() link := cname + ".md" link = strings.ReplaceAll(link, " ", "_") - buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short)) + buf.WriteString(fmt.Sprintf("| [%s](%s) | %s |\n", cname, linkHandler(link), formatDescription(child.Short))) } buf.WriteString("\n") } + if !cmd.DisableAutoGenTag { buf.WriteString("###### Auto generated by spf13/cobra on " + time.Now().Format("2-Jan-2006") + "\n") } diff --git a/doc/md_docs_test.go b/doc/md_docs_test.go index b1632e9c..a51d4aa7 100644 --- a/doc/md_docs_test.go +++ b/doc/md_docs_test.go @@ -39,7 +39,7 @@ func TestGenMdDoc(t *testing.T) { checkStringContains(t, output, rootCmd.Short) checkStringContains(t, output, echoSubCmd.Short) checkStringOmits(t, output, deprecatedCmd.Short) - checkStringContains(t, output, "Options inherited from parent commands") + checkStringContains(t, output, "Global options") } func TestGenMdDocWithNoLongOrSynopsis(t *testing.T) { @@ -52,7 +52,7 @@ func TestGenMdDocWithNoLongOrSynopsis(t *testing.T) { checkStringContains(t, output, dummyCmd.Example) checkStringContains(t, output, dummyCmd.Short) - checkStringContains(t, output, "Options inherited from parent commands") + checkStringContains(t, output, "Global options") checkStringOmits(t, output, "### Synopsis") } @@ -76,7 +76,7 @@ func TestGenMdNoHiddenParents(t *testing.T) { checkStringContains(t, output, rootCmd.Short) checkStringContains(t, output, echoSubCmd.Short) checkStringOmits(t, output, deprecatedCmd.Short) - checkStringOmits(t, output, "Options inherited from parent commands") + checkStringOmits(t, output, "Global options") } func TestGenMdNoTag(t *testing.T) {