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