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