##// END OF EJS Templates
help: fix makeitemsdoc English description
timeless@mozdev.org -
r26196:3a4620ad default
parent child Browse files
Show More
@@ -1,519 +1,519 b''
1 1 # help.py - help data for mercurial
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import gettext, _
9 9 import itertools, os, textwrap
10 10 import error
11 11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
12 12 import templater
13 13 import encoding, util, minirst
14 14 import cmdutil
15 15 import hgweb.webcommands as webcommands
16 16
17 17 def listexts(header, exts, indent=1, showdeprecated=False):
18 18 '''return a text listing of the given extensions'''
19 19 rst = []
20 20 if exts:
21 21 rst.append('\n%s\n\n' % header)
22 22 for name, desc in sorted(exts.iteritems()):
23 23 if '(DEPRECATED)' in desc and not showdeprecated:
24 24 continue
25 25 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
26 26 return rst
27 27
28 28 def extshelp():
29 29 rst = loaddoc('extensions')().splitlines(True)
30 30 rst.extend(listexts(
31 31 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
32 32 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
33 33 doc = ''.join(rst)
34 34 return doc
35 35
36 36 def optrst(header, options, verbose):
37 37 data = []
38 38 multioccur = False
39 39 for option in options:
40 40 if len(option) == 5:
41 41 shortopt, longopt, default, desc, optlabel = option
42 42 else:
43 43 shortopt, longopt, default, desc = option
44 44 optlabel = _("VALUE") # default label
45 45
46 46 if not verbose and ("DEPRECATED" in desc or _("DEPRECATED") in desc or
47 47 "EXPERIMENTAL" in desc or
48 48 _("EXPERIMENTAL") in desc):
49 49 continue
50 50
51 51 so = ''
52 52 if shortopt:
53 53 so = '-' + shortopt
54 54 lo = '--' + longopt
55 55 if default:
56 56 desc += _(" (default: %s)") % default
57 57
58 58 if isinstance(default, list):
59 59 lo += " %s [+]" % optlabel
60 60 multioccur = True
61 61 elif (default is not None) and not isinstance(default, bool):
62 62 lo += " %s" % optlabel
63 63
64 64 data.append((so, lo, desc))
65 65
66 66 if multioccur:
67 67 header += (_(" ([+] can be repeated)"))
68 68
69 69 rst = ['\n%s:\n\n' % header]
70 70 rst.extend(minirst.maketable(data, 1))
71 71
72 72 return ''.join(rst)
73 73
74 74 def indicateomitted(rst, omitted, notomitted=None):
75 75 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
76 76 if notomitted:
77 77 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
78 78
79 79 def topicmatch(kw):
80 80 """Return help topics matching kw.
81 81
82 82 Returns {'section': [(name, summary), ...], ...} where section is
83 83 one of topics, commands, extensions, or extensioncommands.
84 84 """
85 85 kw = encoding.lower(kw)
86 86 def lowercontains(container):
87 87 return kw in encoding.lower(container) # translated in helptable
88 88 results = {'topics': [],
89 89 'commands': [],
90 90 'extensions': [],
91 91 'extensioncommands': [],
92 92 }
93 93 for names, header, doc in helptable:
94 94 # Old extensions may use a str as doc.
95 95 if (sum(map(lowercontains, names))
96 96 or lowercontains(header)
97 97 or (callable(doc) and lowercontains(doc()))):
98 98 results['topics'].append((names[0], header))
99 99 import commands # avoid cycle
100 100 for cmd, entry in commands.table.iteritems():
101 101 if len(entry) == 3:
102 102 summary = entry[2]
103 103 else:
104 104 summary = ''
105 105 # translate docs *before* searching there
106 106 docs = _(getattr(entry[0], '__doc__', None)) or ''
107 107 if kw in cmd or lowercontains(summary) or lowercontains(docs):
108 108 doclines = docs.splitlines()
109 109 if doclines:
110 110 summary = doclines[0]
111 111 cmdname = cmd.split('|')[0].lstrip('^')
112 112 results['commands'].append((cmdname, summary))
113 113 for name, docs in itertools.chain(
114 114 extensions.enabled(False).iteritems(),
115 115 extensions.disabled().iteritems()):
116 116 # extensions.load ignores the UI argument
117 117 mod = extensions.load(None, name, '')
118 118 name = name.split('.')[-1]
119 119 if lowercontains(name) or lowercontains(docs):
120 120 # extension docs are already translated
121 121 results['extensions'].append((name, docs.splitlines()[0]))
122 122 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
123 123 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
124 124 cmdname = cmd.split('|')[0].lstrip('^')
125 125 if entry[0].__doc__:
126 126 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
127 127 else:
128 128 cmddoc = _('(no help text available)')
129 129 results['extensioncommands'].append((cmdname, cmddoc))
130 130 return results
131 131
132 132 def loaddoc(topic):
133 133 """Return a delayed loader for help/topic.txt."""
134 134
135 135 def loader():
136 136 docdir = os.path.join(util.datapath, 'help')
137 137 path = os.path.join(docdir, topic + ".txt")
138 138 doc = gettext(util.readfile(path))
139 139 for rewriter in helphooks.get(topic, []):
140 140 doc = rewriter(topic, doc)
141 141 return doc
142 142
143 143 return loader
144 144
145 145 helptable = sorted([
146 146 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
147 147 (["dates"], _("Date Formats"), loaddoc('dates')),
148 148 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
149 149 (['environment', 'env'], _('Environment Variables'),
150 150 loaddoc('environment')),
151 151 (['revisions', 'revs'], _('Specifying Single Revisions'),
152 152 loaddoc('revisions')),
153 153 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
154 154 loaddoc('multirevs')),
155 155 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
156 156 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
157 157 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
158 158 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
159 159 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
160 160 loaddoc('templates')),
161 161 (['urls'], _('URL Paths'), loaddoc('urls')),
162 162 (["extensions"], _("Using Additional Features"), extshelp),
163 163 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
164 164 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
165 165 (["glossary"], _("Glossary"), loaddoc('glossary')),
166 166 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
167 167 loaddoc('hgignore')),
168 168 (["phases"], _("Working with Phases"), loaddoc('phases')),
169 169 (['scripting'], _('Using Mercurial from scripts and automation'),
170 170 loaddoc('scripting')),
171 171 ])
172 172
173 173 # Map topics to lists of callable taking the current topic help and
174 174 # returning the updated version
175 175 helphooks = {}
176 176
177 177 def addtopichook(topic, rewriter):
178 178 helphooks.setdefault(topic, []).append(rewriter)
179 179
180 180 def makeitemsdoc(topic, doc, marker, items, dedent=False):
181 181 """Extract docstring from the items key to function mapping, build a
182 .single documentation block and use it to overwrite the marker in doc
182 single documentation block and use it to overwrite the marker in doc.
183 183 """
184 184 entries = []
185 185 for name in sorted(items):
186 186 text = (items[name].__doc__ or '').rstrip()
187 187 if not text:
188 188 continue
189 189 text = gettext(text)
190 190 if dedent:
191 191 text = textwrap.dedent(text)
192 192 lines = text.splitlines()
193 193 doclines = [(lines[0])]
194 194 for l in lines[1:]:
195 195 # Stop once we find some Python doctest
196 196 if l.strip().startswith('>>>'):
197 197 break
198 198 if dedent:
199 199 doclines.append(l.rstrip())
200 200 else:
201 201 doclines.append(' ' + l.strip())
202 202 entries.append('\n'.join(doclines))
203 203 entries = '\n\n'.join(entries)
204 204 return doc.replace(marker, entries)
205 205
206 206 def addtopicsymbols(topic, marker, symbols, dedent=False):
207 207 def add(topic, doc):
208 208 return makeitemsdoc(topic, doc, marker, symbols, dedent=dedent)
209 209 addtopichook(topic, add)
210 210
211 211 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
212 212 addtopicsymbols('merge-tools', '.. internaltoolsmarker',
213 213 filemerge.internalsdoc)
214 214 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
215 215 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
216 216 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
217 217 addtopicsymbols('templates', '.. functionsmarker', templater.funcs)
218 218 addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
219 219 dedent=True)
220 220
221 221 def help_(ui, name, unknowncmd=False, full=True, **opts):
222 222 '''
223 223 Generate the help for 'name' as unformatted restructured text. If
224 224 'name' is None, describe the commands available.
225 225 '''
226 226
227 227 import commands # avoid cycle
228 228
229 229 def helpcmd(name):
230 230 try:
231 231 aliases, entry = cmdutil.findcmd(name, commands.table,
232 232 strict=unknowncmd)
233 233 except error.AmbiguousCommand as inst:
234 234 # py3k fix: except vars can't be used outside the scope of the
235 235 # except block, nor can be used inside a lambda. python issue4617
236 236 prefix = inst.args[0]
237 237 select = lambda c: c.lstrip('^').startswith(prefix)
238 238 rst = helplist(select)
239 239 return rst
240 240
241 241 rst = []
242 242
243 243 # check if it's an invalid alias and display its error if it is
244 244 if getattr(entry[0], 'badalias', None):
245 245 rst.append(entry[0].badalias + '\n')
246 246 if entry[0].unknowncmd:
247 247 try:
248 248 rst.extend(helpextcmd(entry[0].cmdname))
249 249 except error.UnknownCommand:
250 250 pass
251 251 return rst
252 252
253 253 # synopsis
254 254 if len(entry) > 2:
255 255 if entry[2].startswith('hg'):
256 256 rst.append("%s\n" % entry[2])
257 257 else:
258 258 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
259 259 else:
260 260 rst.append('hg %s\n' % aliases[0])
261 261 # aliases
262 262 if full and not ui.quiet and len(aliases) > 1:
263 263 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
264 264 rst.append('\n')
265 265
266 266 # description
267 267 doc = gettext(entry[0].__doc__)
268 268 if not doc:
269 269 doc = _("(no help text available)")
270 270 if util.safehasattr(entry[0], 'definition'): # aliased command
271 271 if entry[0].definition.startswith('!'): # shell alias
272 272 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
273 273 else:
274 274 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
275 275 doc = doc.splitlines(True)
276 276 if ui.quiet or not full:
277 277 rst.append(doc[0])
278 278 else:
279 279 rst.extend(doc)
280 280 rst.append('\n')
281 281
282 282 # check if this command shadows a non-trivial (multi-line)
283 283 # extension help text
284 284 try:
285 285 mod = extensions.find(name)
286 286 doc = gettext(mod.__doc__) or ''
287 287 if '\n' in doc.strip():
288 288 msg = _('(use "hg help -e %s" to show help for '
289 289 'the %s extension)') % (name, name)
290 290 rst.append('\n%s\n' % msg)
291 291 except KeyError:
292 292 pass
293 293
294 294 # options
295 295 if not ui.quiet and entry[1]:
296 296 rst.append(optrst(_("options"), entry[1], ui.verbose))
297 297
298 298 if ui.verbose:
299 299 rst.append(optrst(_("global options"),
300 300 commands.globalopts, ui.verbose))
301 301
302 302 if not ui.verbose:
303 303 if not full:
304 304 rst.append(_('\n(use "hg %s -h" to show more help)\n')
305 305 % name)
306 306 elif not ui.quiet:
307 307 rst.append(_('\n(some details hidden, use --verbose '
308 308 'to show complete help)'))
309 309
310 310 return rst
311 311
312 312
313 313 def helplist(select=None):
314 314 # list of commands
315 315 if name == "shortlist":
316 316 header = _('basic commands:\n\n')
317 317 elif name == "debug":
318 318 header = _('debug commands (internal and unsupported):\n\n')
319 319 else:
320 320 header = _('list of commands:\n\n')
321 321
322 322 h = {}
323 323 cmds = {}
324 324 for c, e in commands.table.iteritems():
325 325 f = c.split("|", 1)[0]
326 326 if select and not select(f):
327 327 continue
328 328 if (not select and name != 'shortlist' and
329 329 e[0].__module__ != commands.__name__):
330 330 continue
331 331 if name == "shortlist" and not f.startswith("^"):
332 332 continue
333 333 f = f.lstrip("^")
334 334 if not ui.debugflag and f.startswith("debug") and name != "debug":
335 335 continue
336 336 doc = e[0].__doc__
337 337 if doc and 'DEPRECATED' in doc and not ui.verbose:
338 338 continue
339 339 doc = gettext(doc)
340 340 if not doc:
341 341 doc = _("(no help text available)")
342 342 h[f] = doc.splitlines()[0].rstrip()
343 343 cmds[f] = c.lstrip("^")
344 344
345 345 rst = []
346 346 if not h:
347 347 if not ui.quiet:
348 348 rst.append(_('no commands defined\n'))
349 349 return rst
350 350
351 351 if not ui.quiet:
352 352 rst.append(header)
353 353 fns = sorted(h)
354 354 for f in fns:
355 355 if ui.verbose:
356 356 commacmds = cmds[f].replace("|",", ")
357 357 rst.append(" :%s: %s\n" % (commacmds, h[f]))
358 358 else:
359 359 rst.append(' :%s: %s\n' % (f, h[f]))
360 360
361 361 if not name:
362 362 exts = listexts(_('enabled extensions:'), extensions.enabled())
363 363 if exts:
364 364 rst.append('\n')
365 365 rst.extend(exts)
366 366
367 367 rst.append(_("\nadditional help topics:\n\n"))
368 368 topics = []
369 369 for names, header, doc in helptable:
370 370 topics.append((names[0], header))
371 371 for t, desc in topics:
372 372 rst.append(" :%s: %s\n" % (t, desc))
373 373
374 374 if ui.quiet:
375 375 pass
376 376 elif ui.verbose:
377 377 rst.append('\n%s\n' % optrst(_("global options"),
378 378 commands.globalopts, ui.verbose))
379 379 if name == 'shortlist':
380 380 rst.append(_('\n(use "hg help" for the full list '
381 381 'of commands)\n'))
382 382 else:
383 383 if name == 'shortlist':
384 384 rst.append(_('\n(use "hg help" for the full list of commands '
385 385 'or "hg -v" for details)\n'))
386 386 elif name and not full:
387 387 rst.append(_('\n(use "hg help %s" to show the full help '
388 388 'text)\n') % name)
389 389 elif name and cmds and name in cmds.keys():
390 390 rst.append(_('\n(use "hg help -v -e %s" to show built-in '
391 391 'aliases and global options)\n') % name)
392 392 else:
393 393 rst.append(_('\n(use "hg help -v%s" to show built-in aliases '
394 394 'and global options)\n')
395 395 % (name and " " + name or ""))
396 396 return rst
397 397
398 398 def helptopic(name):
399 399 for names, header, doc in helptable:
400 400 if name in names:
401 401 break
402 402 else:
403 403 raise error.UnknownCommand(name)
404 404
405 405 rst = [minirst.section(header)]
406 406
407 407 # description
408 408 if not doc:
409 409 rst.append(" %s\n" % _("(no help text available)"))
410 410 if callable(doc):
411 411 rst += [" %s\n" % l for l in doc().splitlines()]
412 412
413 413 if not ui.verbose:
414 414 omitted = _('(some details hidden, use --verbose'
415 415 ' to show complete help)')
416 416 indicateomitted(rst, omitted)
417 417
418 418 try:
419 419 cmdutil.findcmd(name, commands.table)
420 420 rst.append(_('\nuse "hg help -c %s" to see help for '
421 421 'the %s command\n') % (name, name))
422 422 except error.UnknownCommand:
423 423 pass
424 424 return rst
425 425
426 426 def helpext(name):
427 427 try:
428 428 mod = extensions.find(name)
429 429 doc = gettext(mod.__doc__) or _('no help text available')
430 430 except KeyError:
431 431 mod = None
432 432 doc = extensions.disabledext(name)
433 433 if not doc:
434 434 raise error.UnknownCommand(name)
435 435
436 436 if '\n' not in doc:
437 437 head, tail = doc, ""
438 438 else:
439 439 head, tail = doc.split('\n', 1)
440 440 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
441 441 if tail:
442 442 rst.extend(tail.splitlines(True))
443 443 rst.append('\n')
444 444
445 445 if not ui.verbose:
446 446 omitted = _('(some details hidden, use --verbose'
447 447 ' to show complete help)')
448 448 indicateomitted(rst, omitted)
449 449
450 450 if mod:
451 451 try:
452 452 ct = mod.cmdtable
453 453 except AttributeError:
454 454 ct = {}
455 455 modcmds = set([c.split('|', 1)[0] for c in ct])
456 456 rst.extend(helplist(modcmds.__contains__))
457 457 else:
458 458 rst.append(_('(use "hg help extensions" for information on enabling'
459 459 ' extensions)\n'))
460 460 return rst
461 461
462 462 def helpextcmd(name):
463 463 cmd, ext, mod = extensions.disabledcmd(ui, name,
464 464 ui.configbool('ui', 'strict'))
465 465 doc = gettext(mod.__doc__).splitlines()[0]
466 466
467 467 rst = listexts(_("'%s' is provided by the following "
468 468 "extension:") % cmd, {ext: doc}, indent=4)
469 469 rst.append('\n')
470 470 rst.append(_('(use "hg help extensions" for information on enabling '
471 471 'extensions)\n'))
472 472 return rst
473 473
474 474
475 475 rst = []
476 476 kw = opts.get('keyword')
477 477 if kw:
478 478 matches = topicmatch(kw)
479 479 for t, title in (('topics', _('Topics')),
480 480 ('commands', _('Commands')),
481 481 ('extensions', _('Extensions')),
482 482 ('extensioncommands', _('Extension Commands'))):
483 483 if matches[t]:
484 484 rst.append('%s:\n\n' % title)
485 485 rst.extend(minirst.maketable(sorted(matches[t]), 1))
486 486 rst.append('\n')
487 487 if not rst:
488 488 msg = _('no matches')
489 489 hint = _('try "hg help" for a list of topics')
490 490 raise util.Abort(msg, hint=hint)
491 491 elif name and name != 'shortlist':
492 492 if unknowncmd:
493 493 queries = (helpextcmd,)
494 494 elif opts.get('extension'):
495 495 queries = (helpext,)
496 496 elif opts.get('command'):
497 497 queries = (helpcmd,)
498 498 else:
499 499 queries = (helptopic, helpcmd, helpext, helpextcmd)
500 500 for f in queries:
501 501 try:
502 502 rst = f(name)
503 503 break
504 504 except error.UnknownCommand:
505 505 pass
506 506 else:
507 507 if unknowncmd:
508 508 raise error.UnknownCommand(name)
509 509 else:
510 510 msg = _('no such help topic: %s') % name
511 511 hint = _('try "hg help --keyword %s"') % name
512 512 raise util.Abort(msg, hint=hint)
513 513 else:
514 514 # program name
515 515 if not ui.quiet:
516 516 rst = [_("Mercurial Distributed SCM\n"), '\n']
517 517 rst.extend(helplist())
518 518
519 519 return ''.join(rst)
General Comments 0
You need to be logged in to leave comments. Login now