Skip to content

Commit 5c7f6c7

Browse files
authored
Merge pull request #44 from serverscom/improve-docs-format
Improve docs format
2 parents ee572eb + 71d9b2a commit 5c7f6c7

2 files changed

Lines changed: 76 additions & 35 deletions

File tree

cmd/root.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,13 @@ func NewRootCmd(version string) *cobra.Command {
3939
cmdContext := &base.CmdContext{}
4040

4141
cmd := &cobra.Command{
42-
Use: "srvctl",
42+
Use: "srvctl [command] [flags]",
4343
Short: "CLI tool for servers.com API",
4444
Long: `A command line interface for managing servers.com resources`,
4545
Version: version,
4646
PersistentPreRunE: base.InitCmdContext(cmdContext),
4747
SilenceUsage: true,
4848
}
49-
5049
// Global flags
5150
base.AddGlobalFlags(cmd)
5251

internal/docgen/generator.go

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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
171168
func (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
213217
func (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
272310
func (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

Comments
 (0)