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