##// END OF EJS Templates
help: fix a py3 error interpolating Set into b'%s'...
Matt Harbison -
r50536:f09bc2ed stable
parent child Browse files
Show More
@@ -1,1181 +1,1181 b''
1 # help.py - help data for mercurial
1 # help.py - help data for mercurial
2 #
2 #
3 # Copyright 2006 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2006 Olivia Mackall <olivia@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
8
9 import itertools
9 import itertools
10 import re
10 import re
11 import textwrap
11 import textwrap
12
12
13 from .i18n import (
13 from .i18n import (
14 _,
14 _,
15 gettext,
15 gettext,
16 )
16 )
17 from .pycompat import getattr
17 from .pycompat import getattr
18 from . import (
18 from . import (
19 cmdutil,
19 cmdutil,
20 encoding,
20 encoding,
21 error,
21 error,
22 extensions,
22 extensions,
23 fancyopts,
23 fancyopts,
24 filemerge,
24 filemerge,
25 fileset,
25 fileset,
26 minirst,
26 minirst,
27 pycompat,
27 pycompat,
28 registrar,
28 registrar,
29 revset,
29 revset,
30 templatefilters,
30 templatefilters,
31 templatefuncs,
31 templatefuncs,
32 templatekw,
32 templatekw,
33 ui as uimod,
33 ui as uimod,
34 util,
34 util,
35 )
35 )
36 from .hgweb import webcommands
36 from .hgweb import webcommands
37 from .utils import (
37 from .utils import (
38 compression,
38 compression,
39 resourceutil,
39 resourceutil,
40 stringutil,
40 stringutil,
41 )
41 )
42
42
43 _exclkeywords = {
43 _exclkeywords = {
44 b"(ADVANCED)",
44 b"(ADVANCED)",
45 b"(DEPRECATED)",
45 b"(DEPRECATED)",
46 b"(EXPERIMENTAL)",
46 b"(EXPERIMENTAL)",
47 # i18n: "(ADVANCED)" is a keyword, must be translated consistently
47 # i18n: "(ADVANCED)" is a keyword, must be translated consistently
48 _(b"(ADVANCED)"),
48 _(b"(ADVANCED)"),
49 # i18n: "(DEPRECATED)" is a keyword, must be translated consistently
49 # i18n: "(DEPRECATED)" is a keyword, must be translated consistently
50 _(b"(DEPRECATED)"),
50 _(b"(DEPRECATED)"),
51 # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently
51 # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently
52 _(b"(EXPERIMENTAL)"),
52 _(b"(EXPERIMENTAL)"),
53 }
53 }
54
54
55 # The order in which command categories will be displayed.
55 # The order in which command categories will be displayed.
56 # Extensions with custom categories should insert them into this list
56 # Extensions with custom categories should insert them into this list
57 # after/before the appropriate item, rather than replacing the list or
57 # after/before the appropriate item, rather than replacing the list or
58 # assuming absolute positions.
58 # assuming absolute positions.
59 CATEGORY_ORDER = [
59 CATEGORY_ORDER = [
60 registrar.command.CATEGORY_REPO_CREATION,
60 registrar.command.CATEGORY_REPO_CREATION,
61 registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT,
61 registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT,
62 registrar.command.CATEGORY_COMMITTING,
62 registrar.command.CATEGORY_COMMITTING,
63 registrar.command.CATEGORY_CHANGE_MANAGEMENT,
63 registrar.command.CATEGORY_CHANGE_MANAGEMENT,
64 registrar.command.CATEGORY_CHANGE_ORGANIZATION,
64 registrar.command.CATEGORY_CHANGE_ORGANIZATION,
65 registrar.command.CATEGORY_FILE_CONTENTS,
65 registrar.command.CATEGORY_FILE_CONTENTS,
66 registrar.command.CATEGORY_CHANGE_NAVIGATION,
66 registrar.command.CATEGORY_CHANGE_NAVIGATION,
67 registrar.command.CATEGORY_WORKING_DIRECTORY,
67 registrar.command.CATEGORY_WORKING_DIRECTORY,
68 registrar.command.CATEGORY_IMPORT_EXPORT,
68 registrar.command.CATEGORY_IMPORT_EXPORT,
69 registrar.command.CATEGORY_MAINTENANCE,
69 registrar.command.CATEGORY_MAINTENANCE,
70 registrar.command.CATEGORY_HELP,
70 registrar.command.CATEGORY_HELP,
71 registrar.command.CATEGORY_MISC,
71 registrar.command.CATEGORY_MISC,
72 registrar.command.CATEGORY_NONE,
72 registrar.command.CATEGORY_NONE,
73 ]
73 ]
74
74
75 # Human-readable category names. These are translated.
75 # Human-readable category names. These are translated.
76 # Extensions with custom categories should add their names here.
76 # Extensions with custom categories should add their names here.
77 CATEGORY_NAMES = {
77 CATEGORY_NAMES = {
78 registrar.command.CATEGORY_REPO_CREATION: b'Repository creation',
78 registrar.command.CATEGORY_REPO_CREATION: b'Repository creation',
79 registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT: b'Remote repository management',
79 registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT: b'Remote repository management',
80 registrar.command.CATEGORY_COMMITTING: b'Change creation',
80 registrar.command.CATEGORY_COMMITTING: b'Change creation',
81 registrar.command.CATEGORY_CHANGE_NAVIGATION: b'Change navigation',
81 registrar.command.CATEGORY_CHANGE_NAVIGATION: b'Change navigation',
82 registrar.command.CATEGORY_CHANGE_MANAGEMENT: b'Change manipulation',
82 registrar.command.CATEGORY_CHANGE_MANAGEMENT: b'Change manipulation',
83 registrar.command.CATEGORY_CHANGE_ORGANIZATION: b'Change organization',
83 registrar.command.CATEGORY_CHANGE_ORGANIZATION: b'Change organization',
84 registrar.command.CATEGORY_WORKING_DIRECTORY: b'Working directory management',
84 registrar.command.CATEGORY_WORKING_DIRECTORY: b'Working directory management',
85 registrar.command.CATEGORY_FILE_CONTENTS: b'File content management',
85 registrar.command.CATEGORY_FILE_CONTENTS: b'File content management',
86 registrar.command.CATEGORY_IMPORT_EXPORT: b'Change import/export',
86 registrar.command.CATEGORY_IMPORT_EXPORT: b'Change import/export',
87 registrar.command.CATEGORY_MAINTENANCE: b'Repository maintenance',
87 registrar.command.CATEGORY_MAINTENANCE: b'Repository maintenance',
88 registrar.command.CATEGORY_HELP: b'Help',
88 registrar.command.CATEGORY_HELP: b'Help',
89 registrar.command.CATEGORY_MISC: b'Miscellaneous commands',
89 registrar.command.CATEGORY_MISC: b'Miscellaneous commands',
90 registrar.command.CATEGORY_NONE: b'Uncategorized commands',
90 registrar.command.CATEGORY_NONE: b'Uncategorized commands',
91 }
91 }
92
92
93 # Topic categories.
93 # Topic categories.
94 TOPIC_CATEGORY_IDS = b'ids'
94 TOPIC_CATEGORY_IDS = b'ids'
95 TOPIC_CATEGORY_OUTPUT = b'output'
95 TOPIC_CATEGORY_OUTPUT = b'output'
96 TOPIC_CATEGORY_CONFIG = b'config'
96 TOPIC_CATEGORY_CONFIG = b'config'
97 TOPIC_CATEGORY_CONCEPTS = b'concepts'
97 TOPIC_CATEGORY_CONCEPTS = b'concepts'
98 TOPIC_CATEGORY_MISC = b'misc'
98 TOPIC_CATEGORY_MISC = b'misc'
99 TOPIC_CATEGORY_NONE = b'none'
99 TOPIC_CATEGORY_NONE = b'none'
100
100
101 # The order in which topic categories will be displayed.
101 # The order in which topic categories will be displayed.
102 # Extensions with custom categories should insert them into this list
102 # Extensions with custom categories should insert them into this list
103 # after/before the appropriate item, rather than replacing the list or
103 # after/before the appropriate item, rather than replacing the list or
104 # assuming absolute positions.
104 # assuming absolute positions.
105 TOPIC_CATEGORY_ORDER = [
105 TOPIC_CATEGORY_ORDER = [
106 TOPIC_CATEGORY_IDS,
106 TOPIC_CATEGORY_IDS,
107 TOPIC_CATEGORY_OUTPUT,
107 TOPIC_CATEGORY_OUTPUT,
108 TOPIC_CATEGORY_CONFIG,
108 TOPIC_CATEGORY_CONFIG,
109 TOPIC_CATEGORY_CONCEPTS,
109 TOPIC_CATEGORY_CONCEPTS,
110 TOPIC_CATEGORY_MISC,
110 TOPIC_CATEGORY_MISC,
111 TOPIC_CATEGORY_NONE,
111 TOPIC_CATEGORY_NONE,
112 ]
112 ]
113
113
114 # Human-readable topic category names. These are translated.
114 # Human-readable topic category names. These are translated.
115 TOPIC_CATEGORY_NAMES = {
115 TOPIC_CATEGORY_NAMES = {
116 TOPIC_CATEGORY_IDS: b'Mercurial identifiers',
116 TOPIC_CATEGORY_IDS: b'Mercurial identifiers',
117 TOPIC_CATEGORY_OUTPUT: b'Mercurial output',
117 TOPIC_CATEGORY_OUTPUT: b'Mercurial output',
118 TOPIC_CATEGORY_CONFIG: b'Mercurial configuration',
118 TOPIC_CATEGORY_CONFIG: b'Mercurial configuration',
119 TOPIC_CATEGORY_CONCEPTS: b'Concepts',
119 TOPIC_CATEGORY_CONCEPTS: b'Concepts',
120 TOPIC_CATEGORY_MISC: b'Miscellaneous',
120 TOPIC_CATEGORY_MISC: b'Miscellaneous',
121 TOPIC_CATEGORY_NONE: b'Uncategorized topics',
121 TOPIC_CATEGORY_NONE: b'Uncategorized topics',
122 }
122 }
123
123
124
124
125 def listexts(header, exts, indent=1, showdeprecated=False):
125 def listexts(header, exts, indent=1, showdeprecated=False):
126 '''return a text listing of the given extensions'''
126 '''return a text listing of the given extensions'''
127 rst = []
127 rst = []
128 if exts:
128 if exts:
129 for name, desc in sorted(exts.items()):
129 for name, desc in sorted(exts.items()):
130 if not showdeprecated and any(w in desc for w in _exclkeywords):
130 if not showdeprecated and any(w in desc for w in _exclkeywords):
131 continue
131 continue
132 rst.append(b'%s:%s: %s\n' % (b' ' * indent, name, desc))
132 rst.append(b'%s:%s: %s\n' % (b' ' * indent, name, desc))
133 if rst:
133 if rst:
134 rst.insert(0, b'\n%s\n\n' % header)
134 rst.insert(0, b'\n%s\n\n' % header)
135 return rst
135 return rst
136
136
137
137
138 def extshelp(ui):
138 def extshelp(ui):
139 rst = loaddoc(b'extensions')(ui).splitlines(True)
139 rst = loaddoc(b'extensions')(ui).splitlines(True)
140 rst.extend(
140 rst.extend(
141 listexts(
141 listexts(
142 _(b'enabled extensions:'), extensions.enabled(), showdeprecated=True
142 _(b'enabled extensions:'), extensions.enabled(), showdeprecated=True
143 )
143 )
144 )
144 )
145 rst.extend(
145 rst.extend(
146 listexts(
146 listexts(
147 _(b'disabled extensions:'),
147 _(b'disabled extensions:'),
148 extensions.disabled(),
148 extensions.disabled(),
149 showdeprecated=ui.verbose,
149 showdeprecated=ui.verbose,
150 )
150 )
151 )
151 )
152 doc = b''.join(rst)
152 doc = b''.join(rst)
153 return doc
153 return doc
154
154
155
155
156 def parsedefaultmarker(text):
156 def parsedefaultmarker(text):
157 """given a text 'abc (DEFAULT: def.ghi)',
157 """given a text 'abc (DEFAULT: def.ghi)',
158 returns (b'abc', (b'def', b'ghi')). Otherwise return None"""
158 returns (b'abc', (b'def', b'ghi')). Otherwise return None"""
159 if text[-1:] == b')':
159 if text[-1:] == b')':
160 marker = b' (DEFAULT: '
160 marker = b' (DEFAULT: '
161 pos = text.find(marker)
161 pos = text.find(marker)
162 if pos >= 0:
162 if pos >= 0:
163 item = text[pos + len(marker) : -1]
163 item = text[pos + len(marker) : -1]
164 return text[:pos], item.split(b'.', 2)
164 return text[:pos], item.split(b'.', 2)
165
165
166
166
167 def optrst(header, options, verbose, ui):
167 def optrst(header, options, verbose, ui):
168 data = []
168 data = []
169 multioccur = False
169 multioccur = False
170 for option in options:
170 for option in options:
171 if len(option) == 5:
171 if len(option) == 5:
172 shortopt, longopt, default, desc, optlabel = option
172 shortopt, longopt, default, desc, optlabel = option
173 else:
173 else:
174 shortopt, longopt, default, desc = option
174 shortopt, longopt, default, desc = option
175 optlabel = _(b"VALUE") # default label
175 optlabel = _(b"VALUE") # default label
176
176
177 if not verbose and any(w in desc for w in _exclkeywords):
177 if not verbose and any(w in desc for w in _exclkeywords):
178 continue
178 continue
179 defaultstrsuffix = b''
179 defaultstrsuffix = b''
180 if default is None:
180 if default is None:
181 parseresult = parsedefaultmarker(desc)
181 parseresult = parsedefaultmarker(desc)
182 if parseresult is not None:
182 if parseresult is not None:
183 (desc, (section, name)) = parseresult
183 (desc, (section, name)) = parseresult
184 if ui.configbool(section, name):
184 if ui.configbool(section, name):
185 default = True
185 default = True
186 defaultstrsuffix = _(b' from config')
186 defaultstrsuffix = _(b' from config')
187 so = b''
187 so = b''
188 if shortopt:
188 if shortopt:
189 so = b'-' + shortopt
189 so = b'-' + shortopt
190 lo = b'--' + longopt
190 lo = b'--' + longopt
191 if default is True:
191 if default is True:
192 lo = b'--[no-]' + longopt
192 lo = b'--[no-]' + longopt
193
193
194 if isinstance(default, fancyopts.customopt):
194 if isinstance(default, fancyopts.customopt):
195 default = default.getdefaultvalue()
195 default = default.getdefaultvalue()
196 if default and not callable(default):
196 if default and not callable(default):
197 # default is of unknown type, and in Python 2 we abused
197 # default is of unknown type, and in Python 2 we abused
198 # the %s-shows-repr property to handle integers etc. To
198 # the %s-shows-repr property to handle integers etc. To
199 # match that behavior on Python 3, we do str(default) and
199 # match that behavior on Python 3, we do str(default) and
200 # then convert it to bytes.
200 # then convert it to bytes.
201 defaultstr = pycompat.bytestr(default)
201 defaultstr = pycompat.bytestr(default)
202 if default is True:
202 if default is True:
203 defaultstr = _(b"on")
203 defaultstr = _(b"on")
204 desc += _(b" (default: %s)") % (defaultstr + defaultstrsuffix)
204 desc += _(b" (default: %s)") % (defaultstr + defaultstrsuffix)
205
205
206 if isinstance(default, list):
206 if isinstance(default, list):
207 lo += b" %s [+]" % optlabel
207 lo += b" %s [+]" % optlabel
208 multioccur = True
208 multioccur = True
209 elif (default is not None) and not isinstance(default, bool):
209 elif (default is not None) and not isinstance(default, bool):
210 lo += b" %s" % optlabel
210 lo += b" %s" % optlabel
211
211
212 data.append((so, lo, desc))
212 data.append((so, lo, desc))
213
213
214 if multioccur:
214 if multioccur:
215 header += _(b" ([+] can be repeated)")
215 header += _(b" ([+] can be repeated)")
216
216
217 rst = [b'\n%s:\n\n' % header]
217 rst = [b'\n%s:\n\n' % header]
218 rst.extend(minirst.maketable(data, 1))
218 rst.extend(minirst.maketable(data, 1))
219
219
220 return b''.join(rst)
220 return b''.join(rst)
221
221
222
222
223 def indicateomitted(rst, omitted, notomitted=None):
223 def indicateomitted(rst, omitted, notomitted=None):
224 rst.append(b'\n\n.. container:: omitted\n\n %s\n\n' % omitted)
224 rst.append(b'\n\n.. container:: omitted\n\n %s\n\n' % omitted)
225 if notomitted:
225 if notomitted:
226 rst.append(b'\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
226 rst.append(b'\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
227
227
228
228
229 def filtercmd(ui, cmd, func, kw, doc):
229 def filtercmd(ui, cmd, func, kw, doc):
230 if not ui.debugflag and cmd.startswith(b"debug") and kw != b"debug":
230 if not ui.debugflag and cmd.startswith(b"debug") and kw != b"debug":
231 # Debug command, and user is not looking for those.
231 # Debug command, and user is not looking for those.
232 return True
232 return True
233 if not ui.verbose:
233 if not ui.verbose:
234 if not kw and not doc:
234 if not kw and not doc:
235 # Command had no documentation, no point in showing it by default.
235 # Command had no documentation, no point in showing it by default.
236 return True
236 return True
237 if getattr(func, 'alias', False) and not getattr(func, 'owndoc', False):
237 if getattr(func, 'alias', False) and not getattr(func, 'owndoc', False):
238 # Alias didn't have its own documentation.
238 # Alias didn't have its own documentation.
239 return True
239 return True
240 if doc and any(w in doc for w in _exclkeywords):
240 if doc and any(w in doc for w in _exclkeywords):
241 # Documentation has excluded keywords.
241 # Documentation has excluded keywords.
242 return True
242 return True
243 if kw == b"shortlist" and not getattr(func, 'helpbasic', False):
243 if kw == b"shortlist" and not getattr(func, 'helpbasic', False):
244 # We're presenting the short list but the command is not basic.
244 # We're presenting the short list but the command is not basic.
245 return True
245 return True
246 if ui.configbool(b'help', b'hidden-command.%s' % cmd):
246 if ui.configbool(b'help', b'hidden-command.%s' % cmd):
247 # Configuration explicitly hides the command.
247 # Configuration explicitly hides the command.
248 return True
248 return True
249 return False
249 return False
250
250
251
251
252 def filtertopic(ui, topic):
252 def filtertopic(ui, topic):
253 return ui.configbool(b'help', b'hidden-topic.%s' % topic, False)
253 return ui.configbool(b'help', b'hidden-topic.%s' % topic, False)
254
254
255
255
256 def topicmatch(ui, commands, kw):
256 def topicmatch(ui, commands, kw):
257 """Return help topics matching kw.
257 """Return help topics matching kw.
258
258
259 Returns {'section': [(name, summary), ...], ...} where section is
259 Returns {'section': [(name, summary), ...], ...} where section is
260 one of topics, commands, extensions, or extensioncommands.
260 one of topics, commands, extensions, or extensioncommands.
261 """
261 """
262 kw = encoding.lower(kw)
262 kw = encoding.lower(kw)
263
263
264 def lowercontains(container):
264 def lowercontains(container):
265 return kw in encoding.lower(container) # translated in helptable
265 return kw in encoding.lower(container) # translated in helptable
266
266
267 results = {
267 results = {
268 b'topics': [],
268 b'topics': [],
269 b'commands': [],
269 b'commands': [],
270 b'extensions': [],
270 b'extensions': [],
271 b'extensioncommands': [],
271 b'extensioncommands': [],
272 }
272 }
273 for topic in helptable:
273 for topic in helptable:
274 names, header, doc = topic[0:3]
274 names, header, doc = topic[0:3]
275 # Old extensions may use a str as doc.
275 # Old extensions may use a str as doc.
276 if (
276 if (
277 sum(map(lowercontains, names))
277 sum(map(lowercontains, names))
278 or lowercontains(header)
278 or lowercontains(header)
279 or (callable(doc) and lowercontains(doc(ui)))
279 or (callable(doc) and lowercontains(doc(ui)))
280 ):
280 ):
281 name = names[0]
281 name = names[0]
282 if not filtertopic(ui, name):
282 if not filtertopic(ui, name):
283 results[b'topics'].append((names[0], header))
283 results[b'topics'].append((names[0], header))
284 for cmd, entry in commands.table.items():
284 for cmd, entry in commands.table.items():
285 if len(entry) == 3:
285 if len(entry) == 3:
286 summary = entry[2]
286 summary = entry[2]
287 else:
287 else:
288 summary = b''
288 summary = b''
289 # translate docs *before* searching there
289 # translate docs *before* searching there
290 func = entry[0]
290 func = entry[0]
291 docs = _(pycompat.getdoc(func)) or b''
291 docs = _(pycompat.getdoc(func)) or b''
292 if kw in cmd or lowercontains(summary) or lowercontains(docs):
292 if kw in cmd or lowercontains(summary) or lowercontains(docs):
293 if docs:
293 if docs:
294 summary = stringutil.firstline(docs)
294 summary = stringutil.firstline(docs)
295 cmdname = cmdutil.parsealiases(cmd)[0]
295 cmdname = cmdutil.parsealiases(cmd)[0]
296 if filtercmd(ui, cmdname, func, kw, docs):
296 if filtercmd(ui, cmdname, func, kw, docs):
297 continue
297 continue
298 results[b'commands'].append((cmdname, summary))
298 results[b'commands'].append((cmdname, summary))
299 for name, docs in itertools.chain(
299 for name, docs in itertools.chain(
300 extensions.enabled(False).items(),
300 extensions.enabled(False).items(),
301 extensions.disabled().items(),
301 extensions.disabled().items(),
302 ):
302 ):
303 if not docs:
303 if not docs:
304 continue
304 continue
305 name = name.rpartition(b'.')[-1]
305 name = name.rpartition(b'.')[-1]
306 if lowercontains(name) or lowercontains(docs):
306 if lowercontains(name) or lowercontains(docs):
307 # extension docs are already translated
307 # extension docs are already translated
308 results[b'extensions'].append((name, stringutil.firstline(docs)))
308 results[b'extensions'].append((name, stringutil.firstline(docs)))
309 try:
309 try:
310 mod = extensions.load(ui, name, b'')
310 mod = extensions.load(ui, name, b'')
311 except ImportError:
311 except ImportError:
312 # debug message would be printed in extensions.load()
312 # debug message would be printed in extensions.load()
313 continue
313 continue
314 for cmd, entry in getattr(mod, 'cmdtable', {}).items():
314 for cmd, entry in getattr(mod, 'cmdtable', {}).items():
315 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
315 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
316 cmdname = cmdutil.parsealiases(cmd)[0]
316 cmdname = cmdutil.parsealiases(cmd)[0]
317 func = entry[0]
317 func = entry[0]
318 cmddoc = pycompat.getdoc(func)
318 cmddoc = pycompat.getdoc(func)
319 if cmddoc:
319 if cmddoc:
320 cmddoc = stringutil.firstline(gettext(cmddoc))
320 cmddoc = stringutil.firstline(gettext(cmddoc))
321 else:
321 else:
322 cmddoc = _(b'(no help text available)')
322 cmddoc = _(b'(no help text available)')
323 if filtercmd(ui, cmdname, func, kw, cmddoc):
323 if filtercmd(ui, cmdname, func, kw, cmddoc):
324 continue
324 continue
325 results[b'extensioncommands'].append((cmdname, cmddoc))
325 results[b'extensioncommands'].append((cmdname, cmddoc))
326 return results
326 return results
327
327
328
328
329 def loaddoc(topic, subdir=None):
329 def loaddoc(topic, subdir=None):
330 """Return a delayed loader for help/topic.txt."""
330 """Return a delayed loader for help/topic.txt."""
331
331
332 def loader(ui):
332 def loader(ui):
333 package = b'mercurial.helptext'
333 package = b'mercurial.helptext'
334 if subdir:
334 if subdir:
335 package += b'.' + subdir
335 package += b'.' + subdir
336 with resourceutil.open_resource(package, topic + b'.txt') as fp:
336 with resourceutil.open_resource(package, topic + b'.txt') as fp:
337 doc = gettext(fp.read())
337 doc = gettext(fp.read())
338 for rewriter in helphooks.get(topic, []):
338 for rewriter in helphooks.get(topic, []):
339 doc = rewriter(ui, topic, doc)
339 doc = rewriter(ui, topic, doc)
340 return doc
340 return doc
341
341
342 return loader
342 return loader
343
343
344
344
345 internalstable = sorted(
345 internalstable = sorted(
346 [
346 [
347 (
347 (
348 [b'bid-merge'],
348 [b'bid-merge'],
349 _(b'Bid Merge Algorithm'),
349 _(b'Bid Merge Algorithm'),
350 loaddoc(b'bid-merge', subdir=b'internals'),
350 loaddoc(b'bid-merge', subdir=b'internals'),
351 ),
351 ),
352 ([b'bundle2'], _(b'Bundle2'), loaddoc(b'bundle2', subdir=b'internals')),
352 ([b'bundle2'], _(b'Bundle2'), loaddoc(b'bundle2', subdir=b'internals')),
353 ([b'bundles'], _(b'Bundles'), loaddoc(b'bundles', subdir=b'internals')),
353 ([b'bundles'], _(b'Bundles'), loaddoc(b'bundles', subdir=b'internals')),
354 ([b'cbor'], _(b'CBOR'), loaddoc(b'cbor', subdir=b'internals')),
354 ([b'cbor'], _(b'CBOR'), loaddoc(b'cbor', subdir=b'internals')),
355 ([b'censor'], _(b'Censor'), loaddoc(b'censor', subdir=b'internals')),
355 ([b'censor'], _(b'Censor'), loaddoc(b'censor', subdir=b'internals')),
356 (
356 (
357 [b'changegroups'],
357 [b'changegroups'],
358 _(b'Changegroups'),
358 _(b'Changegroups'),
359 loaddoc(b'changegroups', subdir=b'internals'),
359 loaddoc(b'changegroups', subdir=b'internals'),
360 ),
360 ),
361 (
361 (
362 [b'config'],
362 [b'config'],
363 _(b'Config Registrar'),
363 _(b'Config Registrar'),
364 loaddoc(b'config', subdir=b'internals'),
364 loaddoc(b'config', subdir=b'internals'),
365 ),
365 ),
366 (
366 (
367 [b'dirstate-v2'],
367 [b'dirstate-v2'],
368 _(b'dirstate-v2 file format'),
368 _(b'dirstate-v2 file format'),
369 loaddoc(b'dirstate-v2', subdir=b'internals'),
369 loaddoc(b'dirstate-v2', subdir=b'internals'),
370 ),
370 ),
371 (
371 (
372 [b'extensions', b'extension'],
372 [b'extensions', b'extension'],
373 _(b'Extension API'),
373 _(b'Extension API'),
374 loaddoc(b'extensions', subdir=b'internals'),
374 loaddoc(b'extensions', subdir=b'internals'),
375 ),
375 ),
376 (
376 (
377 [b'mergestate'],
377 [b'mergestate'],
378 _(b'Mergestate'),
378 _(b'Mergestate'),
379 loaddoc(b'mergestate', subdir=b'internals'),
379 loaddoc(b'mergestate', subdir=b'internals'),
380 ),
380 ),
381 (
381 (
382 [b'requirements'],
382 [b'requirements'],
383 _(b'Repository Requirements'),
383 _(b'Repository Requirements'),
384 loaddoc(b'requirements', subdir=b'internals'),
384 loaddoc(b'requirements', subdir=b'internals'),
385 ),
385 ),
386 (
386 (
387 [b'revlogs'],
387 [b'revlogs'],
388 _(b'Revision Logs'),
388 _(b'Revision Logs'),
389 loaddoc(b'revlogs', subdir=b'internals'),
389 loaddoc(b'revlogs', subdir=b'internals'),
390 ),
390 ),
391 (
391 (
392 [b'wireprotocol'],
392 [b'wireprotocol'],
393 _(b'Wire Protocol'),
393 _(b'Wire Protocol'),
394 loaddoc(b'wireprotocol', subdir=b'internals'),
394 loaddoc(b'wireprotocol', subdir=b'internals'),
395 ),
395 ),
396 (
396 (
397 [b'wireprotocolrpc'],
397 [b'wireprotocolrpc'],
398 _(b'Wire Protocol RPC'),
398 _(b'Wire Protocol RPC'),
399 loaddoc(b'wireprotocolrpc', subdir=b'internals'),
399 loaddoc(b'wireprotocolrpc', subdir=b'internals'),
400 ),
400 ),
401 (
401 (
402 [b'wireprotocolv2'],
402 [b'wireprotocolv2'],
403 _(b'Wire Protocol Version 2'),
403 _(b'Wire Protocol Version 2'),
404 loaddoc(b'wireprotocolv2', subdir=b'internals'),
404 loaddoc(b'wireprotocolv2', subdir=b'internals'),
405 ),
405 ),
406 ]
406 ]
407 )
407 )
408
408
409
409
410 def internalshelp(ui):
410 def internalshelp(ui):
411 """Generate the index for the "internals" topic."""
411 """Generate the index for the "internals" topic."""
412 lines = [
412 lines = [
413 b'To access a subtopic, use "hg help internals.{subtopic-name}"\n',
413 b'To access a subtopic, use "hg help internals.{subtopic-name}"\n',
414 b'\n',
414 b'\n',
415 ]
415 ]
416 for names, header, doc in internalstable:
416 for names, header, doc in internalstable:
417 lines.append(b' :%s: %s\n' % (names[0], header))
417 lines.append(b' :%s: %s\n' % (names[0], header))
418
418
419 return b''.join(lines)
419 return b''.join(lines)
420
420
421
421
422 helptable = sorted(
422 helptable = sorted(
423 [
423 [
424 (
424 (
425 [b'bundlespec'],
425 [b'bundlespec'],
426 _(b"Bundle File Formats"),
426 _(b"Bundle File Formats"),
427 loaddoc(b'bundlespec'),
427 loaddoc(b'bundlespec'),
428 TOPIC_CATEGORY_CONCEPTS,
428 TOPIC_CATEGORY_CONCEPTS,
429 ),
429 ),
430 (
430 (
431 [b'color'],
431 [b'color'],
432 _(b"Colorizing Outputs"),
432 _(b"Colorizing Outputs"),
433 loaddoc(b'color'),
433 loaddoc(b'color'),
434 TOPIC_CATEGORY_OUTPUT,
434 TOPIC_CATEGORY_OUTPUT,
435 ),
435 ),
436 (
436 (
437 [b"config", b"hgrc"],
437 [b"config", b"hgrc"],
438 _(b"Configuration Files"),
438 _(b"Configuration Files"),
439 loaddoc(b'config'),
439 loaddoc(b'config'),
440 TOPIC_CATEGORY_CONFIG,
440 TOPIC_CATEGORY_CONFIG,
441 ),
441 ),
442 (
442 (
443 [b'deprecated'],
443 [b'deprecated'],
444 _(b"Deprecated Features"),
444 _(b"Deprecated Features"),
445 loaddoc(b'deprecated'),
445 loaddoc(b'deprecated'),
446 TOPIC_CATEGORY_MISC,
446 TOPIC_CATEGORY_MISC,
447 ),
447 ),
448 (
448 (
449 [b"dates"],
449 [b"dates"],
450 _(b"Date Formats"),
450 _(b"Date Formats"),
451 loaddoc(b'dates'),
451 loaddoc(b'dates'),
452 TOPIC_CATEGORY_OUTPUT,
452 TOPIC_CATEGORY_OUTPUT,
453 ),
453 ),
454 (
454 (
455 [b"flags"],
455 [b"flags"],
456 _(b"Command-line flags"),
456 _(b"Command-line flags"),
457 loaddoc(b'flags'),
457 loaddoc(b'flags'),
458 TOPIC_CATEGORY_CONFIG,
458 TOPIC_CATEGORY_CONFIG,
459 ),
459 ),
460 (
460 (
461 [b"patterns"],
461 [b"patterns"],
462 _(b"File Name Patterns"),
462 _(b"File Name Patterns"),
463 loaddoc(b'patterns'),
463 loaddoc(b'patterns'),
464 TOPIC_CATEGORY_IDS,
464 TOPIC_CATEGORY_IDS,
465 ),
465 ),
466 (
466 (
467 [b'environment', b'env'],
467 [b'environment', b'env'],
468 _(b'Environment Variables'),
468 _(b'Environment Variables'),
469 loaddoc(b'environment'),
469 loaddoc(b'environment'),
470 TOPIC_CATEGORY_CONFIG,
470 TOPIC_CATEGORY_CONFIG,
471 ),
471 ),
472 (
472 (
473 [
473 [
474 b'revisions',
474 b'revisions',
475 b'revs',
475 b'revs',
476 b'revsets',
476 b'revsets',
477 b'revset',
477 b'revset',
478 b'multirevs',
478 b'multirevs',
479 b'mrevs',
479 b'mrevs',
480 ],
480 ],
481 _(b'Specifying Revisions'),
481 _(b'Specifying Revisions'),
482 loaddoc(b'revisions'),
482 loaddoc(b'revisions'),
483 TOPIC_CATEGORY_IDS,
483 TOPIC_CATEGORY_IDS,
484 ),
484 ),
485 (
485 (
486 [
486 [
487 b'rust',
487 b'rust',
488 b'rustext',
488 b'rustext',
489 b'rhg',
489 b'rhg',
490 ],
490 ],
491 _(b'Rust in Mercurial'),
491 _(b'Rust in Mercurial'),
492 loaddoc(b'rust'),
492 loaddoc(b'rust'),
493 TOPIC_CATEGORY_CONFIG,
493 TOPIC_CATEGORY_CONFIG,
494 ),
494 ),
495 (
495 (
496 [b'filesets', b'fileset'],
496 [b'filesets', b'fileset'],
497 _(b"Specifying File Sets"),
497 _(b"Specifying File Sets"),
498 loaddoc(b'filesets'),
498 loaddoc(b'filesets'),
499 TOPIC_CATEGORY_IDS,
499 TOPIC_CATEGORY_IDS,
500 ),
500 ),
501 (
501 (
502 [b'diffs'],
502 [b'diffs'],
503 _(b'Diff Formats'),
503 _(b'Diff Formats'),
504 loaddoc(b'diffs'),
504 loaddoc(b'diffs'),
505 TOPIC_CATEGORY_OUTPUT,
505 TOPIC_CATEGORY_OUTPUT,
506 ),
506 ),
507 (
507 (
508 [b'merge-tools', b'mergetools', b'mergetool'],
508 [b'merge-tools', b'mergetools', b'mergetool'],
509 _(b'Merge Tools'),
509 _(b'Merge Tools'),
510 loaddoc(b'merge-tools'),
510 loaddoc(b'merge-tools'),
511 TOPIC_CATEGORY_CONFIG,
511 TOPIC_CATEGORY_CONFIG,
512 ),
512 ),
513 (
513 (
514 [b'templating', b'templates', b'template', b'style'],
514 [b'templating', b'templates', b'template', b'style'],
515 _(b'Template Usage'),
515 _(b'Template Usage'),
516 loaddoc(b'templates'),
516 loaddoc(b'templates'),
517 TOPIC_CATEGORY_OUTPUT,
517 TOPIC_CATEGORY_OUTPUT,
518 ),
518 ),
519 ([b'urls'], _(b'URL Paths'), loaddoc(b'urls'), TOPIC_CATEGORY_IDS),
519 ([b'urls'], _(b'URL Paths'), loaddoc(b'urls'), TOPIC_CATEGORY_IDS),
520 (
520 (
521 [b"extensions"],
521 [b"extensions"],
522 _(b"Using Additional Features"),
522 _(b"Using Additional Features"),
523 extshelp,
523 extshelp,
524 TOPIC_CATEGORY_CONFIG,
524 TOPIC_CATEGORY_CONFIG,
525 ),
525 ),
526 (
526 (
527 [b"subrepos", b"subrepo"],
527 [b"subrepos", b"subrepo"],
528 _(b"Subrepositories"),
528 _(b"Subrepositories"),
529 loaddoc(b'subrepos'),
529 loaddoc(b'subrepos'),
530 TOPIC_CATEGORY_CONCEPTS,
530 TOPIC_CATEGORY_CONCEPTS,
531 ),
531 ),
532 (
532 (
533 [b"hgweb"],
533 [b"hgweb"],
534 _(b"Configuring hgweb"),
534 _(b"Configuring hgweb"),
535 loaddoc(b'hgweb'),
535 loaddoc(b'hgweb'),
536 TOPIC_CATEGORY_CONFIG,
536 TOPIC_CATEGORY_CONFIG,
537 ),
537 ),
538 (
538 (
539 [b"glossary"],
539 [b"glossary"],
540 _(b"Glossary"),
540 _(b"Glossary"),
541 loaddoc(b'glossary'),
541 loaddoc(b'glossary'),
542 TOPIC_CATEGORY_CONCEPTS,
542 TOPIC_CATEGORY_CONCEPTS,
543 ),
543 ),
544 (
544 (
545 [b"hgignore", b"ignore"],
545 [b"hgignore", b"ignore"],
546 _(b"Syntax for Mercurial Ignore Files"),
546 _(b"Syntax for Mercurial Ignore Files"),
547 loaddoc(b'hgignore'),
547 loaddoc(b'hgignore'),
548 TOPIC_CATEGORY_IDS,
548 TOPIC_CATEGORY_IDS,
549 ),
549 ),
550 (
550 (
551 [b"phases"],
551 [b"phases"],
552 _(b"Working with Phases"),
552 _(b"Working with Phases"),
553 loaddoc(b'phases'),
553 loaddoc(b'phases'),
554 TOPIC_CATEGORY_CONCEPTS,
554 TOPIC_CATEGORY_CONCEPTS,
555 ),
555 ),
556 (
556 (
557 [b"evolution"],
557 [b"evolution"],
558 _(b"Safely rewriting history (EXPERIMENTAL)"),
558 _(b"Safely rewriting history (EXPERIMENTAL)"),
559 loaddoc(b'evolution'),
559 loaddoc(b'evolution'),
560 TOPIC_CATEGORY_CONCEPTS,
560 TOPIC_CATEGORY_CONCEPTS,
561 ),
561 ),
562 (
562 (
563 [b'scripting'],
563 [b'scripting'],
564 _(b'Using Mercurial from scripts and automation'),
564 _(b'Using Mercurial from scripts and automation'),
565 loaddoc(b'scripting'),
565 loaddoc(b'scripting'),
566 TOPIC_CATEGORY_MISC,
566 TOPIC_CATEGORY_MISC,
567 ),
567 ),
568 (
568 (
569 [b'internals'],
569 [b'internals'],
570 _(b"Technical implementation topics"),
570 _(b"Technical implementation topics"),
571 internalshelp,
571 internalshelp,
572 TOPIC_CATEGORY_MISC,
572 TOPIC_CATEGORY_MISC,
573 ),
573 ),
574 (
574 (
575 [b'pager'],
575 [b'pager'],
576 _(b"Pager Support"),
576 _(b"Pager Support"),
577 loaddoc(b'pager'),
577 loaddoc(b'pager'),
578 TOPIC_CATEGORY_CONFIG,
578 TOPIC_CATEGORY_CONFIG,
579 ),
579 ),
580 ]
580 ]
581 )
581 )
582
582
583 # Maps topics with sub-topics to a list of their sub-topics.
583 # Maps topics with sub-topics to a list of their sub-topics.
584 subtopics = {
584 subtopics = {
585 b'internals': internalstable,
585 b'internals': internalstable,
586 }
586 }
587
587
588 # Map topics to lists of callable taking the current topic help and
588 # Map topics to lists of callable taking the current topic help and
589 # returning the updated version
589 # returning the updated version
590 helphooks = {}
590 helphooks = {}
591
591
592
592
593 def addtopichook(topic, rewriter):
593 def addtopichook(topic, rewriter):
594 helphooks.setdefault(topic, []).append(rewriter)
594 helphooks.setdefault(topic, []).append(rewriter)
595
595
596
596
597 def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
597 def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
598 """Extract docstring from the items key to function mapping, build a
598 """Extract docstring from the items key to function mapping, build a
599 single documentation block and use it to overwrite the marker in doc.
599 single documentation block and use it to overwrite the marker in doc.
600 """
600 """
601 entries = []
601 entries = []
602 for name in sorted(items):
602 for name in sorted(items):
603 text = (pycompat.getdoc(items[name]) or b'').rstrip()
603 text = (pycompat.getdoc(items[name]) or b'').rstrip()
604 if not text or not ui.verbose and any(w in text for w in _exclkeywords):
604 if not text or not ui.verbose and any(w in text for w in _exclkeywords):
605 continue
605 continue
606 text = gettext(text)
606 text = gettext(text)
607 if dedent:
607 if dedent:
608 # Abuse latin1 to use textwrap.dedent() on bytes.
608 # Abuse latin1 to use textwrap.dedent() on bytes.
609 text = textwrap.dedent(text.decode('latin1')).encode('latin1')
609 text = textwrap.dedent(text.decode('latin1')).encode('latin1')
610 lines = text.splitlines()
610 lines = text.splitlines()
611 doclines = [lines[0]]
611 doclines = [lines[0]]
612 for l in lines[1:]:
612 for l in lines[1:]:
613 # Stop once we find some Python doctest
613 # Stop once we find some Python doctest
614 if l.strip().startswith(b'>>>'):
614 if l.strip().startswith(b'>>>'):
615 break
615 break
616 if dedent:
616 if dedent:
617 doclines.append(l.rstrip())
617 doclines.append(l.rstrip())
618 else:
618 else:
619 doclines.append(b' ' + l.strip())
619 doclines.append(b' ' + l.strip())
620 entries.append(b'\n'.join(doclines))
620 entries.append(b'\n'.join(doclines))
621 entries = b'\n\n'.join(entries)
621 entries = b'\n\n'.join(entries)
622 return doc.replace(marker, entries)
622 return doc.replace(marker, entries)
623
623
624
624
625 def addtopicsymbols(topic, marker, symbols, dedent=False):
625 def addtopicsymbols(topic, marker, symbols, dedent=False):
626 def add(ui, topic, doc):
626 def add(ui, topic, doc):
627 return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
627 return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
628
628
629 addtopichook(topic, add)
629 addtopichook(topic, add)
630
630
631
631
632 addtopicsymbols(
632 addtopicsymbols(
633 b'bundlespec',
633 b'bundlespec',
634 b'.. bundlecompressionmarker',
634 b'.. bundlecompressionmarker',
635 compression.bundlecompressiontopics(),
635 compression.bundlecompressiontopics(),
636 )
636 )
637 addtopicsymbols(b'filesets', b'.. predicatesmarker', fileset.symbols)
637 addtopicsymbols(b'filesets', b'.. predicatesmarker', fileset.symbols)
638 addtopicsymbols(
638 addtopicsymbols(
639 b'merge-tools', b'.. internaltoolsmarker', filemerge.internalsdoc
639 b'merge-tools', b'.. internaltoolsmarker', filemerge.internalsdoc
640 )
640 )
641 addtopicsymbols(b'revisions', b'.. predicatesmarker', revset.symbols)
641 addtopicsymbols(b'revisions', b'.. predicatesmarker', revset.symbols)
642 addtopicsymbols(b'templates', b'.. keywordsmarker', templatekw.keywords)
642 addtopicsymbols(b'templates', b'.. keywordsmarker', templatekw.keywords)
643 addtopicsymbols(b'templates', b'.. filtersmarker', templatefilters.filters)
643 addtopicsymbols(b'templates', b'.. filtersmarker', templatefilters.filters)
644 addtopicsymbols(b'templates', b'.. functionsmarker', templatefuncs.funcs)
644 addtopicsymbols(b'templates', b'.. functionsmarker', templatefuncs.funcs)
645 addtopicsymbols(
645 addtopicsymbols(
646 b'hgweb', b'.. webcommandsmarker', webcommands.commands, dedent=True
646 b'hgweb', b'.. webcommandsmarker', webcommands.commands, dedent=True
647 )
647 )
648
648
649
649
650 def inserttweakrc(ui, topic, doc):
650 def inserttweakrc(ui, topic, doc):
651 marker = b'.. tweakdefaultsmarker'
651 marker = b'.. tweakdefaultsmarker'
652 repl = uimod.tweakrc
652 repl = uimod.tweakrc
653
653
654 def sub(m):
654 def sub(m):
655 lines = [m.group(1) + s for s in repl.splitlines()]
655 lines = [m.group(1) + s for s in repl.splitlines()]
656 return b'\n'.join(lines)
656 return b'\n'.join(lines)
657
657
658 return re.sub(br'( *)%s' % re.escape(marker), sub, doc)
658 return re.sub(br'( *)%s' % re.escape(marker), sub, doc)
659
659
660
660
661 def _getcategorizedhelpcmds(ui, cmdtable, name, select=None):
661 def _getcategorizedhelpcmds(ui, cmdtable, name, select=None):
662 # Category -> list of commands
662 # Category -> list of commands
663 cats = {}
663 cats = {}
664 # Command -> short description
664 # Command -> short description
665 h = {}
665 h = {}
666 # Command -> string showing synonyms
666 # Command -> string showing synonyms
667 syns = {}
667 syns = {}
668 for c, e in cmdtable.items():
668 for c, e in cmdtable.items():
669 fs = cmdutil.parsealiases(c)
669 fs = cmdutil.parsealiases(c)
670 f = fs[0]
670 f = fs[0]
671 syns[f] = fs
671 syns[f] = fs
672 func = e[0]
672 func = e[0]
673 if select and not select(f):
673 if select and not select(f):
674 continue
674 continue
675 doc = pycompat.getdoc(func)
675 doc = pycompat.getdoc(func)
676 if filtercmd(ui, f, func, name, doc):
676 if filtercmd(ui, f, func, name, doc):
677 continue
677 continue
678 doc = gettext(doc)
678 doc = gettext(doc)
679 if not doc:
679 if not doc:
680 doc = _(b"(no help text available)")
680 doc = _(b"(no help text available)")
681 h[f] = stringutil.firstline(doc).rstrip()
681 h[f] = stringutil.firstline(doc).rstrip()
682
682
683 cat = getattr(func, 'helpcategory', None) or (
683 cat = getattr(func, 'helpcategory', None) or (
684 registrar.command.CATEGORY_NONE
684 registrar.command.CATEGORY_NONE
685 )
685 )
686 cats.setdefault(cat, []).append(f)
686 cats.setdefault(cat, []).append(f)
687 return cats, h, syns
687 return cats, h, syns
688
688
689
689
690 def _getcategorizedhelptopics(ui, topictable):
690 def _getcategorizedhelptopics(ui, topictable):
691 # Group commands by category.
691 # Group commands by category.
692 topiccats = {}
692 topiccats = {}
693 syns = {}
693 syns = {}
694 for topic in topictable:
694 for topic in topictable:
695 names, header, doc = topic[0:3]
695 names, header, doc = topic[0:3]
696 if len(topic) > 3 and topic[3]:
696 if len(topic) > 3 and topic[3]:
697 category = topic[3]
697 category = topic[3]
698 else:
698 else:
699 category = TOPIC_CATEGORY_NONE
699 category = TOPIC_CATEGORY_NONE
700
700
701 topicname = names[0]
701 topicname = names[0]
702 syns[topicname] = list(names)
702 syns[topicname] = list(names)
703 if not filtertopic(ui, topicname):
703 if not filtertopic(ui, topicname):
704 topiccats.setdefault(category, []).append((topicname, header))
704 topiccats.setdefault(category, []).append((topicname, header))
705 return topiccats, syns
705 return topiccats, syns
706
706
707
707
708 addtopichook(b'config', inserttweakrc)
708 addtopichook(b'config', inserttweakrc)
709
709
710
710
711 def help_(
711 def help_(
712 ui,
712 ui,
713 commands,
713 commands,
714 name,
714 name,
715 unknowncmd=False,
715 unknowncmd=False,
716 full=True,
716 full=True,
717 subtopic=None,
717 subtopic=None,
718 fullname=None,
718 fullname=None,
719 **opts
719 **opts
720 ):
720 ):
721 """
721 """
722 Generate the help for 'name' as unformatted restructured text. If
722 Generate the help for 'name' as unformatted restructured text. If
723 'name' is None, describe the commands available.
723 'name' is None, describe the commands available.
724 """
724 """
725
725
726 opts = pycompat.byteskwargs(opts)
726 opts = pycompat.byteskwargs(opts)
727
727
728 def helpcmd(name, subtopic=None):
728 def helpcmd(name, subtopic=None):
729 try:
729 try:
730 aliases, entry = cmdutil.findcmd(
730 aliases, entry = cmdutil.findcmd(
731 name, commands.table, strict=unknowncmd
731 name, commands.table, strict=unknowncmd
732 )
732 )
733 except error.AmbiguousCommand as inst:
733 except error.AmbiguousCommand as inst:
734 # py3 fix: except vars can't be used outside the scope of the
734 # py3 fix: except vars can't be used outside the scope of the
735 # except block, nor can be used inside a lambda. python issue4617
735 # except block, nor can be used inside a lambda. python issue4617
736 prefix = inst.prefix
736 prefix = inst.prefix
737 select = lambda c: cmdutil.parsealiases(c)[0].startswith(prefix)
737 select = lambda c: cmdutil.parsealiases(c)[0].startswith(prefix)
738 rst = helplist(select)
738 rst = helplist(select)
739 return rst
739 return rst
740
740
741 rst = []
741 rst = []
742
742
743 # check if it's an invalid alias and display its error if it is
743 # check if it's an invalid alias and display its error if it is
744 if getattr(entry[0], 'badalias', None):
744 if getattr(entry[0], 'badalias', None):
745 rst.append(entry[0].badalias + b'\n')
745 rst.append(entry[0].badalias + b'\n')
746 if entry[0].unknowncmd:
746 if entry[0].unknowncmd:
747 try:
747 try:
748 rst.extend(helpextcmd(entry[0].cmdname))
748 rst.extend(helpextcmd(entry[0].cmdname))
749 except error.UnknownCommand:
749 except error.UnknownCommand:
750 pass
750 pass
751 return rst
751 return rst
752
752
753 # synopsis
753 # synopsis
754 if len(entry) > 2:
754 if len(entry) > 2:
755 if entry[2].startswith(b'hg'):
755 if entry[2].startswith(b'hg'):
756 rst.append(b"%s\n" % entry[2])
756 rst.append(b"%s\n" % entry[2])
757 else:
757 else:
758 rst.append(b'hg %s %s\n' % (aliases[0], entry[2]))
758 rst.append(b'hg %s %s\n' % (aliases[0], entry[2]))
759 else:
759 else:
760 rst.append(b'hg %s\n' % aliases[0])
760 rst.append(b'hg %s\n' % aliases[0])
761 # aliases
761 # aliases
762 if full and not ui.quiet and len(aliases) > 1:
762 if full and not ui.quiet and len(aliases) > 1:
763 rst.append(_(b"\naliases: %s\n") % b', '.join(aliases[1:]))
763 rst.append(_(b"\naliases: %s\n") % b', '.join(aliases[1:]))
764 rst.append(b'\n')
764 rst.append(b'\n')
765
765
766 # description
766 # description
767 doc = gettext(pycompat.getdoc(entry[0]))
767 doc = gettext(pycompat.getdoc(entry[0]))
768 if not doc:
768 if not doc:
769 doc = _(b"(no help text available)")
769 doc = _(b"(no help text available)")
770 if util.safehasattr(entry[0], b'definition'): # aliased command
770 if util.safehasattr(entry[0], b'definition'): # aliased command
771 source = entry[0].source
771 source = entry[0].source
772 if entry[0].definition.startswith(b'!'): # shell alias
772 if entry[0].definition.startswith(b'!'): # shell alias
773 doc = _(b'shell alias for: %s\n\n%s\n\ndefined by: %s\n') % (
773 doc = _(b'shell alias for: %s\n\n%s\n\ndefined by: %s\n') % (
774 entry[0].definition[1:],
774 entry[0].definition[1:],
775 doc,
775 doc,
776 source,
776 source,
777 )
777 )
778 else:
778 else:
779 doc = _(b'alias for: hg %s\n\n%s\n\ndefined by: %s\n') % (
779 doc = _(b'alias for: hg %s\n\n%s\n\ndefined by: %s\n') % (
780 entry[0].definition,
780 entry[0].definition,
781 doc,
781 doc,
782 source,
782 source,
783 )
783 )
784 doc = doc.splitlines(True)
784 doc = doc.splitlines(True)
785 if ui.quiet or not full:
785 if ui.quiet or not full:
786 rst.append(doc[0])
786 rst.append(doc[0])
787 else:
787 else:
788 rst.extend(doc)
788 rst.extend(doc)
789 rst.append(b'\n')
789 rst.append(b'\n')
790
790
791 # check if this command shadows a non-trivial (multi-line)
791 # check if this command shadows a non-trivial (multi-line)
792 # extension help text
792 # extension help text
793 try:
793 try:
794 mod = extensions.find(name)
794 mod = extensions.find(name)
795 doc = gettext(pycompat.getdoc(mod)) or b''
795 doc = gettext(pycompat.getdoc(mod)) or b''
796 if b'\n' in doc.strip():
796 if b'\n' in doc.strip():
797 msg = _(
797 msg = _(
798 b"(use 'hg help -e %s' to show help for "
798 b"(use 'hg help -e %s' to show help for "
799 b"the %s extension)"
799 b"the %s extension)"
800 ) % (name, name)
800 ) % (name, name)
801 rst.append(b'\n%s\n' % msg)
801 rst.append(b'\n%s\n' % msg)
802 except KeyError:
802 except KeyError:
803 pass
803 pass
804
804
805 # options
805 # options
806 if not ui.quiet and entry[1]:
806 if not ui.quiet and entry[1]:
807 rst.append(optrst(_(b"options"), entry[1], ui.verbose, ui))
807 rst.append(optrst(_(b"options"), entry[1], ui.verbose, ui))
808
808
809 if ui.verbose:
809 if ui.verbose:
810 rst.append(
810 rst.append(
811 optrst(
811 optrst(
812 _(b"global options"), commands.globalopts, ui.verbose, ui
812 _(b"global options"), commands.globalopts, ui.verbose, ui
813 )
813 )
814 )
814 )
815
815
816 if not ui.verbose:
816 if not ui.verbose:
817 if not full:
817 if not full:
818 rst.append(_(b"\n(use 'hg %s -h' to show more help)\n") % name)
818 rst.append(_(b"\n(use 'hg %s -h' to show more help)\n") % name)
819 elif not ui.quiet:
819 elif not ui.quiet:
820 rst.append(
820 rst.append(
821 _(
821 _(
822 b'\n(some details hidden, use --verbose '
822 b'\n(some details hidden, use --verbose '
823 b'to show complete help)'
823 b'to show complete help)'
824 )
824 )
825 )
825 )
826
826
827 return rst
827 return rst
828
828
829 def helplist(select=None, **opts):
829 def helplist(select=None, **opts):
830 cats, h, syns = _getcategorizedhelpcmds(
830 cats, h, syns = _getcategorizedhelpcmds(
831 ui, commands.table, name, select
831 ui, commands.table, name, select
832 )
832 )
833
833
834 rst = []
834 rst = []
835 if not h:
835 if not h:
836 if not ui.quiet:
836 if not ui.quiet:
837 rst.append(_(b'no commands defined\n'))
837 rst.append(_(b'no commands defined\n'))
838 return rst
838 return rst
839
839
840 # Output top header.
840 # Output top header.
841 if not ui.quiet:
841 if not ui.quiet:
842 if name == b"shortlist":
842 if name == b"shortlist":
843 rst.append(_(b'basic commands:\n\n'))
843 rst.append(_(b'basic commands:\n\n'))
844 elif name == b"debug":
844 elif name == b"debug":
845 rst.append(_(b'debug commands (internal and unsupported):\n\n'))
845 rst.append(_(b'debug commands (internal and unsupported):\n\n'))
846 else:
846 else:
847 rst.append(_(b'list of commands:\n'))
847 rst.append(_(b'list of commands:\n'))
848
848
849 def appendcmds(cmds):
849 def appendcmds(cmds):
850 cmds = sorted(cmds)
850 cmds = sorted(cmds)
851 for c in cmds:
851 for c in cmds:
852 display_cmd = c
852 display_cmd = c
853 if ui.verbose:
853 if ui.verbose:
854 display_cmd = b', '.join(syns[c])
854 display_cmd = b', '.join(syns[c])
855 display_cmd = display_cmd.replace(b':', br'\:')
855 display_cmd = display_cmd.replace(b':', br'\:')
856 rst.append(b' :%s: %s\n' % (display_cmd, h[c]))
856 rst.append(b' :%s: %s\n' % (display_cmd, h[c]))
857
857
858 if name in (b'shortlist', b'debug'):
858 if name in (b'shortlist', b'debug'):
859 # List without categories.
859 # List without categories.
860 appendcmds(h)
860 appendcmds(h)
861 else:
861 else:
862 # Check that all categories have an order.
862 # Check that all categories have an order.
863 missing_order = set(cats.keys()) - set(CATEGORY_ORDER)
863 missing_order = set(cats.keys()) - set(CATEGORY_ORDER)
864 if missing_order:
864 if missing_order:
865 ui.develwarn(
865 ui.develwarn(
866 b'help categories missing from CATEGORY_ORDER: %s'
866 b'help categories missing from CATEGORY_ORDER: %s'
867 % missing_order
867 % stringutil.forcebytestr(missing_order)
868 )
868 )
869
869
870 # List per category.
870 # List per category.
871 for cat in CATEGORY_ORDER:
871 for cat in CATEGORY_ORDER:
872 catfns = cats.get(cat, [])
872 catfns = cats.get(cat, [])
873 if catfns:
873 if catfns:
874 if len(cats) > 1:
874 if len(cats) > 1:
875 catname = gettext(CATEGORY_NAMES[cat])
875 catname = gettext(CATEGORY_NAMES[cat])
876 rst.append(b"\n%s:\n" % catname)
876 rst.append(b"\n%s:\n" % catname)
877 rst.append(b"\n")
877 rst.append(b"\n")
878 appendcmds(catfns)
878 appendcmds(catfns)
879
879
880 ex = opts.get
880 ex = opts.get
881 anyopts = ex('keyword') or not (ex('command') or ex('extension'))
881 anyopts = ex('keyword') or not (ex('command') or ex('extension'))
882 if not name and anyopts:
882 if not name and anyopts:
883 exts = listexts(
883 exts = listexts(
884 _(b'enabled extensions:'),
884 _(b'enabled extensions:'),
885 extensions.enabled(),
885 extensions.enabled(),
886 showdeprecated=ui.verbose,
886 showdeprecated=ui.verbose,
887 )
887 )
888 if exts:
888 if exts:
889 rst.append(b'\n')
889 rst.append(b'\n')
890 rst.extend(exts)
890 rst.extend(exts)
891
891
892 rst.append(_(b"\nadditional help topics:\n"))
892 rst.append(_(b"\nadditional help topics:\n"))
893 topiccats, topicsyns = _getcategorizedhelptopics(ui, helptable)
893 topiccats, topicsyns = _getcategorizedhelptopics(ui, helptable)
894
894
895 # Check that all categories have an order.
895 # Check that all categories have an order.
896 missing_order = set(topiccats.keys()) - set(TOPIC_CATEGORY_ORDER)
896 missing_order = set(topiccats.keys()) - set(TOPIC_CATEGORY_ORDER)
897 if missing_order:
897 if missing_order:
898 ui.develwarn(
898 ui.develwarn(
899 b'help categories missing from TOPIC_CATEGORY_ORDER: %s'
899 b'help categories missing from TOPIC_CATEGORY_ORDER: %s'
900 % missing_order
900 % stringutil.forcebytestr(missing_order)
901 )
901 )
902
902
903 # Output topics per category.
903 # Output topics per category.
904 for cat in TOPIC_CATEGORY_ORDER:
904 for cat in TOPIC_CATEGORY_ORDER:
905 topics = topiccats.get(cat, [])
905 topics = topiccats.get(cat, [])
906 if topics:
906 if topics:
907 if len(topiccats) > 1:
907 if len(topiccats) > 1:
908 catname = gettext(TOPIC_CATEGORY_NAMES[cat])
908 catname = gettext(TOPIC_CATEGORY_NAMES[cat])
909 rst.append(b"\n%s:\n" % catname)
909 rst.append(b"\n%s:\n" % catname)
910 rst.append(b"\n")
910 rst.append(b"\n")
911 for t, desc in topics:
911 for t, desc in topics:
912 rst.append(b" :%s: %s\n" % (t, desc))
912 rst.append(b" :%s: %s\n" % (t, desc))
913
913
914 if ui.quiet:
914 if ui.quiet:
915 pass
915 pass
916 elif ui.verbose:
916 elif ui.verbose:
917 rst.append(
917 rst.append(
918 b'\n%s\n'
918 b'\n%s\n'
919 % optrst(
919 % optrst(
920 _(b"global options"), commands.globalopts, ui.verbose, ui
920 _(b"global options"), commands.globalopts, ui.verbose, ui
921 )
921 )
922 )
922 )
923 if name == b'shortlist':
923 if name == b'shortlist':
924 rst.append(
924 rst.append(
925 _(b"\n(use 'hg help' for the full list of commands)\n")
925 _(b"\n(use 'hg help' for the full list of commands)\n")
926 )
926 )
927 else:
927 else:
928 if name == b'shortlist':
928 if name == b'shortlist':
929 rst.append(
929 rst.append(
930 _(
930 _(
931 b"\n(use 'hg help' for the full list of commands "
931 b"\n(use 'hg help' for the full list of commands "
932 b"or 'hg -v' for details)\n"
932 b"or 'hg -v' for details)\n"
933 )
933 )
934 )
934 )
935 elif name and not full:
935 elif name and not full:
936 rst.append(
936 rst.append(
937 _(b"\n(use 'hg help %s' to show the full help text)\n")
937 _(b"\n(use 'hg help %s' to show the full help text)\n")
938 % name
938 % name
939 )
939 )
940 elif name and syns and name in syns.keys():
940 elif name and syns and name in syns.keys():
941 rst.append(
941 rst.append(
942 _(
942 _(
943 b"\n(use 'hg help -v -e %s' to show built-in "
943 b"\n(use 'hg help -v -e %s' to show built-in "
944 b"aliases and global options)\n"
944 b"aliases and global options)\n"
945 )
945 )
946 % name
946 % name
947 )
947 )
948 else:
948 else:
949 rst.append(
949 rst.append(
950 _(
950 _(
951 b"\n(use 'hg help -v%s' to show built-in aliases "
951 b"\n(use 'hg help -v%s' to show built-in aliases "
952 b"and global options)\n"
952 b"and global options)\n"
953 )
953 )
954 % (name and b" " + name or b"")
954 % (name and b" " + name or b"")
955 )
955 )
956 return rst
956 return rst
957
957
958 def helptopic(name, subtopic=None):
958 def helptopic(name, subtopic=None):
959 # Look for sub-topic entry first.
959 # Look for sub-topic entry first.
960 header, doc = None, None
960 header, doc = None, None
961 if subtopic and name in subtopics:
961 if subtopic and name in subtopics:
962 for names, header, doc in subtopics[name]:
962 for names, header, doc in subtopics[name]:
963 if subtopic in names:
963 if subtopic in names:
964 break
964 break
965 if not any(subtopic in s[0] for s in subtopics[name]):
965 if not any(subtopic in s[0] for s in subtopics[name]):
966 raise error.UnknownCommand(name)
966 raise error.UnknownCommand(name)
967
967
968 if not header:
968 if not header:
969 for topic in helptable:
969 for topic in helptable:
970 names, header, doc = topic[0:3]
970 names, header, doc = topic[0:3]
971 if name in names:
971 if name in names:
972 break
972 break
973 else:
973 else:
974 raise error.UnknownCommand(name)
974 raise error.UnknownCommand(name)
975
975
976 rst = [minirst.section(header)]
976 rst = [minirst.section(header)]
977
977
978 # description
978 # description
979 if not doc:
979 if not doc:
980 rst.append(b" %s\n" % _(b"(no help text available)"))
980 rst.append(b" %s\n" % _(b"(no help text available)"))
981 if callable(doc):
981 if callable(doc):
982 rst += [b" %s\n" % l for l in doc(ui).splitlines()]
982 rst += [b" %s\n" % l for l in doc(ui).splitlines()]
983
983
984 if not ui.verbose:
984 if not ui.verbose:
985 omitted = _(
985 omitted = _(
986 b'(some details hidden, use --verbose'
986 b'(some details hidden, use --verbose'
987 b' to show complete help)'
987 b' to show complete help)'
988 )
988 )
989 indicateomitted(rst, omitted)
989 indicateomitted(rst, omitted)
990
990
991 try:
991 try:
992 cmdutil.findcmd(name, commands.table)
992 cmdutil.findcmd(name, commands.table)
993 rst.append(
993 rst.append(
994 _(b"\nuse 'hg help -c %s' to see help for the %s command\n")
994 _(b"\nuse 'hg help -c %s' to see help for the %s command\n")
995 % (name, name)
995 % (name, name)
996 )
996 )
997 except error.UnknownCommand:
997 except error.UnknownCommand:
998 pass
998 pass
999 return rst
999 return rst
1000
1000
1001 def helpext(name, subtopic=None):
1001 def helpext(name, subtopic=None):
1002 try:
1002 try:
1003 mod = extensions.find(name)
1003 mod = extensions.find(name)
1004 doc = gettext(pycompat.getdoc(mod)) or _(b'no help text available')
1004 doc = gettext(pycompat.getdoc(mod)) or _(b'no help text available')
1005 except KeyError:
1005 except KeyError:
1006 mod = None
1006 mod = None
1007 doc = extensions.disabled_help(name)
1007 doc = extensions.disabled_help(name)
1008 if not doc:
1008 if not doc:
1009 raise error.UnknownCommand(name)
1009 raise error.UnknownCommand(name)
1010
1010
1011 if b'\n' not in doc:
1011 if b'\n' not in doc:
1012 head, tail = doc, b""
1012 head, tail = doc, b""
1013 else:
1013 else:
1014 head, tail = doc.split(b'\n', 1)
1014 head, tail = doc.split(b'\n', 1)
1015 rst = [_(b'%s extension - %s\n\n') % (name.rpartition(b'.')[-1], head)]
1015 rst = [_(b'%s extension - %s\n\n') % (name.rpartition(b'.')[-1], head)]
1016 if tail:
1016 if tail:
1017 rst.extend(tail.splitlines(True))
1017 rst.extend(tail.splitlines(True))
1018 rst.append(b'\n')
1018 rst.append(b'\n')
1019
1019
1020 if not ui.verbose:
1020 if not ui.verbose:
1021 omitted = _(
1021 omitted = _(
1022 b'(some details hidden, use --verbose'
1022 b'(some details hidden, use --verbose'
1023 b' to show complete help)'
1023 b' to show complete help)'
1024 )
1024 )
1025 indicateomitted(rst, omitted)
1025 indicateomitted(rst, omitted)
1026
1026
1027 if mod:
1027 if mod:
1028 try:
1028 try:
1029 ct = mod.cmdtable
1029 ct = mod.cmdtable
1030 except AttributeError:
1030 except AttributeError:
1031 ct = {}
1031 ct = {}
1032 modcmds = {c.partition(b'|')[0] for c in ct}
1032 modcmds = {c.partition(b'|')[0] for c in ct}
1033 rst.extend(helplist(modcmds.__contains__))
1033 rst.extend(helplist(modcmds.__contains__))
1034 else:
1034 else:
1035 rst.append(
1035 rst.append(
1036 _(
1036 _(
1037 b"(use 'hg help extensions' for information on enabling"
1037 b"(use 'hg help extensions' for information on enabling"
1038 b" extensions)\n"
1038 b" extensions)\n"
1039 )
1039 )
1040 )
1040 )
1041 return rst
1041 return rst
1042
1042
1043 def helpextcmd(name, subtopic=None):
1043 def helpextcmd(name, subtopic=None):
1044 cmd, ext, doc = extensions.disabledcmd(
1044 cmd, ext, doc = extensions.disabledcmd(
1045 ui, name, ui.configbool(b'ui', b'strict')
1045 ui, name, ui.configbool(b'ui', b'strict')
1046 )
1046 )
1047 doc = stringutil.firstline(doc)
1047 doc = stringutil.firstline(doc)
1048
1048
1049 rst = listexts(
1049 rst = listexts(
1050 _(b"'%s' is provided by the following extension:") % cmd,
1050 _(b"'%s' is provided by the following extension:") % cmd,
1051 {ext: doc},
1051 {ext: doc},
1052 indent=4,
1052 indent=4,
1053 showdeprecated=True,
1053 showdeprecated=True,
1054 )
1054 )
1055 rst.append(b'\n')
1055 rst.append(b'\n')
1056 rst.append(
1056 rst.append(
1057 _(
1057 _(
1058 b"(use 'hg help extensions' for information on enabling "
1058 b"(use 'hg help extensions' for information on enabling "
1059 b"extensions)\n"
1059 b"extensions)\n"
1060 )
1060 )
1061 )
1061 )
1062 return rst
1062 return rst
1063
1063
1064 rst = []
1064 rst = []
1065 kw = opts.get(b'keyword')
1065 kw = opts.get(b'keyword')
1066 if kw or name is None and any(opts[o] for o in opts):
1066 if kw or name is None and any(opts[o] for o in opts):
1067 matches = topicmatch(ui, commands, name or b'')
1067 matches = topicmatch(ui, commands, name or b'')
1068 helpareas = []
1068 helpareas = []
1069 if opts.get(b'extension'):
1069 if opts.get(b'extension'):
1070 helpareas += [(b'extensions', _(b'Extensions'))]
1070 helpareas += [(b'extensions', _(b'Extensions'))]
1071 if opts.get(b'command'):
1071 if opts.get(b'command'):
1072 helpareas += [(b'commands', _(b'Commands'))]
1072 helpareas += [(b'commands', _(b'Commands'))]
1073 if not helpareas:
1073 if not helpareas:
1074 helpareas = [
1074 helpareas = [
1075 (b'topics', _(b'Topics')),
1075 (b'topics', _(b'Topics')),
1076 (b'commands', _(b'Commands')),
1076 (b'commands', _(b'Commands')),
1077 (b'extensions', _(b'Extensions')),
1077 (b'extensions', _(b'Extensions')),
1078 (b'extensioncommands', _(b'Extension Commands')),
1078 (b'extensioncommands', _(b'Extension Commands')),
1079 ]
1079 ]
1080 for t, title in helpareas:
1080 for t, title in helpareas:
1081 if matches[t]:
1081 if matches[t]:
1082 rst.append(b'%s:\n\n' % title)
1082 rst.append(b'%s:\n\n' % title)
1083 rst.extend(minirst.maketable(sorted(matches[t]), 1))
1083 rst.extend(minirst.maketable(sorted(matches[t]), 1))
1084 rst.append(b'\n')
1084 rst.append(b'\n')
1085 if not rst:
1085 if not rst:
1086 msg = _(b'no matches')
1086 msg = _(b'no matches')
1087 hint = _(b"try 'hg help' for a list of topics")
1087 hint = _(b"try 'hg help' for a list of topics")
1088 raise error.InputError(msg, hint=hint)
1088 raise error.InputError(msg, hint=hint)
1089 elif name and name != b'shortlist':
1089 elif name and name != b'shortlist':
1090 queries = []
1090 queries = []
1091 if unknowncmd:
1091 if unknowncmd:
1092 queries += [helpextcmd]
1092 queries += [helpextcmd]
1093 if opts.get(b'extension'):
1093 if opts.get(b'extension'):
1094 queries += [helpext]
1094 queries += [helpext]
1095 if opts.get(b'command'):
1095 if opts.get(b'command'):
1096 queries += [helpcmd]
1096 queries += [helpcmd]
1097 if not queries:
1097 if not queries:
1098 queries = (helptopic, helpcmd, helpext, helpextcmd)
1098 queries = (helptopic, helpcmd, helpext, helpextcmd)
1099 for f in queries:
1099 for f in queries:
1100 try:
1100 try:
1101 rst = f(name, subtopic)
1101 rst = f(name, subtopic)
1102 break
1102 break
1103 except error.UnknownCommand:
1103 except error.UnknownCommand:
1104 pass
1104 pass
1105 else:
1105 else:
1106 if unknowncmd:
1106 if unknowncmd:
1107 raise error.UnknownCommand(name)
1107 raise error.UnknownCommand(name)
1108 else:
1108 else:
1109 if fullname:
1109 if fullname:
1110 formatname = fullname
1110 formatname = fullname
1111 else:
1111 else:
1112 formatname = name
1112 formatname = name
1113 if subtopic:
1113 if subtopic:
1114 hintname = subtopic
1114 hintname = subtopic
1115 else:
1115 else:
1116 hintname = name
1116 hintname = name
1117 msg = _(b'no such help topic: %s') % formatname
1117 msg = _(b'no such help topic: %s') % formatname
1118 hint = _(b"try 'hg help --keyword %s'") % hintname
1118 hint = _(b"try 'hg help --keyword %s'") % hintname
1119 raise error.InputError(msg, hint=hint)
1119 raise error.InputError(msg, hint=hint)
1120 else:
1120 else:
1121 # program name
1121 # program name
1122 if not ui.quiet:
1122 if not ui.quiet:
1123 rst = [_(b"Mercurial Distributed SCM\n"), b'\n']
1123 rst = [_(b"Mercurial Distributed SCM\n"), b'\n']
1124 rst.extend(helplist(None, **pycompat.strkwargs(opts)))
1124 rst.extend(helplist(None, **pycompat.strkwargs(opts)))
1125
1125
1126 return b''.join(rst)
1126 return b''.join(rst)
1127
1127
1128
1128
1129 def formattedhelp(
1129 def formattedhelp(
1130 ui, commands, fullname, keep=None, unknowncmd=False, full=True, **opts
1130 ui, commands, fullname, keep=None, unknowncmd=False, full=True, **opts
1131 ):
1131 ):
1132 """get help for a given topic (as a dotted name) as rendered rst
1132 """get help for a given topic (as a dotted name) as rendered rst
1133
1133
1134 Either returns the rendered help text or raises an exception.
1134 Either returns the rendered help text or raises an exception.
1135 """
1135 """
1136 if keep is None:
1136 if keep is None:
1137 keep = []
1137 keep = []
1138 else:
1138 else:
1139 keep = list(keep) # make a copy so we can mutate this later
1139 keep = list(keep) # make a copy so we can mutate this later
1140
1140
1141 # <fullname> := <name>[.<subtopic][.<section>]
1141 # <fullname> := <name>[.<subtopic][.<section>]
1142 name = subtopic = section = None
1142 name = subtopic = section = None
1143 if fullname is not None:
1143 if fullname is not None:
1144 nameparts = fullname.split(b'.')
1144 nameparts = fullname.split(b'.')
1145 name = nameparts.pop(0)
1145 name = nameparts.pop(0)
1146 if nameparts and name in subtopics:
1146 if nameparts and name in subtopics:
1147 subtopic = nameparts.pop(0)
1147 subtopic = nameparts.pop(0)
1148 if nameparts:
1148 if nameparts:
1149 section = encoding.lower(b'.'.join(nameparts))
1149 section = encoding.lower(b'.'.join(nameparts))
1150
1150
1151 textwidth = ui.configint(b'ui', b'textwidth')
1151 textwidth = ui.configint(b'ui', b'textwidth')
1152 termwidth = ui.termwidth() - 2
1152 termwidth = ui.termwidth() - 2
1153 if textwidth <= 0 or termwidth < textwidth:
1153 if textwidth <= 0 or termwidth < textwidth:
1154 textwidth = termwidth
1154 textwidth = termwidth
1155 text = help_(
1155 text = help_(
1156 ui,
1156 ui,
1157 commands,
1157 commands,
1158 name,
1158 name,
1159 fullname=fullname,
1159 fullname=fullname,
1160 subtopic=subtopic,
1160 subtopic=subtopic,
1161 unknowncmd=unknowncmd,
1161 unknowncmd=unknowncmd,
1162 full=full,
1162 full=full,
1163 **opts
1163 **opts
1164 )
1164 )
1165
1165
1166 blocks, pruned = minirst.parse(text, keep=keep)
1166 blocks, pruned = minirst.parse(text, keep=keep)
1167 if b'verbose' in pruned:
1167 if b'verbose' in pruned:
1168 keep.append(b'omitted')
1168 keep.append(b'omitted')
1169 else:
1169 else:
1170 keep.append(b'notomitted')
1170 keep.append(b'notomitted')
1171 blocks, pruned = minirst.parse(text, keep=keep)
1171 blocks, pruned = minirst.parse(text, keep=keep)
1172 if section:
1172 if section:
1173 blocks = minirst.filtersections(blocks, section)
1173 blocks = minirst.filtersections(blocks, section)
1174
1174
1175 # We could have been given a weird ".foo" section without a name
1175 # We could have been given a weird ".foo" section without a name
1176 # to look for, or we could have simply failed to found "foo.bar"
1176 # to look for, or we could have simply failed to found "foo.bar"
1177 # because bar isn't a section of foo
1177 # because bar isn't a section of foo
1178 if section and not (blocks and name):
1178 if section and not (blocks and name):
1179 raise error.InputError(_(b"help section not found: %s") % fullname)
1179 raise error.InputError(_(b"help section not found: %s") % fullname)
1180
1180
1181 return minirst.formatplain(blocks, textwidth)
1181 return minirst.formatplain(blocks, textwidth)
General Comments 0
You need to be logged in to leave comments. Login now