1
2
3
4 package cli
5
6 import (
7 "bytes"
8 "fmt"
9 "io"
10 "sort"
11 "strings"
12 "text/template"
13
14 "github.com/cpuguy83/go-md2man/v2/md2man"
15 )
16
17
18
19 func (a *App) ToMarkdown() (string, error) {
20 var w bytes.Buffer
21 if err := a.writeDocTemplate(&w, 0); err != nil {
22 return "", err
23 }
24 return w.String(), nil
25 }
26
27
28
29 func (a *App) ToManWithSection(sectionNumber int) (string, error) {
30 var w bytes.Buffer
31 if err := a.writeDocTemplate(&w, sectionNumber); err != nil {
32 return "", err
33 }
34 man := md2man.Render(w.Bytes())
35 return string(man), nil
36 }
37
38
39
40 func (a *App) ToMan() (string, error) {
41 man, err := a.ToManWithSection(8)
42 return man, err
43 }
44
45 type cliTemplate struct {
46 App *App
47 SectionNum int
48 Commands []string
49 GlobalArgs []string
50 SynopsisArgs []string
51 }
52
53 func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error {
54 const name = "cli"
55 t, err := template.New(name).Parse(MarkdownDocTemplate)
56 if err != nil {
57 return err
58 }
59 return t.ExecuteTemplate(w, name, &cliTemplate{
60 App: a,
61 SectionNum: sectionNum,
62 Commands: prepareCommands(a.Commands, 0),
63 GlobalArgs: prepareArgsWithValues(a.VisibleFlags()),
64 SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
65 })
66 }
67
68 func prepareCommands(commands []*Command, level int) []string {
69 var coms []string
70 for _, command := range commands {
71 if command.Hidden {
72 continue
73 }
74
75 usageText := prepareUsageText(command)
76
77 usage := prepareUsage(command, usageText)
78
79 prepared := fmt.Sprintf("%s %s\n\n%s%s",
80 strings.Repeat("#", level+2),
81 strings.Join(command.Names(), ", "),
82 usage,
83 usageText,
84 )
85
86 flags := prepareArgsWithValues(command.VisibleFlags())
87 if len(flags) > 0 {
88 prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
89 }
90
91 coms = append(coms, prepared)
92
93
94 if len(command.Subcommands) > 0 {
95 coms = append(
96 coms,
97 prepareCommands(command.Subcommands, level+1)...,
98 )
99 }
100 }
101
102 return coms
103 }
104
105 func prepareArgsWithValues(flags []Flag) []string {
106 return prepareFlags(flags, ", ", "**", "**", `""`, true)
107 }
108
109 func prepareArgsSynopsis(flags []Flag) []string {
110 return prepareFlags(flags, "|", "[", "]", "[value]", false)
111 }
112
113 func prepareFlags(
114 flags []Flag,
115 sep, opener, closer, value string,
116 addDetails bool,
117 ) []string {
118 args := []string{}
119 for _, f := range flags {
120 flag, ok := f.(DocGenerationFlag)
121 if !ok {
122 continue
123 }
124 modifiedArg := opener
125
126 for _, s := range flag.Names() {
127 trimmed := strings.TrimSpace(s)
128 if len(modifiedArg) > len(opener) {
129 modifiedArg += sep
130 }
131 if len(trimmed) > 1 {
132 modifiedArg += fmt.Sprintf("--%s", trimmed)
133 } else {
134 modifiedArg += fmt.Sprintf("-%s", trimmed)
135 }
136 }
137 modifiedArg += closer
138 if flag.TakesValue() {
139 modifiedArg += fmt.Sprintf("=%s", value)
140 }
141
142 if addDetails {
143 modifiedArg += flagDetails(flag)
144 }
145
146 args = append(args, modifiedArg+"\n")
147
148 }
149 sort.Strings(args)
150 return args
151 }
152
153
154 func flagDetails(flag DocGenerationFlag) string {
155 description := flag.GetUsage()
156 if flag.TakesValue() {
157 defaultText := flag.GetDefaultText()
158 if defaultText == "" {
159 defaultText = flag.GetValue()
160 }
161 if defaultText != "" {
162 description += " (default: " + defaultText + ")"
163 }
164 }
165 return ": " + description
166 }
167
168 func prepareUsageText(command *Command) string {
169 if command.UsageText == "" {
170 return ""
171 }
172
173
174 preparedUsageText := strings.Trim(command.UsageText, "\n")
175
176 var usageText string
177 if strings.Contains(preparedUsageText, "\n") {
178
179
180 for _, ln := range strings.Split(preparedUsageText, "\n") {
181 usageText += fmt.Sprintf(" %s\n", ln)
182 }
183 } else {
184
185 usageText = fmt.Sprintf(">%s\n", preparedUsageText)
186 }
187
188 return usageText
189 }
190
191 func prepareUsage(command *Command, usageText string) string {
192 if command.Usage == "" {
193 return ""
194 }
195
196 usage := command.Usage + "\n"
197
198 if usageText != "" {
199 usage += "\n"
200 }
201
202 return usage
203 }
204
View as plain text