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