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