package docs import ( "fmt" "html/template" "io" "os" "path/filepath" "strings" "github.com/cli/cli/v2/pkg/cmd/root" "github.com/spf13/cobra" "github.com/spf13/pflag" ) func printOptions(w io.Writer, cmd *cobra.Command) error { flags := cmd.NonInheritedFlags() flags.SetOutput(w) if flags.HasAvailableFlags() { fmt.Fprint(w, "### Options\n\n") if err := printFlagsHTML(w, flags); err != nil { return err } fmt.Fprint(w, "\n\n") } parentFlags := cmd.InheritedFlags() parentFlags.SetOutput(w) if hasNonHelpFlags(parentFlags) { fmt.Fprint(w, "### Options inherited from parent commands\n\n") if err := printFlagsHTML(w, parentFlags); err != nil { return err } fmt.Fprint(w, "\n\n") } return nil } func hasNonHelpFlags(fs *pflag.FlagSet) (found bool) { fs.VisitAll(func(f *pflag.Flag) { if !f.Hidden && f.Name != "help" { found = true } }) return } type flagView struct { Name string Varname string Shorthand string Usage string } var flagsTemplate = `
{{ range . }}
{{ if .Shorthand }}-{{.Shorthand}}, {{ end -}} --{{.Name}}{{ if .Varname }} <{{.Varname}}>{{ end }}
{{.Usage}}
{{ end }}
` var tpl = template.Must(template.New("flags").Parse(flagsTemplate)) func printFlagsHTML(w io.Writer, fs *pflag.FlagSet) error { var flags []flagView fs.VisitAll(func(f *pflag.Flag) { if f.Hidden || f.Name == "help" { return } varname, usage := pflag.UnquoteUsage(f) flags = append(flags, flagView{ Name: f.Name, Varname: varname, Shorthand: f.Shorthand, Usage: usage, }) }) return tpl.Execute(w, flags) } // GenMarkdown creates markdown output. func GenMarkdown(cmd *cobra.Command, w io.Writer) error { return GenMarkdownCustom(cmd, w, func(s string) string { return s }) } // GenMarkdownCustom creates custom markdown output. func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error { fmt.Fprint(w, "{% raw %}") fmt.Fprintf(w, "## %s\n\n", cmd.CommandPath()) hasLong := cmd.Long != "" if !hasLong { fmt.Fprintf(w, "%s\n\n", cmd.Short) } if cmd.Runnable() { fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine()) } if hasLong { fmt.Fprintf(w, "%s\n\n", cmd.Long) } for _, g := range root.GroupedCommands(cmd) { fmt.Fprintf(w, "### %s\n\n", g.Title) for _, subcmd := range g.Commands { fmt.Fprintf(w, "* [%s](%s)\n", subcmd.CommandPath(), linkHandler(cmdManualPath(subcmd))) } fmt.Fprint(w, "\n\n") } if err := printOptions(w, cmd); err != nil { return err } fmt.Fprint(w, "{% endraw %}\n") if len(cmd.Example) > 0 { fmt.Fprint(w, "### Examples\n\n{% highlight bash %}{% raw %}\n") fmt.Fprint(w, cmd.Example) fmt.Fprint(w, "{% endraw %}{% endhighlight %}\n\n") } if cmd.HasParent() { p := cmd.Parent() fmt.Fprint(w, "### See also\n\n") fmt.Fprintf(w, "* [%s](%s)\n", p.CommandPath(), linkHandler(cmdManualPath(p))) } return nil } // GenMarkdownTree will generate a markdown page for this command and all // descendants in the directory given. The header may be nil. // This function may not work correctly if your command names have `-` in them. // If you have `cmd` with two subcmds, `sub` and `sub-third`, // and `sub` has a subcommand called `third`, it is undefined which // help output will be in the file `cmd-sub-third.1`. func GenMarkdownTree(cmd *cobra.Command, dir string) error { identity := func(s string) string { return s } emptyStr := func(s string) string { return "" } return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity) } // GenMarkdownTreeCustom is the same as GenMarkdownTree, but // with custom filePrepender and linkHandler. func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHandler func(string) string) error { for _, c := range cmd.Commands() { _, forceGeneration := c.Annotations["markdown:generate"] if c.Hidden && !forceGeneration { continue } if err := GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler); err != nil { return err } } filename := filepath.Join(dir, cmdManualPath(cmd)) f, err := os.Create(filename) if err != nil { return err } defer f.Close() if _, err := io.WriteString(f, filePrepender(filename)); err != nil { return err } if err := GenMarkdownCustom(cmd, f, linkHandler); err != nil { return err } return nil } func cmdManualPath(c *cobra.Command) string { if basenameOverride, found := c.Annotations["markdown:basename"]; found { return basenameOverride + ".md" } return strings.ReplaceAll(c.CommandPath(), " ", "_") + ".md" }