11package docs
22
33import (
4- "bytes"
54 "fmt"
5+ "html/template"
66 "io"
77 "os"
88 "path/filepath"
99 "strings"
1010
1111 "github.com/spf13/cobra"
12+ "github.com/spf13/pflag"
1213)
1314
14- func printOptions (buf * bytes. Buffer , cmd * cobra.Command , name string ) error {
15+ func printOptions (w io. Writer , cmd * cobra.Command ) error {
1516 flags := cmd .NonInheritedFlags ()
16- flags .SetOutput (buf )
17+ flags .SetOutput (w )
1718 if flags .HasAvailableFlags () {
18- buf .WriteString ("### Options\n \n ```\n " )
19- flags .PrintDefaults ()
20- buf .WriteString ("```\n \n " )
19+ fmt .Fprint (w , "### Options\n \n " )
20+ if err := printFlagsHTML (w , flags ); err != nil {
21+ return err
22+ }
23+ fmt .Fprint (w , "\n \n " )
2124 }
2225
2326 parentFlags := cmd .InheritedFlags ()
24- parentFlags .SetOutput (buf )
25- if parentFlags .HasAvailableFlags () {
26- buf .WriteString ("### Options inherited from parent commands\n \n ```\n " )
27- parentFlags .PrintDefaults ()
28- buf .WriteString ("```\n \n " )
27+ parentFlags .SetOutput (w )
28+ if hasNonHelpFlags (parentFlags ) {
29+ fmt .Fprint (w , "### Options inherited from parent commands\n \n " )
30+ if err := printFlagsHTML (w , parentFlags ); err != nil {
31+ return err
32+ }
33+ fmt .Fprint (w , "\n \n " )
2934 }
3035 return nil
3136}
3237
38+ func hasNonHelpFlags (fs * pflag.FlagSet ) (found bool ) {
39+ fs .VisitAll (func (f * pflag.Flag ) {
40+ if ! f .Hidden && f .Name != "help" {
41+ found = true
42+ }
43+ })
44+ return
45+ }
46+
47+ type flagView struct {
48+ Name string
49+ Varname string
50+ Shorthand string
51+ Usage string
52+ }
53+
54+ var flagsTemplate = `
55+ <dl class="flags">{{ range . }}
56+ <dt>{{ if .Shorthand }}<code>-{{.Shorthand}}</code>, {{ end -}}
57+ <code>--{{.Name}}{{ if .Varname }} <{{.Varname}}>{{ end }}</code></dt>
58+ <dd>{{.Usage}}</dd>
59+ {{ end }}</dl>
60+ `
61+
62+ var tpl = template .Must (template .New ("flags" ).Parse (flagsTemplate ))
63+
64+ func printFlagsHTML (w io.Writer , fs * pflag.FlagSet ) error {
65+ var flags []flagView
66+ fs .VisitAll (func (f * pflag.Flag ) {
67+ if f .Hidden || f .Name == "help" {
68+ return
69+ }
70+ varname , usage := pflag .UnquoteUsage (f )
71+ flags = append (flags , flagView {
72+ Name : f .Name ,
73+ Varname : varname ,
74+ Shorthand : f .Shorthand ,
75+ Usage : usage ,
76+ })
77+ })
78+ return tpl .Execute (w , flags )
79+ }
80+
3381// GenMarkdown creates markdown output.
3482func GenMarkdown (cmd * cobra.Command , w io.Writer ) error {
3583 return GenMarkdownCustom (cmd , w , func (s string ) string { return s })
3684}
3785
3886// GenMarkdownCustom creates custom markdown output.
3987func GenMarkdownCustom (cmd * cobra.Command , w io.Writer , linkHandler func (string ) string ) error {
40- cmd .InitDefaultHelpCmd ()
41- cmd .InitDefaultHelpFlag ()
88+ fmt .Fprintf (w , "## %s\n \n " , cmd .CommandPath ())
4289
43- buf := new (bytes.Buffer )
44- name := cmd .CommandPath ()
90+ hasLong := cmd .Long != ""
91+ if ! hasLong {
92+ fmt .Fprintf (w , "%s\n \n " , cmd .Short )
93+ }
94+ if cmd .Runnable () {
95+ fmt .Fprintf (w , "```\n %s\n ```\n \n " , cmd .UseLine ())
96+ }
97+ if hasLong {
98+ fmt .Fprintf (w , "%s\n \n " , cmd .Long )
99+ }
45100
46- buf .WriteString ("## " + name + "\n \n " )
47- buf .WriteString (cmd .Short + "\n \n " )
48- if len (cmd .Long ) > 0 {
49- buf .WriteString ("### Synopsis\n \n " )
50- buf .WriteString (cmd .Long + "\n \n " )
101+ for _ , g := range subcommandGroups (cmd ) {
102+ if len (g .Commands ) == 0 {
103+ continue
104+ }
105+ fmt .Fprintf (w , "### %s\n \n " , g .Name )
106+ for _ , subcmd := range g .Commands {
107+ fmt .Fprintf (w , "* [%s](%s)\n " , subcmd .CommandPath (), linkHandler (cmdManualPath (subcmd )))
108+ }
109+ fmt .Fprint (w , "\n \n " )
51110 }
52111
53- if cmd . Runnable () {
54- buf . WriteString ( fmt . Sprintf ( "``` \n %s \n ``` \n \n " , cmd . UseLine ()))
112+ if err := printOptions ( w , cmd ); err != nil {
113+ return err
55114 }
56115
57116 if len (cmd .Example ) > 0 {
58- buf .WriteString ("### Examples\n \n " )
59- buf .WriteString (fmt .Sprintf ("```\n %s\n ```\n \n " , cmd .Example ))
117+ fmt .Fprint (w , "### Examples\n \n {% highlight bash %}{% raw %}\n " )
118+ fmt .Fprint (w , cmd .Example )
119+ fmt .Fprint (w , "{% endraw %}{% endhighlight %}\n \n " )
60120 }
61121
62- if err := printOptions (buf , cmd , name ); err != nil {
63- return err
122+ if cmd .HasParent () {
123+ p := cmd .Parent ()
124+ fmt .Fprint (w , "### See also\n \n " )
125+ fmt .Fprintf (w , "* [%s](%s)\n " , p .CommandPath (), linkHandler (cmdManualPath (p )))
126+ }
127+
128+ return nil
129+ }
130+
131+ type commandGroup struct {
132+ Name string
133+ Commands []* cobra.Command
134+ }
135+
136+ // subcommandGroups lists child commands of a Cobra command split into groups.
137+ // TODO: have rootHelpFunc use this instead of repeating the same logic.
138+ func subcommandGroups (c * cobra.Command ) []commandGroup {
139+ var rest []* cobra.Command
140+ var core []* cobra.Command
141+ var actions []* cobra.Command
142+
143+ for _ , subcmd := range c .Commands () {
144+ if ! subcmd .IsAvailableCommand () {
145+ continue
146+ }
147+ if _ , ok := subcmd .Annotations ["IsCore" ]; ok {
148+ core = append (core , subcmd )
149+ } else if _ , ok := subcmd .Annotations ["IsActions" ]; ok {
150+ actions = append (actions , subcmd )
151+ } else {
152+ rest = append (rest , subcmd )
153+ }
154+ }
155+
156+ if len (core ) > 0 {
157+ return []commandGroup {
158+ {
159+ Name : "Core commands" ,
160+ Commands : core ,
161+ },
162+ {
163+ Name : "Actions commands" ,
164+ Commands : actions ,
165+ },
166+ {
167+ Name : "Additional commands" ,
168+ Commands : rest ,
169+ },
170+ }
171+ }
172+
173+ return []commandGroup {
174+ {
175+ Name : "Commands" ,
176+ Commands : rest ,
177+ },
64178 }
65- _ , err := buf .WriteTo (w )
66- return err
67179}
68180
69181// GenMarkdownTree will generate a markdown page for this command and all
@@ -92,12 +204,7 @@ func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHa
92204 }
93205 }
94206
95- basename := strings .Replace (cmd .CommandPath (), " " , "_" , - 1 ) + ".md"
96- if basenameOverride , found := cmd .Annotations ["markdown:basename" ]; found {
97- basename = basenameOverride + ".md"
98- }
99-
100- filename := filepath .Join (dir , basename )
207+ filename := filepath .Join (dir , cmdManualPath (cmd ))
101208 f , err := os .Create (filename )
102209 if err != nil {
103210 return err
@@ -112,3 +219,10 @@ func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHa
112219 }
113220 return nil
114221}
222+
223+ func cmdManualPath (c * cobra.Command ) string {
224+ if basenameOverride , found := c .Annotations ["markdown:basename" ]; found {
225+ return basenameOverride + ".md"
226+ }
227+ return strings .ReplaceAll (c .CommandPath (), " " , "_" ) + ".md"
228+ }
0 commit comments