v / vlib / cli / man.v
173 lines · 164 sloc · 3.71 KB · 390efe46a1f46f302ae98c803b8ffbbb333fdb28
Raw
1module cli
2
3import time
4
5fn man_flag() Flag {
6 return Flag{
7 flag: .bool
8 name: 'man'
9 description: 'Print the auto-generated manpage'
10 }
11}
12
13fn man_cmd() Command {
14 return Command{
15 name: 'man'
16 usage: '<subcommand>'
17 description: 'Print the auto-generated manpage'
18 execute: print_manpage_for_command
19 }
20}
21
22// print_manpage_for_command prints the manpage for the command or subcommand in `man_cmd` to stdout.
23pub fn print_manpage_for_command(cmd Command) ! {
24 if cmd.args.len > 0 {
25 for sub_cmd in cmd.commands {
26 if sub_cmd.matches(cmd.args[0]) {
27 target := unsafe { &sub_cmd }
28 print(target.manpage())
29 return
30 }
31 }
32 print('Invalid command: ${cmd.args.join(' ')}')
33 } else if cmd.parent != unsafe { nil } {
34 print(cmd.parent.manpage())
35 }
36}
37
38// manpage returns a `string` containing the mdoc(7) manpage for this `Command`.
39pub fn (cmd &Command) manpage() string {
40 mut mdoc := '.Dd ${time.now().strftime('%B %d, %Y')}\n'
41 mdoc += '.Dt ${cmd.full_name().replace(' ', '-').to_upper()} 1\n'
42 mdoc += '.Os\n.Sh NAME\n.Nm ${cmd.full_name().replace(' ', '-')}\n.Nd ${cmd.description}\n'
43 mdoc += '.Sh SYNOPSIS\n'
44 mdoc += '.Nm ${cmd.root().name}\n'
45 if unsafe { cmd.parent != 0 } {
46 mut parents := []Command{}
47 if !cmd.parent.is_root() {
48 parents.prepend(cmd.parent)
49 for {
50 p := parents[0]
51 if p.parent.is_root() {
52 break
53 } else {
54 parents.prepend(p.parent)
55 }
56 }
57 for c in parents {
58 mdoc += '.Ar ${c.name}\n'
59 }
60 }
61 mdoc += '.Ar ${cmd.name}\n'
62 }
63 for flag in cmd.flags {
64 mdoc += '.Op'
65 if flag.abbrev != '' {
66 mdoc += ' Fl ${flag.abbrev}'
67 } else {
68 if cmd.posix_mode {
69 mdoc += ' Fl -${flag.name}'
70 } else {
71 mdoc += ' Fl ${flag.name}'
72 }
73 }
74 match flag.flag {
75 .int, .float, .int_array, .float_array { mdoc += ' Ar num' }
76 .string, .string_array { mdoc += ' Ar string' }
77 else {}
78 }
79
80 mdoc += '\n'
81 }
82 for i in 0 .. cmd.required_args {
83 mdoc += '.Ar arg${i}\n'
84 }
85 if cmd.commands.len > 0 {
86 mdoc += '.Nm ${cmd.root().name}\n'
87 if unsafe { cmd.parent != 0 } {
88 mut parents := []Command{}
89 if !cmd.parent.is_root() {
90 parents.prepend(cmd.parent)
91 for {
92 p := parents[0]
93 if p.parent.is_root() {
94 break
95 } else {
96 parents.prepend(p.parent)
97 }
98 }
99 for c in parents {
100 mdoc += '.Ar ${c.name}\n'
101 }
102 }
103 mdoc += '.Ar ${cmd.name}\n'
104 }
105 mdoc += '.Ar subcommand\n'
106 }
107
108 mdoc += '.Sh DESCRIPTION\n'
109 if cmd.man_description != '' {
110 mdoc += '${cmd.man_description}\n'
111 } else if cmd.description != '' {
112 mdoc += '${cmd.description}\n'
113 }
114 if cmd.flags.len > 0 {
115 mdoc += '.Pp\nThe options are as follows:\n'
116 mdoc += '.Bl -tag -width indent\n'
117 for flag in cmd.flags {
118 mdoc += '.It'
119 if flag.abbrev != '' {
120 mdoc += ' Fl ${flag.abbrev}'
121 }
122 if cmd.posix_mode {
123 mdoc += ' Fl -${flag.name}'
124 } else {
125 mdoc += ' Fl ${flag.name}'
126 }
127 mdoc += '\n'
128 if flag.description != '' {
129 mdoc += '${flag.description}\n'
130 }
131 }
132 mdoc += '.El\n'
133 }
134 if cmd.commands.len > 0 {
135 mdoc += '.Pp\nThe subcommands are as follows:\n'
136 mdoc += '.Bl -tag -width indent\n'
137 for c in cmd.commands {
138 mdoc += '.It Cm ${c.name}'
139 if c.alias != '' {
140 mdoc += ' Pq Cm ${c.alias}'
141 }
142 mdoc += '\n'
143 if c.description != '' {
144 mdoc += '${c.description}\n'
145 }
146 }
147 mdoc += '.El\n'
148 }
149
150 if cmd.commands.len > 0 {
151 mdoc += '.Sh SEE ALSO\n'
152 mut cmds := []string{}
153 if unsafe { cmd.parent != 0 } {
154 cmds << cmd.parent.full_name().replace(' ', '-')
155 }
156 for c in cmd.commands {
157 cmds << c.full_name().replace(' ', '-')
158 }
159 cmds.sort()
160 mut i := 1
161 for c in cmds {
162 mdoc += '.Xr ${c} 1'
163 if i == cmds.len {
164 mdoc += '\n'
165 } else {
166 mdoc += ' ,\n'
167 }
168 i++
169 }
170 }
171
172 return mdoc
173}
174