diff --git a/doc/gendoc.py b/doc/gendoc.py --- a/doc/gendoc.py +++ b/doc/gendoc.py @@ -149,7 +149,8 @@ helpprinter(ui, helptable + extrahelptable, None, include=[topic]) def helpprinter(ui, helptable, sectionfunc, include=[], exclude=[]): - for names, sec, doc in helptable: + for h in helptable: + names, sec, doc = h[0:3] if exclude and names[0] in exclude: continue if include and names[0] not in include: diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -690,6 +690,10 @@ coreconfigitem('fsmonitor', 'warn_update_file_count', default=50000, ) +coreconfigitem('help', 'hide\..*', + default=False, + generic=True, +) coreconfigitem('hooks', '.*', default=dynamicdefault, generic=True, diff --git a/mercurial/help.py b/mercurial/help.py --- a/mercurial/help.py +++ b/mercurial/help.py @@ -47,6 +47,17 @@ _("(EXPERIMENTAL)"), } +# Command categories. +CATEGORY_NONE = _('Uncategorized commands') + +# The order in which command categories will be displayed. +# Extensions with custom categories should insert them into this list +# after/before the appropriate item, rather than replacing the list or +# assuming absolute positions. +CATEGORY_ORDER = [ + CATEGORY_NONE, +] + def listexts(header, exts, indent=1, showdeprecated=False): '''return a text listing of the given extensions''' rst = [] @@ -121,6 +132,8 @@ return True if not ui.verbose and doc and any(w in doc for w in _exclkeywords): return True + if ui.configbool('help', 'hide.%s' % cmd): + return True return False def topicmatch(ui, commands, kw): @@ -421,37 +434,37 @@ def helplist(select=None, **opts): - # list of commands - if name == "shortlist": - header = _('basic commands:\n\n') - elif name == "debug": - header = _('debug commands (internal and unsupported):\n\n') - else: - header = _('list of commands:\n\n') - + # Category -> list of commands + cats = {} + # Command -> short description h = {} - cmds = {} + # Command -> string showing synonyms + syns = {} for c, e in commands.table.iteritems(): fs = cmdutil.parsealiases(c) f = fs[0] + syns[f] = ', '.join(fs) + func = e[0] p = '' if c.startswith("^"): p = '^' if select and not select(p + f): continue if (not select and name != 'shortlist' and - e[0].__module__ != commands.__name__): + func.__module__ != commands.__name__): continue if name == "shortlist" and not p: continue - doc = pycompat.getdoc(e[0]) + doc = pycompat.getdoc(func) if filtercmd(ui, f, name, doc): continue doc = gettext(doc) if not doc: doc = _("(no help text available)") h[f] = doc.splitlines()[0].rstrip() - cmds[f] = '|'.join(fs) + + cat = getattr(func, 'helpcategory', None) or CATEGORY_NONE + cats.setdefault(cat, []).append(f) rst = [] if not h: @@ -459,15 +472,42 @@ rst.append(_('no commands defined\n')) return rst + # Output top header. if not ui.quiet: - rst.append(header) - fns = sorted(h) - for f in fns: - if ui.verbose: - commacmds = cmds[f].replace("|",", ") - rst.append(" :%s: %s\n" % (commacmds, h[f])) + if name == "shortlist": + rst.append(_('basic commands:\n\n')) + elif name == "debug": + rst.append(_('debug commands (internal and unsupported):\n\n')) else: - rst.append(' :%s: %s\n' % (f, h[f])) + rst.append(_('list of commands:\n')) + + def appendcmds(cmds): + cmds = sorted(cmds) + for c in cmds: + if ui.verbose: + rst.append(" :%s: %s\n" % (syns[c], h[c])) + else: + rst.append(' :%s: %s\n' % (c, h[c])) + + if name in ('shortlist', 'debug'): + # List without categories. + appendcmds(h) + else: + # Check that all categories have an order. + missing_order = set(cats.keys()) - set(CATEGORY_ORDER) + if missing_order: + ui.develwarn('Help categories missing from CATEGORY_ORDER: %s' % + missing_order) + + print('CATS: %s' % cats) + # List per category. + for cat in CATEGORY_ORDER: + catfns = cats.get(cat, []) + if catfns: + if len(cats) > 1: + rst.append("\n%s:\n" % cat) + rst.append("\n") + appendcmds(catfns) ex = opts.get anyopts = (ex(r'keyword') or not (ex(r'command') or ex(r'extension'))) @@ -499,7 +539,7 @@ elif name and not full: rst.append(_("\n(use 'hg help %s' to show the full help " "text)\n") % name) - elif name and cmds and name in cmds.keys(): + elif name and syns and name in syns.keys(): rst.append(_("\n(use 'hg help -v -e %s' to show built-in " "aliases and global options)\n") % name) else: diff --git a/mercurial/registrar.py b/mercurial/registrar.py --- a/mercurial/registrar.py +++ b/mercurial/registrar.py @@ -146,6 +146,10 @@ to prevent the command from running if the requested intent could not be fulfilled. + If `helpcategory` is set (usually to one of the constants in the help + module), the command will be displayed under that category in the help's + list of commands. + The following intents are defined: readonly @@ -166,12 +170,12 @@ def _doregister(self, func, name, options=(), synopsis=None, norepo=False, optionalrepo=False, inferrepo=False, - intents=None): - + intents=None, helpcategory=None): func.norepo = norepo func.optionalrepo = optionalrepo func.inferrepo = inferrepo func.intents = intents or set() + func.helpcategory = helpcategory if synopsis: self._table[name] = func, list(options), synopsis else: