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