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