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