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