##// END OF EJS Templates
py3: fix broken man page generation, it was generating `(default: NUL*)`...
Kyle Lippincott -
r45509:1a4b9b60 stable
parent child Browse files
Show More
@@ -1,330 +1,333
1 1 #!/usr/bin/env python
2 2 """usage: %s DOC ...
3 3
4 4 where DOC is the name of a document
5 5 """
6 6
7 7 from __future__ import absolute_import
8 8
9 9 import os
10 10 import sys
11 11 import textwrap
12 12
13 13 try:
14 14 import msvcrt
15 15
16 16 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
17 17 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
18 18 except ImportError:
19 19 pass
20 20
21 21 # This script is executed during installs and may not have C extensions
22 22 # available. Relax C module requirements.
23 23 os.environ['HGMODULEPOLICY'] = 'allow'
24 24 # import from the live mercurial repo
25 25 sys.path.insert(0, "..")
26 26 from mercurial import demandimport
27 27
28 28 demandimport.enable()
29 29
30 30 from mercurial import (
31 31 commands,
32 32 encoding,
33 33 extensions,
34 34 help,
35 35 minirst,
36 36 pycompat,
37 37 ui as uimod,
38 38 )
39 39 from mercurial.i18n import (
40 40 gettext,
41 41 _,
42 42 )
43 from mercurial.utils import stringutil
43 44
44 45 table = commands.table
45 46 globalopts = commands.globalopts
46 47 helptable = help.helptable
47 48 loaddoc = help.loaddoc
48 49
49 50
50 51 def get_desc(docstr):
51 52 if not docstr:
52 53 return b"", b""
53 54 # sanitize
54 55 docstr = docstr.strip(b"\n")
55 56 docstr = docstr.rstrip()
56 57 shortdesc = docstr.splitlines()[0].strip()
57 58
58 59 i = docstr.find(b"\n")
59 60 if i != -1:
60 61 desc = docstr[i + 2 :]
61 62 else:
62 63 desc = shortdesc
63 64
64 65 desc = textwrap.dedent(desc.decode('latin1')).encode('latin1')
65 66
66 67 return (shortdesc, desc)
67 68
68 69
69 70 def get_opts(opts):
70 71 for opt in opts:
71 72 if len(opt) == 5:
72 73 shortopt, longopt, default, desc, optlabel = opt
73 74 else:
74 75 shortopt, longopt, default, desc = opt
75 76 optlabel = _(b"VALUE")
76 77 allopts = []
77 78 if shortopt:
78 79 allopts.append(b"-%s" % shortopt)
79 80 if longopt:
80 81 allopts.append(b"--%s" % longopt)
81 82 if isinstance(default, list):
82 83 allopts[-1] += b" <%s[+]>" % optlabel
83 84 elif (default is not None) and not isinstance(default, bool):
84 85 allopts[-1] += b" <%s>" % optlabel
85 86 if b'\n' in desc:
86 87 # only remove line breaks and indentation
87 88 desc = b' '.join(l.lstrip() for l in desc.split(b'\n'))
88 desc += default and _(b" (default: %s)") % bytes(default) or b""
89 if default:
90 default = stringutil.forcebytestr(default)
91 desc += _(b" (default: %s)") % default
89 92 yield (b", ".join(allopts), desc)
90 93
91 94
92 95 def get_cmd(cmd, cmdtable):
93 96 d = {}
94 97 attr = cmdtable[cmd]
95 98 cmds = cmd.lstrip(b"^").split(b"|")
96 99
97 100 d[b'cmd'] = cmds[0]
98 101 d[b'aliases'] = cmd.split(b"|")[1:]
99 102 d[b'desc'] = get_desc(gettext(pycompat.getdoc(attr[0])))
100 103 d[b'opts'] = list(get_opts(attr[1]))
101 104
102 105 s = b'hg ' + cmds[0]
103 106 if len(attr) > 2:
104 107 if not attr[2].startswith(b'hg'):
105 108 s += b' ' + attr[2]
106 109 else:
107 110 s = attr[2]
108 111 d[b'synopsis'] = s.strip()
109 112
110 113 return d
111 114
112 115
113 116 def showdoc(ui):
114 117 # print options
115 118 ui.write(minirst.section(_(b"Options")))
116 119 multioccur = False
117 120 for optstr, desc in get_opts(globalopts):
118 121 ui.write(b"%s\n %s\n\n" % (optstr, desc))
119 122 if optstr.endswith(b"[+]>"):
120 123 multioccur = True
121 124 if multioccur:
122 125 ui.write(_(b"\n[+] marked option can be specified multiple times\n"))
123 126 ui.write(b"\n")
124 127
125 128 # print cmds
126 129 ui.write(minirst.section(_(b"Commands")))
127 130 commandprinter(ui, table, minirst.subsection, minirst.subsubsection)
128 131
129 132 # print help topics
130 133 # The config help topic is included in the hgrc.5 man page.
131 134 helpprinter(ui, helptable, minirst.section, exclude=[b'config'])
132 135
133 136 ui.write(minirst.section(_(b"Extensions")))
134 137 ui.write(
135 138 _(
136 139 b"This section contains help for extensions that are "
137 140 b"distributed together with Mercurial. Help for other "
138 141 b"extensions is available in the help system."
139 142 )
140 143 )
141 144 ui.write(
142 145 (
143 146 b"\n\n"
144 147 b".. contents::\n"
145 148 b" :class: htmlonly\n"
146 149 b" :local:\n"
147 150 b" :depth: 1\n\n"
148 151 )
149 152 )
150 153
151 154 for extensionname in sorted(allextensionnames()):
152 155 mod = extensions.load(ui, extensionname, None)
153 156 ui.write(minirst.subsection(extensionname))
154 157 ui.write(b"%s\n\n" % gettext(pycompat.getdoc(mod)))
155 158 cmdtable = getattr(mod, 'cmdtable', None)
156 159 if cmdtable:
157 160 ui.write(minirst.subsubsection(_(b'Commands')))
158 161 commandprinter(
159 162 ui,
160 163 cmdtable,
161 164 minirst.subsubsubsection,
162 165 minirst.subsubsubsubsection,
163 166 )
164 167
165 168
166 169 def showtopic(ui, topic):
167 170 extrahelptable = [
168 171 ([b"common"], b'', loaddoc(b'common'), help.TOPIC_CATEGORY_MISC),
169 172 ([b"hg.1"], b'', loaddoc(b'hg.1'), help.TOPIC_CATEGORY_CONFIG),
170 173 ([b"hg-ssh.8"], b'', loaddoc(b'hg-ssh.8'), help.TOPIC_CATEGORY_CONFIG),
171 174 (
172 175 [b"hgignore.5"],
173 176 b'',
174 177 loaddoc(b'hgignore.5'),
175 178 help.TOPIC_CATEGORY_CONFIG,
176 179 ),
177 180 ([b"hgrc.5"], b'', loaddoc(b'hgrc.5'), help.TOPIC_CATEGORY_CONFIG),
178 181 (
179 182 [b"hgignore.5.gendoc"],
180 183 b'',
181 184 loaddoc(b'hgignore'),
182 185 help.TOPIC_CATEGORY_CONFIG,
183 186 ),
184 187 (
185 188 [b"hgrc.5.gendoc"],
186 189 b'',
187 190 loaddoc(b'config'),
188 191 help.TOPIC_CATEGORY_CONFIG,
189 192 ),
190 193 ]
191 194 helpprinter(ui, helptable + extrahelptable, None, include=[topic])
192 195
193 196
194 197 def helpprinter(ui, helptable, sectionfunc, include=[], exclude=[]):
195 198 for h in helptable:
196 199 names, sec, doc = h[0:3]
197 200 if exclude and names[0] in exclude:
198 201 continue
199 202 if include and names[0] not in include:
200 203 continue
201 204 for name in names:
202 205 ui.write(b".. _%s:\n" % name)
203 206 ui.write(b"\n")
204 207 if sectionfunc:
205 208 ui.write(sectionfunc(sec))
206 209 if callable(doc):
207 210 doc = doc(ui)
208 211 ui.write(doc)
209 212 ui.write(b"\n")
210 213
211 214
212 215 def commandprinter(ui, cmdtable, sectionfunc, subsectionfunc):
213 216 """Render restructuredtext describing a list of commands and their
214 217 documentations, grouped by command category.
215 218
216 219 Args:
217 220 ui: UI object to write the output to
218 221 cmdtable: a dict that maps a string of the command name plus its aliases
219 222 (separated with pipes) to a 3-tuple of (the command's function, a list
220 223 of its option descriptions, and a string summarizing available
221 224 options). Example, with aliases added for demonstration purposes:
222 225
223 226 'phase|alias1|alias2': (
224 227 <function phase at 0x7f0816b05e60>,
225 228 [ ('p', 'public', False, 'set changeset phase to public'),
226 229 ...,
227 230 ('r', 'rev', [], 'target revision', 'REV')],
228 231 '[-p|-d|-s] [-f] [-r] [REV...]'
229 232 )
230 233 sectionfunc: minirst function to format command category headers
231 234 subsectionfunc: minirst function to format command headers
232 235 """
233 236 h = {}
234 237 for c, attr in cmdtable.items():
235 238 f = c.split(b"|")[0]
236 239 f = f.lstrip(b"^")
237 240 h[f] = c
238 241 cmds = h.keys()
239 242
240 243 def helpcategory(cmd):
241 244 """Given a canonical command name from `cmds` (above), retrieve its
242 245 help category. If helpcategory is None, default to CATEGORY_NONE.
243 246 """
244 247 fullname = h[cmd]
245 248 details = cmdtable[fullname]
246 249 helpcategory = details[0].helpcategory
247 250 return helpcategory or help.registrar.command.CATEGORY_NONE
248 251
249 252 cmdsbycategory = {category: [] for category in help.CATEGORY_ORDER}
250 253 for cmd in cmds:
251 254 # If a command category wasn't registered, the command won't get
252 255 # rendered below, so we raise an AssertionError.
253 256 if helpcategory(cmd) not in cmdsbycategory:
254 257 raise AssertionError(
255 258 "The following command did not register its (category) in "
256 259 "help.CATEGORY_ORDER: %s (%s)" % (cmd, helpcategory(cmd))
257 260 )
258 261 cmdsbycategory[helpcategory(cmd)].append(cmd)
259 262
260 263 # Print the help for each command. We present the commands grouped by
261 264 # category, and we use help.CATEGORY_ORDER as a guide for a helpful order
262 265 # in which to present the categories.
263 266 for category in help.CATEGORY_ORDER:
264 267 categorycmds = cmdsbycategory[category]
265 268 if not categorycmds:
266 269 # Skip empty categories
267 270 continue
268 271 # Print a section header for the category.
269 272 # For now, the category header is at the same level as the headers for
270 273 # the commands in the category; this is fixed in the next commit.
271 274 ui.write(sectionfunc(help.CATEGORY_NAMES[category]))
272 275 # Print each command in the category
273 276 for f in sorted(categorycmds):
274 277 if f.startswith(b"debug"):
275 278 continue
276 279 d = get_cmd(h[f], cmdtable)
277 280 ui.write(subsectionfunc(d[b'cmd']))
278 281 # short description
279 282 ui.write(d[b'desc'][0])
280 283 # synopsis
281 284 ui.write(b"::\n\n")
282 285 synopsislines = d[b'synopsis'].splitlines()
283 286 for line in synopsislines:
284 287 # some commands (such as rebase) have a multi-line
285 288 # synopsis
286 289 ui.write(b" %s\n" % line)
287 290 ui.write(b'\n')
288 291 # description
289 292 ui.write(b"%s\n\n" % d[b'desc'][1])
290 293 # options
291 294 opt_output = list(d[b'opts'])
292 295 if opt_output:
293 296 opts_len = max([len(line[0]) for line in opt_output])
294 297 ui.write(_(b"Options:\n\n"))
295 298 multioccur = False
296 299 for optstr, desc in opt_output:
297 300 if desc:
298 301 s = b"%-*s %s" % (opts_len, optstr, desc)
299 302 else:
300 303 s = optstr
301 304 ui.write(b"%s\n" % s)
302 305 if optstr.endswith(b"[+]>"):
303 306 multioccur = True
304 307 if multioccur:
305 308 ui.write(
306 309 _(
307 310 b"\n[+] marked option can be specified"
308 311 b" multiple times\n"
309 312 )
310 313 )
311 314 ui.write(b"\n")
312 315 # aliases
313 316 if d[b'aliases']:
314 317 ui.write(_(b" aliases: %s\n\n") % b" ".join(d[b'aliases']))
315 318
316 319
317 320 def allextensionnames():
318 321 return set(extensions.enabled().keys()) | set(extensions.disabled().keys())
319 322
320 323
321 324 if __name__ == "__main__":
322 325 doc = b'hg.1.gendoc'
323 326 if len(sys.argv) > 1:
324 327 doc = encoding.strtolocal(sys.argv[1])
325 328
326 329 ui = uimod.ui.load()
327 330 if doc == b'hg.1.gendoc':
328 331 showdoc(ui)
329 332 else:
330 333 showtopic(ui, encoding.strtolocal(sys.argv[1]))
General Comments 0
You need to be logged in to leave comments. Login now