mercurial/help.py
changeset 43076 2372284d9457
parent 42450 9d31581cc44e
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    32     templatefuncs,
    32     templatefuncs,
    33     templatekw,
    33     templatekw,
    34     ui as uimod,
    34     ui as uimod,
    35     util,
    35     util,
    36 )
    36 )
    37 from .hgweb import (
    37 from .hgweb import webcommands
    38     webcommands,
    38 from .utils import compression
    39 )
       
    40 from .utils import (
       
    41     compression,
       
    42 )
       
    43 
    39 
    44 _exclkeywords = {
    40 _exclkeywords = {
    45     "(ADVANCED)",
    41     "(ADVANCED)",
    46     "(DEPRECATED)",
    42     "(DEPRECATED)",
    47     "(EXPERIMENTAL)",
    43     "(EXPERIMENTAL)",
    62     registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT,
    58     registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT,
    63     registrar.command.CATEGORY_COMMITTING,
    59     registrar.command.CATEGORY_COMMITTING,
    64     registrar.command.CATEGORY_CHANGE_MANAGEMENT,
    60     registrar.command.CATEGORY_CHANGE_MANAGEMENT,
    65     registrar.command.CATEGORY_CHANGE_ORGANIZATION,
    61     registrar.command.CATEGORY_CHANGE_ORGANIZATION,
    66     registrar.command.CATEGORY_FILE_CONTENTS,
    62     registrar.command.CATEGORY_FILE_CONTENTS,
    67     registrar.command.CATEGORY_CHANGE_NAVIGATION ,
    63     registrar.command.CATEGORY_CHANGE_NAVIGATION,
    68     registrar.command.CATEGORY_WORKING_DIRECTORY,
    64     registrar.command.CATEGORY_WORKING_DIRECTORY,
    69     registrar.command.CATEGORY_IMPORT_EXPORT,
    65     registrar.command.CATEGORY_IMPORT_EXPORT,
    70     registrar.command.CATEGORY_MAINTENANCE,
    66     registrar.command.CATEGORY_MAINTENANCE,
    71     registrar.command.CATEGORY_HELP,
    67     registrar.command.CATEGORY_HELP,
    72     registrar.command.CATEGORY_MISC,
    68     registrar.command.CATEGORY_MISC,
    75 
    71 
    76 # Human-readable category names. These are translated.
    72 # Human-readable category names. These are translated.
    77 # Extensions with custom categories should add their names here.
    73 # Extensions with custom categories should add their names here.
    78 CATEGORY_NAMES = {
    74 CATEGORY_NAMES = {
    79     registrar.command.CATEGORY_REPO_CREATION: 'Repository creation',
    75     registrar.command.CATEGORY_REPO_CREATION: 'Repository creation',
    80     registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT:
    76     registrar.command.CATEGORY_REMOTE_REPO_MANAGEMENT: 'Remote repository management',
    81         'Remote repository management',
       
    82     registrar.command.CATEGORY_COMMITTING: 'Change creation',
    77     registrar.command.CATEGORY_COMMITTING: 'Change creation',
    83     registrar.command.CATEGORY_CHANGE_NAVIGATION: 'Change navigation',
    78     registrar.command.CATEGORY_CHANGE_NAVIGATION: 'Change navigation',
    84     registrar.command.CATEGORY_CHANGE_MANAGEMENT: 'Change manipulation',
    79     registrar.command.CATEGORY_CHANGE_MANAGEMENT: 'Change manipulation',
    85     registrar.command.CATEGORY_CHANGE_ORGANIZATION: 'Change organization',
    80     registrar.command.CATEGORY_CHANGE_ORGANIZATION: 'Change organization',
    86     registrar.command.CATEGORY_WORKING_DIRECTORY:
    81     registrar.command.CATEGORY_WORKING_DIRECTORY: 'Working directory management',
    87         'Working directory management',
       
    88     registrar.command.CATEGORY_FILE_CONTENTS: 'File content management',
    82     registrar.command.CATEGORY_FILE_CONTENTS: 'File content management',
    89     registrar.command.CATEGORY_IMPORT_EXPORT: 'Change import/export',
    83     registrar.command.CATEGORY_IMPORT_EXPORT: 'Change import/export',
    90     registrar.command.CATEGORY_MAINTENANCE: 'Repository maintenance',
    84     registrar.command.CATEGORY_MAINTENANCE: 'Repository maintenance',
    91     registrar.command.CATEGORY_HELP: 'Help',
    85     registrar.command.CATEGORY_HELP: 'Help',
    92     registrar.command.CATEGORY_MISC: 'Miscellaneous commands',
    86     registrar.command.CATEGORY_MISC: 'Miscellaneous commands',
   122     TOPIC_CATEGORY_CONCEPTS: 'Concepts',
   116     TOPIC_CATEGORY_CONCEPTS: 'Concepts',
   123     TOPIC_CATEGORY_MISC: 'Miscellaneous',
   117     TOPIC_CATEGORY_MISC: 'Miscellaneous',
   124     TOPIC_CATEGORY_NONE: 'Uncategorized topics',
   118     TOPIC_CATEGORY_NONE: 'Uncategorized topics',
   125 }
   119 }
   126 
   120 
       
   121 
   127 def listexts(header, exts, indent=1, showdeprecated=False):
   122 def listexts(header, exts, indent=1, showdeprecated=False):
   128     '''return a text listing of the given extensions'''
   123     '''return a text listing of the given extensions'''
   129     rst = []
   124     rst = []
   130     if exts:
   125     if exts:
   131         for name, desc in sorted(exts.iteritems()):
   126         for name, desc in sorted(exts.iteritems()):
   134             rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
   129             rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
   135     if rst:
   130     if rst:
   136         rst.insert(0, '\n%s\n\n' % header)
   131         rst.insert(0, '\n%s\n\n' % header)
   137     return rst
   132     return rst
   138 
   133 
       
   134 
   139 def extshelp(ui):
   135 def extshelp(ui):
   140     rst = loaddoc('extensions')(ui).splitlines(True)
   136     rst = loaddoc('extensions')(ui).splitlines(True)
   141     rst.extend(listexts(
   137     rst.extend(
   142         _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
   138         listexts(
   143     rst.extend(listexts(_('disabled extensions:'), extensions.disabled(),
   139             _('enabled extensions:'), extensions.enabled(), showdeprecated=True
   144                         showdeprecated=ui.verbose))
   140         )
       
   141     )
       
   142     rst.extend(
       
   143         listexts(
       
   144             _('disabled extensions:'),
       
   145             extensions.disabled(),
       
   146             showdeprecated=ui.verbose,
       
   147         )
       
   148     )
   145     doc = ''.join(rst)
   149     doc = ''.join(rst)
   146     return doc
   150     return doc
       
   151 
   147 
   152 
   148 def optrst(header, options, verbose):
   153 def optrst(header, options, verbose):
   149     data = []
   154     data = []
   150     multioccur = False
   155     multioccur = False
   151     for option in options:
   156     for option in options:
   152         if len(option) == 5:
   157         if len(option) == 5:
   153             shortopt, longopt, default, desc, optlabel = option
   158             shortopt, longopt, default, desc, optlabel = option
   154         else:
   159         else:
   155             shortopt, longopt, default, desc = option
   160             shortopt, longopt, default, desc = option
   156             optlabel = _("VALUE") # default label
   161             optlabel = _("VALUE")  # default label
   157 
   162 
   158         if not verbose and any(w in desc for w in _exclkeywords):
   163         if not verbose and any(w in desc for w in _exclkeywords):
   159             continue
   164             continue
   160 
   165 
   161         so = ''
   166         so = ''
   184             lo += " %s" % optlabel
   189             lo += " %s" % optlabel
   185 
   190 
   186         data.append((so, lo, desc))
   191         data.append((so, lo, desc))
   187 
   192 
   188     if multioccur:
   193     if multioccur:
   189         header += (_(" ([+] can be repeated)"))
   194         header += _(" ([+] can be repeated)")
   190 
   195 
   191     rst = ['\n%s:\n\n' % header]
   196     rst = ['\n%s:\n\n' % header]
   192     rst.extend(minirst.maketable(data, 1))
   197     rst.extend(minirst.maketable(data, 1))
   193 
   198 
   194     return ''.join(rst)
   199     return ''.join(rst)
       
   200 
   195 
   201 
   196 def indicateomitted(rst, omitted, notomitted=None):
   202 def indicateomitted(rst, omitted, notomitted=None):
   197     rst.append('\n\n.. container:: omitted\n\n    %s\n\n' % omitted)
   203     rst.append('\n\n.. container:: omitted\n\n    %s\n\n' % omitted)
   198     if notomitted:
   204     if notomitted:
   199         rst.append('\n\n.. container:: notomitted\n\n    %s\n\n' % notomitted)
   205         rst.append('\n\n.. container:: notomitted\n\n    %s\n\n' % notomitted)
       
   206 
   200 
   207 
   201 def filtercmd(ui, cmd, func, kw, doc):
   208 def filtercmd(ui, cmd, func, kw, doc):
   202     if not ui.debugflag and cmd.startswith("debug") and kw != "debug":
   209     if not ui.debugflag and cmd.startswith("debug") and kw != "debug":
   203         # Debug command, and user is not looking for those.
   210         # Debug command, and user is not looking for those.
   204         return True
   211         return True
   218     if ui.configbool('help', 'hidden-command.%s' % cmd):
   225     if ui.configbool('help', 'hidden-command.%s' % cmd):
   219         # Configuration explicitly hides the command.
   226         # Configuration explicitly hides the command.
   220         return True
   227         return True
   221     return False
   228     return False
   222 
   229 
       
   230 
   223 def filtertopic(ui, topic):
   231 def filtertopic(ui, topic):
   224     return ui.configbool('help', 'hidden-topic.%s' % topic, False)
   232     return ui.configbool('help', 'hidden-topic.%s' % topic, False)
       
   233 
   225 
   234 
   226 def topicmatch(ui, commands, kw):
   235 def topicmatch(ui, commands, kw):
   227     """Return help topics matching kw.
   236     """Return help topics matching kw.
   228 
   237 
   229     Returns {'section': [(name, summary), ...], ...} where section is
   238     Returns {'section': [(name, summary), ...], ...} where section is
   230     one of topics, commands, extensions, or extensioncommands.
   239     one of topics, commands, extensions, or extensioncommands.
   231     """
   240     """
   232     kw = encoding.lower(kw)
   241     kw = encoding.lower(kw)
       
   242 
   233     def lowercontains(container):
   243     def lowercontains(container):
   234         return kw in encoding.lower(container)  # translated in helptable
   244         return kw in encoding.lower(container)  # translated in helptable
   235     results = {'topics': [],
   245 
   236                'commands': [],
   246     results = {
   237                'extensions': [],
   247         'topics': [],
   238                'extensioncommands': [],
   248         'commands': [],
   239                }
   249         'extensions': [],
       
   250         'extensioncommands': [],
       
   251     }
   240     for topic in helptable:
   252     for topic in helptable:
   241         names, header, doc = topic[0:3]
   253         names, header, doc = topic[0:3]
   242         # Old extensions may use a str as doc.
   254         # Old extensions may use a str as doc.
   243         if (sum(map(lowercontains, names))
   255         if (
       
   256             sum(map(lowercontains, names))
   244             or lowercontains(header)
   257             or lowercontains(header)
   245             or (callable(doc) and lowercontains(doc(ui)))):
   258             or (callable(doc) and lowercontains(doc(ui)))
       
   259         ):
   246             name = names[0]
   260             name = names[0]
   247             if not filtertopic(ui, name):
   261             if not filtertopic(ui, name):
   248                 results['topics'].append((names[0], header))
   262                 results['topics'].append((names[0], header))
   249     for cmd, entry in commands.table.iteritems():
   263     for cmd, entry in commands.table.iteritems():
   250         if len(entry) == 3:
   264         if len(entry) == 3:
   261             cmdname = cmdutil.parsealiases(cmd)[0]
   275             cmdname = cmdutil.parsealiases(cmd)[0]
   262             if filtercmd(ui, cmdname, func, kw, docs):
   276             if filtercmd(ui, cmdname, func, kw, docs):
   263                 continue
   277                 continue
   264             results['commands'].append((cmdname, summary))
   278             results['commands'].append((cmdname, summary))
   265     for name, docs in itertools.chain(
   279     for name, docs in itertools.chain(
   266         extensions.enabled(False).iteritems(),
   280         extensions.enabled(False).iteritems(), extensions.disabled().iteritems()
   267         extensions.disabled().iteritems()):
   281     ):
   268         if not docs:
   282         if not docs:
   269             continue
   283             continue
   270         name = name.rpartition('.')[-1]
   284         name = name.rpartition('.')[-1]
   271         if lowercontains(name) or lowercontains(docs):
   285         if lowercontains(name) or lowercontains(docs):
   272             # extension docs are already translated
   286             # extension docs are already translated
   288                 if filtercmd(ui, cmdname, func, kw, cmddoc):
   302                 if filtercmd(ui, cmdname, func, kw, cmddoc):
   289                     continue
   303                     continue
   290                 results['extensioncommands'].append((cmdname, cmddoc))
   304                 results['extensioncommands'].append((cmdname, cmddoc))
   291     return results
   305     return results
   292 
   306 
       
   307 
   293 def loaddoc(topic, subdir=None):
   308 def loaddoc(topic, subdir=None):
   294     """Return a delayed loader for help/topic.txt."""
   309     """Return a delayed loader for help/topic.txt."""
   295 
   310 
   296     def loader(ui):
   311     def loader(ui):
   297         docdir = os.path.join(util.datapath, 'help')
   312         docdir = os.path.join(util.datapath, 'help')
   303             doc = rewriter(ui, topic, doc)
   318             doc = rewriter(ui, topic, doc)
   304         return doc
   319         return doc
   305 
   320 
   306     return loader
   321     return loader
   307 
   322 
   308 internalstable = sorted([
   323 
   309     (['bundle2'], _('Bundle2'),
   324 internalstable = sorted(
   310      loaddoc('bundle2', subdir='internals')),
   325     [
   311     (['bundles'], _('Bundles'),
   326         (['bundle2'], _('Bundle2'), loaddoc('bundle2', subdir='internals')),
   312      loaddoc('bundles', subdir='internals')),
   327         (['bundles'], _('Bundles'), loaddoc('bundles', subdir='internals')),
   313     (['cbor'], _('CBOR'),
   328         (['cbor'], _('CBOR'), loaddoc('cbor', subdir='internals')),
   314      loaddoc('cbor', subdir='internals')),
   329         (['censor'], _('Censor'), loaddoc('censor', subdir='internals')),
   315     (['censor'], _('Censor'),
   330         (
   316      loaddoc('censor', subdir='internals')),
   331             ['changegroups'],
   317     (['changegroups'], _('Changegroups'),
   332             _('Changegroups'),
   318      loaddoc('changegroups', subdir='internals')),
   333             loaddoc('changegroups', subdir='internals'),
   319     (['config'], _('Config Registrar'),
   334         ),
   320      loaddoc('config', subdir='internals')),
   335         (
   321     (['extensions', 'extension'], _('Extension API'),
   336             ['config'],
   322      loaddoc('extensions', subdir='internals')),
   337             _('Config Registrar'),
   323     (['mergestate'], _('Mergestate'),
   338             loaddoc('config', subdir='internals'),
   324      loaddoc('mergestate', subdir='internals')),
   339         ),
   325     (['requirements'], _('Repository Requirements'),
   340         (
   326      loaddoc('requirements', subdir='internals')),
   341             ['extensions', 'extension'],
   327     (['revlogs'], _('Revision Logs'),
   342             _('Extension API'),
   328      loaddoc('revlogs', subdir='internals')),
   343             loaddoc('extensions', subdir='internals'),
   329     (['wireprotocol'], _('Wire Protocol'),
   344         ),
   330      loaddoc('wireprotocol', subdir='internals')),
   345         (
   331     (['wireprotocolrpc'], _('Wire Protocol RPC'),
   346             ['mergestate'],
   332      loaddoc('wireprotocolrpc', subdir='internals')),
   347             _('Mergestate'),
   333     (['wireprotocolv2'], _('Wire Protocol Version 2'),
   348             loaddoc('mergestate', subdir='internals'),
   334      loaddoc('wireprotocolv2', subdir='internals')),
   349         ),
   335 ])
   350         (
       
   351             ['requirements'],
       
   352             _('Repository Requirements'),
       
   353             loaddoc('requirements', subdir='internals'),
       
   354         ),
       
   355         (
       
   356             ['revlogs'],
       
   357             _('Revision Logs'),
       
   358             loaddoc('revlogs', subdir='internals'),
       
   359         ),
       
   360         (
       
   361             ['wireprotocol'],
       
   362             _('Wire Protocol'),
       
   363             loaddoc('wireprotocol', subdir='internals'),
       
   364         ),
       
   365         (
       
   366             ['wireprotocolrpc'],
       
   367             _('Wire Protocol RPC'),
       
   368             loaddoc('wireprotocolrpc', subdir='internals'),
       
   369         ),
       
   370         (
       
   371             ['wireprotocolv2'],
       
   372             _('Wire Protocol Version 2'),
       
   373             loaddoc('wireprotocolv2', subdir='internals'),
       
   374         ),
       
   375     ]
       
   376 )
       
   377 
   336 
   378 
   337 def internalshelp(ui):
   379 def internalshelp(ui):
   338     """Generate the index for the "internals" topic."""
   380     """Generate the index for the "internals" topic."""
   339     lines = ['To access a subtopic, use "hg help internals.{subtopic-name}"\n',
   381     lines = [
   340              '\n']
   382         'To access a subtopic, use "hg help internals.{subtopic-name}"\n',
       
   383         '\n',
       
   384     ]
   341     for names, header, doc in internalstable:
   385     for names, header, doc in internalstable:
   342         lines.append(' :%s: %s\n' % (names[0], header))
   386         lines.append(' :%s: %s\n' % (names[0], header))
   343 
   387 
   344     return ''.join(lines)
   388     return ''.join(lines)
   345 
   389 
   346 helptable = sorted([
   390 
   347     (['bundlespec'], _("Bundle File Formats"), loaddoc('bundlespec'),
   391 helptable = sorted(
   348      TOPIC_CATEGORY_CONCEPTS),
   392     [
   349     (['color'], _("Colorizing Outputs"), loaddoc('color'),
   393         (
   350      TOPIC_CATEGORY_OUTPUT),
   394             ['bundlespec'],
   351     (["config", "hgrc"], _("Configuration Files"), loaddoc('config'),
   395             _("Bundle File Formats"),
   352      TOPIC_CATEGORY_CONFIG),
   396             loaddoc('bundlespec'),
   353     (['deprecated'], _("Deprecated Features"), loaddoc('deprecated'),
   397             TOPIC_CATEGORY_CONCEPTS,
   354      TOPIC_CATEGORY_MISC),
   398         ),
   355     (["dates"], _("Date Formats"), loaddoc('dates'), TOPIC_CATEGORY_OUTPUT),
   399         (
   356     (["flags"], _("Command-line flags"), loaddoc('flags'),
   400             ['color'],
   357      TOPIC_CATEGORY_CONFIG),
   401             _("Colorizing Outputs"),
   358     (["patterns"], _("File Name Patterns"), loaddoc('patterns'),
   402             loaddoc('color'),
   359      TOPIC_CATEGORY_IDS),
   403             TOPIC_CATEGORY_OUTPUT,
   360     (['environment', 'env'], _('Environment Variables'),
   404         ),
   361      loaddoc('environment'), TOPIC_CATEGORY_CONFIG),
   405         (
   362     (['revisions', 'revs', 'revsets', 'revset', 'multirevs', 'mrevs'],
   406             ["config", "hgrc"],
   363       _('Specifying Revisions'), loaddoc('revisions'), TOPIC_CATEGORY_IDS),
   407             _("Configuration Files"),
   364     (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets'),
   408             loaddoc('config'),
   365      TOPIC_CATEGORY_IDS),
   409             TOPIC_CATEGORY_CONFIG,
   366     (['diffs'], _('Diff Formats'), loaddoc('diffs'), TOPIC_CATEGORY_OUTPUT),
   410         ),
   367     (['merge-tools', 'mergetools', 'mergetool'], _('Merge Tools'),
   411         (
   368      loaddoc('merge-tools'), TOPIC_CATEGORY_CONFIG),
   412             ['deprecated'],
   369     (['templating', 'templates', 'template', 'style'], _('Template Usage'),
   413             _("Deprecated Features"),
   370      loaddoc('templates'), TOPIC_CATEGORY_OUTPUT),
   414             loaddoc('deprecated'),
   371     (['urls'], _('URL Paths'), loaddoc('urls'), TOPIC_CATEGORY_IDS),
   415             TOPIC_CATEGORY_MISC,
   372     (["extensions"], _("Using Additional Features"), extshelp,
   416         ),
   373      TOPIC_CATEGORY_CONFIG),
   417         (["dates"], _("Date Formats"), loaddoc('dates'), TOPIC_CATEGORY_OUTPUT),
   374     (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos'),
   418         (
   375      TOPIC_CATEGORY_CONCEPTS),
   419             ["flags"],
   376     (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb'),
   420             _("Command-line flags"),
   377      TOPIC_CATEGORY_CONFIG),
   421             loaddoc('flags'),
   378     (["glossary"], _("Glossary"), loaddoc('glossary'), TOPIC_CATEGORY_CONCEPTS),
   422             TOPIC_CATEGORY_CONFIG,
   379     (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
   423         ),
   380      loaddoc('hgignore'), TOPIC_CATEGORY_IDS),
   424         (
   381     (["phases"], _("Working with Phases"), loaddoc('phases'),
   425             ["patterns"],
   382      TOPIC_CATEGORY_CONCEPTS),
   426             _("File Name Patterns"),
   383     (['scripting'], _('Using Mercurial from scripts and automation'),
   427             loaddoc('patterns'),
   384      loaddoc('scripting'), TOPIC_CATEGORY_MISC),
   428             TOPIC_CATEGORY_IDS,
   385     (['internals'], _("Technical implementation topics"), internalshelp,
   429         ),
   386      TOPIC_CATEGORY_MISC),
   430         (
   387     (['pager'], _("Pager Support"), loaddoc('pager'), TOPIC_CATEGORY_CONFIG),
   431             ['environment', 'env'],
   388 ])
   432             _('Environment Variables'),
       
   433             loaddoc('environment'),
       
   434             TOPIC_CATEGORY_CONFIG,
       
   435         ),
       
   436         (
       
   437             ['revisions', 'revs', 'revsets', 'revset', 'multirevs', 'mrevs'],
       
   438             _('Specifying Revisions'),
       
   439             loaddoc('revisions'),
       
   440             TOPIC_CATEGORY_IDS,
       
   441         ),
       
   442         (
       
   443             ['filesets', 'fileset'],
       
   444             _("Specifying File Sets"),
       
   445             loaddoc('filesets'),
       
   446             TOPIC_CATEGORY_IDS,
       
   447         ),
       
   448         (['diffs'], _('Diff Formats'), loaddoc('diffs'), TOPIC_CATEGORY_OUTPUT),
       
   449         (
       
   450             ['merge-tools', 'mergetools', 'mergetool'],
       
   451             _('Merge Tools'),
       
   452             loaddoc('merge-tools'),
       
   453             TOPIC_CATEGORY_CONFIG,
       
   454         ),
       
   455         (
       
   456             ['templating', 'templates', 'template', 'style'],
       
   457             _('Template Usage'),
       
   458             loaddoc('templates'),
       
   459             TOPIC_CATEGORY_OUTPUT,
       
   460         ),
       
   461         (['urls'], _('URL Paths'), loaddoc('urls'), TOPIC_CATEGORY_IDS),
       
   462         (
       
   463             ["extensions"],
       
   464             _("Using Additional Features"),
       
   465             extshelp,
       
   466             TOPIC_CATEGORY_CONFIG,
       
   467         ),
       
   468         (
       
   469             ["subrepos", "subrepo"],
       
   470             _("Subrepositories"),
       
   471             loaddoc('subrepos'),
       
   472             TOPIC_CATEGORY_CONCEPTS,
       
   473         ),
       
   474         (
       
   475             ["hgweb"],
       
   476             _("Configuring hgweb"),
       
   477             loaddoc('hgweb'),
       
   478             TOPIC_CATEGORY_CONFIG,
       
   479         ),
       
   480         (
       
   481             ["glossary"],
       
   482             _("Glossary"),
       
   483             loaddoc('glossary'),
       
   484             TOPIC_CATEGORY_CONCEPTS,
       
   485         ),
       
   486         (
       
   487             ["hgignore", "ignore"],
       
   488             _("Syntax for Mercurial Ignore Files"),
       
   489             loaddoc('hgignore'),
       
   490             TOPIC_CATEGORY_IDS,
       
   491         ),
       
   492         (
       
   493             ["phases"],
       
   494             _("Working with Phases"),
       
   495             loaddoc('phases'),
       
   496             TOPIC_CATEGORY_CONCEPTS,
       
   497         ),
       
   498         (
       
   499             ['scripting'],
       
   500             _('Using Mercurial from scripts and automation'),
       
   501             loaddoc('scripting'),
       
   502             TOPIC_CATEGORY_MISC,
       
   503         ),
       
   504         (
       
   505             ['internals'],
       
   506             _("Technical implementation topics"),
       
   507             internalshelp,
       
   508             TOPIC_CATEGORY_MISC,
       
   509         ),
       
   510         (
       
   511             ['pager'],
       
   512             _("Pager Support"),
       
   513             loaddoc('pager'),
       
   514             TOPIC_CATEGORY_CONFIG,
       
   515         ),
       
   516     ]
       
   517 )
   389 
   518 
   390 # Maps topics with sub-topics to a list of their sub-topics.
   519 # Maps topics with sub-topics to a list of their sub-topics.
   391 subtopics = {
   520 subtopics = {
   392     'internals': internalstable,
   521     'internals': internalstable,
   393 }
   522 }
   394 
   523 
   395 # Map topics to lists of callable taking the current topic help and
   524 # Map topics to lists of callable taking the current topic help and
   396 # returning the updated version
   525 # returning the updated version
   397 helphooks = {}
   526 helphooks = {}
   398 
   527 
       
   528 
   399 def addtopichook(topic, rewriter):
   529 def addtopichook(topic, rewriter):
   400     helphooks.setdefault(topic, []).append(rewriter)
   530     helphooks.setdefault(topic, []).append(rewriter)
       
   531 
   401 
   532 
   402 def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
   533 def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
   403     """Extract docstring from the items key to function mapping, build a
   534     """Extract docstring from the items key to function mapping, build a
   404     single documentation block and use it to overwrite the marker in doc.
   535     single documentation block and use it to overwrite the marker in doc.
   405     """
   536     """
   406     entries = []
   537     entries = []
   407     for name in sorted(items):
   538     for name in sorted(items):
   408         text = (pycompat.getdoc(items[name]) or '').rstrip()
   539         text = (pycompat.getdoc(items[name]) or '').rstrip()
   409         if (not text
   540         if not text or not ui.verbose and any(w in text for w in _exclkeywords):
   410             or not ui.verbose and any(w in text for w in _exclkeywords)):
       
   411             continue
   541             continue
   412         text = gettext(text)
   542         text = gettext(text)
   413         if dedent:
   543         if dedent:
   414             # Abuse latin1 to use textwrap.dedent() on bytes.
   544             # Abuse latin1 to use textwrap.dedent() on bytes.
   415             text = textwrap.dedent(text.decode('latin1')).encode('latin1')
   545             text = textwrap.dedent(text.decode('latin1')).encode('latin1')
   425                 doclines.append('  ' + l.strip())
   555                 doclines.append('  ' + l.strip())
   426         entries.append('\n'.join(doclines))
   556         entries.append('\n'.join(doclines))
   427     entries = '\n\n'.join(entries)
   557     entries = '\n\n'.join(entries)
   428     return doc.replace(marker, entries)
   558     return doc.replace(marker, entries)
   429 
   559 
       
   560 
   430 def addtopicsymbols(topic, marker, symbols, dedent=False):
   561 def addtopicsymbols(topic, marker, symbols, dedent=False):
   431     def add(ui, topic, doc):
   562     def add(ui, topic, doc):
   432         return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
   563         return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
       
   564 
   433     addtopichook(topic, add)
   565     addtopichook(topic, add)
   434 
   566 
   435 addtopicsymbols('bundlespec', '.. bundlecompressionmarker',
   567 
   436                 compression.bundlecompressiontopics())
   568 addtopicsymbols(
       
   569     'bundlespec',
       
   570     '.. bundlecompressionmarker',
       
   571     compression.bundlecompressiontopics(),
       
   572 )
   437 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
   573 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
   438 addtopicsymbols('merge-tools', '.. internaltoolsmarker',
   574 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internalsdoc)
   439                 filemerge.internalsdoc)
       
   440 addtopicsymbols('revisions', '.. predicatesmarker', revset.symbols)
   575 addtopicsymbols('revisions', '.. predicatesmarker', revset.symbols)
   441 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
   576 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
   442 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
   577 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
   443 addtopicsymbols('templates', '.. functionsmarker', templatefuncs.funcs)
   578 addtopicsymbols('templates', '.. functionsmarker', templatefuncs.funcs)
   444 addtopicsymbols('hgweb', '.. webcommandsmarker', webcommands.commands,
   579 addtopicsymbols(
   445                 dedent=True)
   580     'hgweb', '.. webcommandsmarker', webcommands.commands, dedent=True
       
   581 )
       
   582 
   446 
   583 
   447 def inserttweakrc(ui, topic, doc):
   584 def inserttweakrc(ui, topic, doc):
   448     marker = '.. tweakdefaultsmarker'
   585     marker = '.. tweakdefaultsmarker'
   449     repl = uimod.tweakrc
   586     repl = uimod.tweakrc
       
   587 
   450     def sub(m):
   588     def sub(m):
   451         lines = [m.group(1) + s for s in repl.splitlines()]
   589         lines = [m.group(1) + s for s in repl.splitlines()]
   452         return '\n'.join(lines)
   590         return '\n'.join(lines)
       
   591 
   453     return re.sub(br'( *)%s' % re.escape(marker), sub, doc)
   592     return re.sub(br'( *)%s' % re.escape(marker), sub, doc)
   454 
   593 
       
   594 
   455 addtopichook('config', inserttweakrc)
   595 addtopichook('config', inserttweakrc)
   456 
   596 
   457 def help_(ui, commands, name, unknowncmd=False, full=True, subtopic=None,
   597 
   458           fullname=None, **opts):
   598 def help_(
       
   599     ui,
       
   600     commands,
       
   601     name,
       
   602     unknowncmd=False,
       
   603     full=True,
       
   604     subtopic=None,
       
   605     fullname=None,
       
   606     **opts
       
   607 ):
   459     '''
   608     '''
   460     Generate the help for 'name' as unformatted restructured text. If
   609     Generate the help for 'name' as unformatted restructured text. If
   461     'name' is None, describe the commands available.
   610     'name' is None, describe the commands available.
   462     '''
   611     '''
   463 
   612 
   464     opts = pycompat.byteskwargs(opts)
   613     opts = pycompat.byteskwargs(opts)
   465 
   614 
   466     def helpcmd(name, subtopic=None):
   615     def helpcmd(name, subtopic=None):
   467         try:
   616         try:
   468             aliases, entry = cmdutil.findcmd(name, commands.table,
   617             aliases, entry = cmdutil.findcmd(
   469                                              strict=unknowncmd)
   618                 name, commands.table, strict=unknowncmd
       
   619             )
   470         except error.AmbiguousCommand as inst:
   620         except error.AmbiguousCommand as inst:
   471             # py3 fix: except vars can't be used outside the scope of the
   621             # py3 fix: except vars can't be used outside the scope of the
   472             # except block, nor can be used inside a lambda. python issue4617
   622             # except block, nor can be used inside a lambda. python issue4617
   473             prefix = inst.args[0]
   623             prefix = inst.args[0]
   474             select = lambda c: cmdutil.parsealiases(c)[0].startswith(prefix)
   624             select = lambda c: cmdutil.parsealiases(c)[0].startswith(prefix)
   505         if not doc:
   655         if not doc:
   506             doc = _("(no help text available)")
   656             doc = _("(no help text available)")
   507         if util.safehasattr(entry[0], 'definition'):  # aliased command
   657         if util.safehasattr(entry[0], 'definition'):  # aliased command
   508             source = entry[0].source
   658             source = entry[0].source
   509             if entry[0].definition.startswith('!'):  # shell alias
   659             if entry[0].definition.startswith('!'):  # shell alias
   510                 doc = (_('shell alias for: %s\n\n%s\n\ndefined by: %s\n') %
   660                 doc = _('shell alias for: %s\n\n%s\n\ndefined by: %s\n') % (
   511                        (entry[0].definition[1:], doc, source))
   661                     entry[0].definition[1:],
       
   662                     doc,
       
   663                     source,
       
   664                 )
   512             else:
   665             else:
   513                 doc = (_('alias for: hg %s\n\n%s\n\ndefined by: %s\n') %
   666                 doc = _('alias for: hg %s\n\n%s\n\ndefined by: %s\n') % (
   514                        (entry[0].definition, doc, source))
   667                     entry[0].definition,
       
   668                     doc,
       
   669                     source,
       
   670                 )
   515         doc = doc.splitlines(True)
   671         doc = doc.splitlines(True)
   516         if ui.quiet or not full:
   672         if ui.quiet or not full:
   517             rst.append(doc[0])
   673             rst.append(doc[0])
   518         else:
   674         else:
   519             rst.extend(doc)
   675             rst.extend(doc)
   523         # extension help text
   679         # extension help text
   524         try:
   680         try:
   525             mod = extensions.find(name)
   681             mod = extensions.find(name)
   526             doc = gettext(pycompat.getdoc(mod)) or ''
   682             doc = gettext(pycompat.getdoc(mod)) or ''
   527             if '\n' in doc.strip():
   683             if '\n' in doc.strip():
   528                 msg = _("(use 'hg help -e %s' to show help for "
   684                 msg = _(
   529                         "the %s extension)") % (name, name)
   685                     "(use 'hg help -e %s' to show help for " "the %s extension)"
       
   686                 ) % (name, name)
   530                 rst.append('\n%s\n' % msg)
   687                 rst.append('\n%s\n' % msg)
   531         except KeyError:
   688         except KeyError:
   532             pass
   689             pass
   533 
   690 
   534         # options
   691         # options
   535         if not ui.quiet and entry[1]:
   692         if not ui.quiet and entry[1]:
   536             rst.append(optrst(_("options"), entry[1], ui.verbose))
   693             rst.append(optrst(_("options"), entry[1], ui.verbose))
   537 
   694 
   538         if ui.verbose:
   695         if ui.verbose:
   539             rst.append(optrst(_("global options"),
   696             rst.append(
   540                               commands.globalopts, ui.verbose))
   697                 optrst(_("global options"), commands.globalopts, ui.verbose)
       
   698             )
   541 
   699 
   542         if not ui.verbose:
   700         if not ui.verbose:
   543             if not full:
   701             if not full:
   544                 rst.append(_("\n(use 'hg %s -h' to show more help)\n")
   702                 rst.append(_("\n(use 'hg %s -h' to show more help)\n") % name)
   545                            % name)
       
   546             elif not ui.quiet:
   703             elif not ui.quiet:
   547                 rst.append(_('\n(some details hidden, use --verbose '
   704                 rst.append(
   548                                'to show complete help)'))
   705                     _(
       
   706                         '\n(some details hidden, use --verbose '
       
   707                         'to show complete help)'
       
   708                     )
       
   709                 )
   549 
   710 
   550         return rst
   711         return rst
   551 
   712 
   552     def helplist(select=None, **opts):
   713     def helplist(select=None, **opts):
   553         # Category -> list of commands
   714         # Category -> list of commands
   570             if not doc:
   731             if not doc:
   571                 doc = _("(no help text available)")
   732                 doc = _("(no help text available)")
   572             h[f] = doc.splitlines()[0].rstrip()
   733             h[f] = doc.splitlines()[0].rstrip()
   573 
   734 
   574             cat = getattr(func, 'helpcategory', None) or (
   735             cat = getattr(func, 'helpcategory', None) or (
   575                 registrar.command.CATEGORY_NONE)
   736                 registrar.command.CATEGORY_NONE
       
   737             )
   576             cats.setdefault(cat, []).append(f)
   738             cats.setdefault(cat, []).append(f)
   577 
   739 
   578         rst = []
   740         rst = []
   579         if not h:
   741         if not h:
   580             if not ui.quiet:
   742             if not ui.quiet:
   603             appendcmds(h)
   765             appendcmds(h)
   604         else:
   766         else:
   605             # Check that all categories have an order.
   767             # Check that all categories have an order.
   606             missing_order = set(cats.keys()) - set(CATEGORY_ORDER)
   768             missing_order = set(cats.keys()) - set(CATEGORY_ORDER)
   607             if missing_order:
   769             if missing_order:
   608                 ui.develwarn('help categories missing from CATEGORY_ORDER: %s' %
   770                 ui.develwarn(
   609                              missing_order)
   771                     'help categories missing from CATEGORY_ORDER: %s'
       
   772                     % missing_order
       
   773                 )
   610 
   774 
   611             # List per category.
   775             # List per category.
   612             for cat in CATEGORY_ORDER:
   776             for cat in CATEGORY_ORDER:
   613                 catfns = cats.get(cat, [])
   777                 catfns = cats.get(cat, [])
   614                 if catfns:
   778                 if catfns:
   617                         rst.append("\n%s:\n" % catname)
   781                         rst.append("\n%s:\n" % catname)
   618                     rst.append("\n")
   782                     rst.append("\n")
   619                     appendcmds(catfns)
   783                     appendcmds(catfns)
   620 
   784 
   621         ex = opts.get
   785         ex = opts.get
   622         anyopts = (ex(r'keyword') or not (ex(r'command') or ex(r'extension')))
   786         anyopts = ex(r'keyword') or not (ex(r'command') or ex(r'extension'))
   623         if not name and anyopts:
   787         if not name and anyopts:
   624             exts = listexts(_('enabled extensions:'), extensions.enabled(),
   788             exts = listexts(
   625                             showdeprecated=ui.verbose)
   789                 _('enabled extensions:'),
       
   790                 extensions.enabled(),
       
   791                 showdeprecated=ui.verbose,
       
   792             )
   626             if exts:
   793             if exts:
   627                 rst.append('\n')
   794                 rst.append('\n')
   628                 rst.extend(exts)
   795                 rst.extend(exts)
   629 
   796 
   630             rst.append(_("\nadditional help topics:\n"))
   797             rst.append(_("\nadditional help topics:\n"))
   638                     category = TOPIC_CATEGORY_NONE
   805                     category = TOPIC_CATEGORY_NONE
   639 
   806 
   640                 topicname = names[0]
   807                 topicname = names[0]
   641                 if not filtertopic(ui, topicname):
   808                 if not filtertopic(ui, topicname):
   642                     topiccats.setdefault(category, []).append(
   809                     topiccats.setdefault(category, []).append(
   643                         (topicname, header))
   810                         (topicname, header)
       
   811                     )
   644 
   812 
   645             # Check that all categories have an order.
   813             # Check that all categories have an order.
   646             missing_order = set(topiccats.keys()) - set(TOPIC_CATEGORY_ORDER)
   814             missing_order = set(topiccats.keys()) - set(TOPIC_CATEGORY_ORDER)
   647             if missing_order:
   815             if missing_order:
   648                 ui.develwarn(
   816                 ui.develwarn(
   649                     'help categories missing from TOPIC_CATEGORY_ORDER: %s' %
   817                     'help categories missing from TOPIC_CATEGORY_ORDER: %s'
   650                     missing_order)
   818                     % missing_order
       
   819                 )
   651 
   820 
   652             # Output topics per category.
   821             # Output topics per category.
   653             for cat in TOPIC_CATEGORY_ORDER:
   822             for cat in TOPIC_CATEGORY_ORDER:
   654                 topics = topiccats.get(cat, [])
   823                 topics = topiccats.get(cat, [])
   655                 if topics:
   824                 if topics:
   661                         rst.append(" :%s: %s\n" % (t, desc))
   830                         rst.append(" :%s: %s\n" % (t, desc))
   662 
   831 
   663         if ui.quiet:
   832         if ui.quiet:
   664             pass
   833             pass
   665         elif ui.verbose:
   834         elif ui.verbose:
   666             rst.append('\n%s\n' % optrst(_("global options"),
   835             rst.append(
   667                                          commands.globalopts, ui.verbose))
   836                 '\n%s\n'
       
   837                 % optrst(_("global options"), commands.globalopts, ui.verbose)
       
   838             )
   668             if name == 'shortlist':
   839             if name == 'shortlist':
   669                 rst.append(_("\n(use 'hg help' for the full list "
   840                 rst.append(
   670                              "of commands)\n"))
   841                     _("\n(use 'hg help' for the full list " "of commands)\n")
       
   842                 )
   671         else:
   843         else:
   672             if name == 'shortlist':
   844             if name == 'shortlist':
   673                 rst.append(_("\n(use 'hg help' for the full list of commands "
   845                 rst.append(
   674                              "or 'hg -v' for details)\n"))
   846                     _(
       
   847                         "\n(use 'hg help' for the full list of commands "
       
   848                         "or 'hg -v' for details)\n"
       
   849                     )
       
   850                 )
   675             elif name and not full:
   851             elif name and not full:
   676                 rst.append(_("\n(use 'hg help %s' to show the full help "
   852                 rst.append(
   677                              "text)\n") % name)
   853                     _("\n(use 'hg help %s' to show the full help " "text)\n")
       
   854                     % name
       
   855                 )
   678             elif name and syns and name in syns.keys():
   856             elif name and syns and name in syns.keys():
   679                 rst.append(_("\n(use 'hg help -v -e %s' to show built-in "
   857                 rst.append(
   680                              "aliases and global options)\n") % name)
   858                     _(
       
   859                         "\n(use 'hg help -v -e %s' to show built-in "
       
   860                         "aliases and global options)\n"
       
   861                     )
       
   862                     % name
       
   863                 )
   681             else:
   864             else:
   682                 rst.append(_("\n(use 'hg help -v%s' to show built-in aliases "
   865                 rst.append(
   683                              "and global options)\n")
   866                     _(
   684                            % (name and " " + name or ""))
   867                         "\n(use 'hg help -v%s' to show built-in aliases "
       
   868                         "and global options)\n"
       
   869                     )
       
   870                     % (name and " " + name or "")
       
   871                 )
   685         return rst
   872         return rst
   686 
   873 
   687     def helptopic(name, subtopic=None):
   874     def helptopic(name, subtopic=None):
   688         # Look for sub-topic entry first.
   875         # Look for sub-topic entry first.
   689         header, doc = None, None
   876         header, doc = None, None
   709             rst.append("    %s\n" % _("(no help text available)"))
   896             rst.append("    %s\n" % _("(no help text available)"))
   710         if callable(doc):
   897         if callable(doc):
   711             rst += ["    %s\n" % l for l in doc(ui).splitlines()]
   898             rst += ["    %s\n" % l for l in doc(ui).splitlines()]
   712 
   899 
   713         if not ui.verbose:
   900         if not ui.verbose:
   714             omitted = _('(some details hidden, use --verbose'
   901             omitted = _(
   715                          ' to show complete help)')
   902                 '(some details hidden, use --verbose' ' to show complete help)'
       
   903             )
   716             indicateomitted(rst, omitted)
   904             indicateomitted(rst, omitted)
   717 
   905 
   718         try:
   906         try:
   719             cmdutil.findcmd(name, commands.table)
   907             cmdutil.findcmd(name, commands.table)
   720             rst.append(_("\nuse 'hg help -c %s' to see help for "
   908             rst.append(
   721                        "the %s command\n") % (name, name))
   909                 _("\nuse 'hg help -c %s' to see help for " "the %s command\n")
       
   910                 % (name, name)
       
   911             )
   722         except error.UnknownCommand:
   912         except error.UnknownCommand:
   723             pass
   913             pass
   724         return rst
   914         return rst
   725 
   915 
   726     def helpext(name, subtopic=None):
   916     def helpext(name, subtopic=None):
   741         if tail:
   931         if tail:
   742             rst.extend(tail.splitlines(True))
   932             rst.extend(tail.splitlines(True))
   743             rst.append('\n')
   933             rst.append('\n')
   744 
   934 
   745         if not ui.verbose:
   935         if not ui.verbose:
   746             omitted = _('(some details hidden, use --verbose'
   936             omitted = _(
   747                          ' to show complete help)')
   937                 '(some details hidden, use --verbose' ' to show complete help)'
       
   938             )
   748             indicateomitted(rst, omitted)
   939             indicateomitted(rst, omitted)
   749 
   940 
   750         if mod:
   941         if mod:
   751             try:
   942             try:
   752                 ct = mod.cmdtable
   943                 ct = mod.cmdtable
   753             except AttributeError:
   944             except AttributeError:
   754                 ct = {}
   945                 ct = {}
   755             modcmds = {c.partition('|')[0] for c in ct}
   946             modcmds = {c.partition('|')[0] for c in ct}
   756             rst.extend(helplist(modcmds.__contains__))
   947             rst.extend(helplist(modcmds.__contains__))
   757         else:
   948         else:
   758             rst.append(_("(use 'hg help extensions' for information on enabling"
   949             rst.append(
   759                        " extensions)\n"))
   950                 _(
       
   951                     "(use 'hg help extensions' for information on enabling"
       
   952                     " extensions)\n"
       
   953                 )
       
   954             )
   760         return rst
   955         return rst
   761 
   956 
   762     def helpextcmd(name, subtopic=None):
   957     def helpextcmd(name, subtopic=None):
   763         cmd, ext, doc = extensions.disabledcmd(ui, name,
   958         cmd, ext, doc = extensions.disabledcmd(
   764                                                ui.configbool('ui', 'strict'))
   959             ui, name, ui.configbool('ui', 'strict')
       
   960         )
   765         doc = doc.splitlines()[0]
   961         doc = doc.splitlines()[0]
   766 
   962 
   767         rst = listexts(_("'%s' is provided by the following "
   963         rst = listexts(
   768                               "extension:") % cmd, {ext: doc}, indent=4,
   964             _("'%s' is provided by the following " "extension:") % cmd,
   769                        showdeprecated=True)
   965             {ext: doc},
       
   966             indent=4,
       
   967             showdeprecated=True,
       
   968         )
   770         rst.append('\n')
   969         rst.append('\n')
   771         rst.append(_("(use 'hg help extensions' for information on enabling "
   970         rst.append(
   772                    "extensions)\n"))
   971             _(
       
   972                 "(use 'hg help extensions' for information on enabling "
       
   973                 "extensions)\n"
       
   974             )
       
   975         )
   773         return rst
   976         return rst
   774 
       
   775 
   977 
   776     rst = []
   978     rst = []
   777     kw = opts.get('keyword')
   979     kw = opts.get('keyword')
   778     if kw or name is None and any(opts[o] for o in opts):
   980     if kw or name is None and any(opts[o] for o in opts):
   779         matches = topicmatch(ui, commands, name or '')
   981         matches = topicmatch(ui, commands, name or '')
   781         if opts.get('extension'):
   983         if opts.get('extension'):
   782             helpareas += [('extensions', _('Extensions'))]
   984             helpareas += [('extensions', _('Extensions'))]
   783         if opts.get('command'):
   985         if opts.get('command'):
   784             helpareas += [('commands', _('Commands'))]
   986             helpareas += [('commands', _('Commands'))]
   785         if not helpareas:
   987         if not helpareas:
   786             helpareas = [('topics', _('Topics')),
   988             helpareas = [
   787                          ('commands', _('Commands')),
   989                 ('topics', _('Topics')),
   788                          ('extensions', _('Extensions')),
   990                 ('commands', _('Commands')),
   789                          ('extensioncommands', _('Extension Commands'))]
   991                 ('extensions', _('Extensions')),
       
   992                 ('extensioncommands', _('Extension Commands')),
       
   993             ]
   790         for t, title in helpareas:
   994         for t, title in helpareas:
   791             if matches[t]:
   995             if matches[t]:
   792                 rst.append('%s:\n\n' % title)
   996                 rst.append('%s:\n\n' % title)
   793                 rst.extend(minirst.maketable(sorted(matches[t]), 1))
   997                 rst.extend(minirst.maketable(sorted(matches[t]), 1))
   794                 rst.append('\n')
   998                 rst.append('\n')
   833             rst = [_("Mercurial Distributed SCM\n"), '\n']
  1037             rst = [_("Mercurial Distributed SCM\n"), '\n']
   834         rst.extend(helplist(None, **pycompat.strkwargs(opts)))
  1038         rst.extend(helplist(None, **pycompat.strkwargs(opts)))
   835 
  1039 
   836     return ''.join(rst)
  1040     return ''.join(rst)
   837 
  1041 
   838 def formattedhelp(ui, commands, fullname, keep=None, unknowncmd=False,
  1042 
   839                   full=True, **opts):
  1043 def formattedhelp(
       
  1044     ui, commands, fullname, keep=None, unknowncmd=False, full=True, **opts
       
  1045 ):
   840     """get help for a given topic (as a dotted name) as rendered rst
  1046     """get help for a given topic (as a dotted name) as rendered rst
   841 
  1047 
   842     Either returns the rendered help text or raises an exception.
  1048     Either returns the rendered help text or raises an exception.
   843     """
  1049     """
   844     if keep is None:
  1050     if keep is None:
   845         keep = []
  1051         keep = []
   846     else:
  1052     else:
   847         keep = list(keep) # make a copy so we can mutate this later
  1053         keep = list(keep)  # make a copy so we can mutate this later
   848 
  1054 
   849     # <fullname> := <name>[.<subtopic][.<section>]
  1055     # <fullname> := <name>[.<subtopic][.<section>]
   850     name = subtopic = section = None
  1056     name = subtopic = section = None
   851     if fullname is not None:
  1057     if fullname is not None:
   852         nameparts = fullname.split('.')
  1058         nameparts = fullname.split('.')
   858 
  1064 
   859     textwidth = ui.configint('ui', 'textwidth')
  1065     textwidth = ui.configint('ui', 'textwidth')
   860     termwidth = ui.termwidth() - 2
  1066     termwidth = ui.termwidth() - 2
   861     if textwidth <= 0 or termwidth < textwidth:
  1067     if textwidth <= 0 or termwidth < textwidth:
   862         textwidth = termwidth
  1068         textwidth = termwidth
   863     text = help_(ui, commands, name, fullname=fullname,
  1069     text = help_(
   864                  subtopic=subtopic, unknowncmd=unknowncmd, full=full, **opts)
  1070         ui,
       
  1071         commands,
       
  1072         name,
       
  1073         fullname=fullname,
       
  1074         subtopic=subtopic,
       
  1075         unknowncmd=unknowncmd,
       
  1076         full=full,
       
  1077         **opts
       
  1078     )
   865 
  1079 
   866     blocks, pruned = minirst.parse(text, keep=keep)
  1080     blocks, pruned = minirst.parse(text, keep=keep)
   867     if 'verbose' in pruned:
  1081     if 'verbose' in pruned:
   868         keep.append('omitted')
  1082         keep.append('omitted')
   869     else:
  1083     else: