@@ -5,8 +5,10 @@ import (
55 "fmt"
66 "os"
77 "path/filepath"
8+ "regexp"
89 "strings"
910 "text/template"
11+ "time"
1012
1113 "github.com/cpuguy83/go-md2man/v2/md2man"
1214 "github.com/spf13/cobra"
@@ -27,31 +29,26 @@ type ExtraContent struct {
2729 Examples string
2830}
2931
30- const docTemplate = `# NAME
31-
32- {{.Name}} - {{.Short}}
32+ const docTemplate = `% "{{.NameUpper}}" "1" "{{.Date}}" "" ""
33+ # NAME
34+ ** {{.Name}}** - {{.Short}}
3335
3436# SYNOPSIS
35-
3637{{.UseLine}}
3738
3839# DESCRIPTION
39-
4040{{.Description}}
4141
4242# OPTIONS
43-
4443{{.Options}}
4544
4645{{if .SubCommands}}
4746# SUB COMMANDS
48-
4947{{.SubCommands}}
5048{{end}}
5149
5250{{if .Examples}}
5351# EXAMPLES
54-
5552{{.Examples}}
5653{{end}}
5754`
@@ -170,15 +167,14 @@ func (g *Generator) readExtraContent(commandPath string) (*ExtraContent, error)
170167// generateMarkdown generates markdown documentation for a command
171168func (g * Generator ) generateMarkdown (cmd * cobra.Command , extra * ExtraContent , commandPath string ) (string , error ) {
172169 // Build description section
173- description := strings .TrimSpace (cmd .Long )
174- if description == "" {
175- description = cmd .Short
176- }
170+ var description string
177171 if extra .Description != "" {
178- if description != "" {
179- description += "\n \n "
172+ description = strings .TrimSpace (extra .Description )
173+ } else {
174+ description = strings .TrimSpace (cmd .Long )
175+ if description == "" {
176+ description = cmd .Short
180177 }
181- description += strings .TrimSpace (extra .Description )
182178 }
183179
184180 // Build options section
@@ -190,11 +186,19 @@ func (g *Generator) generateMarkdown(cmd *cobra.Command, extra *ExtraContent, co
190186 // Build examples section
191187 examples := strings .TrimSpace (extra .Examples )
192188
189+ now := time .Now ()
190+ curDate := now .Format ("January 2006" )
191+
192+ // Format UseLine with command in bold
193+ useLine := g .formatUseLine (cmd .UseLine ())
194+
193195 // Prepare data for template
194196 data := map [string ]string {
195197 "Name" : commandPath ,
198+ "NameUpper" : strings .ToUpper (commandPath ),
199+ "Date" : curDate ,
196200 "Short" : cmd .Short ,
197- "UseLine" : cmd . UseLine () ,
201+ "UseLine" : useLine ,
198202 "Description" : description ,
199203 "Options" : options ,
200204 "SubCommands" : subCommands ,
@@ -213,15 +217,16 @@ func (g *Generator) generateMarkdown(cmd *cobra.Command, extra *ExtraContent, co
213217func (g * Generator ) buildOptionsSection (cmd * cobra.Command ) string {
214218 var buf bytes.Buffer
215219
216- // Local flags
217- if cmd .LocalFlags ().HasFlags () {
218- buf .WriteString ("## Local Flags\n \n " )
220+ // Local flags (defined on this command)
221+ if cmd .LocalFlags ().HasAvailableFlags () {
219222 g .formatFlags (& buf , cmd .LocalFlags ())
220223 }
221224
222- // Inherited flags (global)
223- if cmd .InheritedFlags ().HasFlags () {
224- buf .WriteString ("## Global Flags\n \n " )
225+ // Inherited flags (from parent commands)
226+ if cmd .InheritedFlags ().HasAvailableFlags () {
227+ if buf .Len () > 0 {
228+ buf .WriteString ("\n " )
229+ }
225230 g .formatFlags (& buf , cmd .InheritedFlags ())
226231 }
227232
@@ -234,20 +239,31 @@ func (g *Generator) formatFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
234239 if flag .Hidden {
235240 return
236241 }
237- fmt .Fprintf (buf , "**--%s**" , flag .Name )
238- if flag .Shorthand != "" {
239- fmt .Fprintf (buf , ", **-%s**" , flag .Shorthand )
240- }
242+ var flagTypeLong , flagTypeShort string
241243 if flag .Value .Type () != "bool" {
242- fmt .Fprintf (buf , " *%s*" , flag .Value .Type ())
244+ typeName := flag .Value .Type ()
245+ // Simplify stringToX types
246+ if strings .HasPrefix (typeName , "stringTo" ) {
247+ typeName = "string"
248+ }
249+ flagTypeLong = fmt .Sprintf ("`=[<%s>]`" , typeName )
250+ flagTypeShort = fmt .Sprintf ("`[<%s>]`" , typeName )
243251 }
244- buf .WriteString ("\n \n " )
252+
253+ // Write flag name and type
254+ fmt .Fprintf (buf , "**--%s**%s" , flag .Name , flagTypeLong )
255+ if flag .Shorthand != "" {
256+ fmt .Fprintf (buf , ", **-%s**%s" , flag .Shorthand , flagTypeShort )
257+ }
258+ buf .WriteString ("\n " )
259+
245260 if flag .Usage != "" {
246- fmt .Fprintf (buf , " %s \n \n " , flag .Usage )
261+ fmt .Fprintf (buf , " %s " , flag .Usage )
247262 }
248263 if flag .DefValue != "" && flag .DefValue != "false" && flag .DefValue != "[]" && flag .DefValue != "0" {
249- fmt .Fprintf (buf , " Default: `%s`\n \n " , flag .DefValue )
264+ fmt .Fprintf (buf , ". The default is `%s`. " , flag .DefValue )
250265 }
266+ buf .WriteString ("\n \n " )
251267 })
252268}
253269
@@ -259,20 +275,46 @@ func (g *Generator) buildSubCommandsSection(cmd *cobra.Command) string {
259275 if subCmd .Hidden || subCmd .Deprecated != "" {
260276 continue
261277 }
262- fmt .Fprintf (& buf , "**%s**\n \n " , subCmd .Name ())
278+ fmt .Fprintf (& buf , "**%s**\n " , subCmd .Name ())
263279 if subCmd .Short != "" {
264- fmt .Fprintf (& buf , " %s\n \n " , subCmd .Short )
280+ fmt .Fprintf (& buf , " %s\n \n " , subCmd .Short )
265281 }
266282 }
267283
268284 return strings .TrimSpace (buf .String ())
269285}
270286
287+ // formatUseLine formats the UseLine by bolding only the command part
288+ // Leaves arguments like <arg>, [flags], etc. unformatted
289+ func (g * Generator ) formatUseLine (useLine string ) string {
290+ parts := strings .Fields (useLine )
291+ var result strings.Builder
292+
293+ for i , part := range parts {
294+ if i > 0 {
295+ result .WriteString (" " )
296+ }
297+
298+ // Bold command parts, leave arguments/flags as-is
299+ if strings .HasPrefix (part , "<" ) || strings .HasPrefix (part , "[" ) {
300+ result .WriteString (part )
301+ } else {
302+ result .WriteString ("**" + part + "**" )
303+ }
304+ }
305+
306+ return result .String ()
307+ }
308+
271309// convertToMan converts markdown to man page format
272310func (g * Generator ) convertToMan (markdown string , outputPath string ) error {
273311 manContent := md2man .Render ([]byte (markdown ))
274312
275- if err := os .WriteFile (outputPath , manContent , 0644 ); err != nil {
313+ // Replace empty lines with a single dot (roff separator)
314+ emptyLineRegex := regexp .MustCompile (`\n\n+` )
315+ result := emptyLineRegex .ReplaceAllString (string (manContent ), "\n .\n " )
316+
317+ if err := os .WriteFile (outputPath , []byte (result ), 0644 ); err != nil {
276318 return fmt .Errorf ("failed to write man page: %w" , err )
277319 }
278320
0 commit comments