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