# HG changeset patch # User rdamazio@google.com # Date 1539432230 25200 # Node ID 170926caf44c33f22bf92ee94c1c3e47203460b0 # Parent 091f9b8a1051fde124df082d6e96e2614ab9387a help: adding support for command categories Differential Revision: https://phab.mercurial-scm.org/D5065 diff -r 091f9b8a1051 -r 170926caf44c doc/gendoc.py --- a/doc/gendoc.py Sun Oct 14 13:35:47 2018 +0200 +++ b/doc/gendoc.py Sat Oct 13 05:03:50 2018 -0700 @@ -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 -r 091f9b8a1051 -r 170926caf44c mercurial/help.py --- a/mercurial/help.py Sun Oct 14 13:35:47 2018 +0200 +++ b/mercurial/help.py Sat Oct 13 05:03:50 2018 -0700 @@ -25,6 +25,7 @@ fileset, minirst, pycompat, + registrar, revset, templatefilters, templatefuncs, @@ -47,6 +48,20 @@ _("(EXPERIMENTAL)"), } +# 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 = [ + registrar.command.CATEGORY_NONE, +] + +# Human-readable category names. These are translated. +# Extensions with custom categories should add their names here. +CATEGORY_NAMES = { + registrar.command.CATEGORY_NONE: 'Uncategorized commands', +} + def listexts(header, exts, indent=1, showdeprecated=False): '''return a text listing of the given extensions''' rst = [] @@ -419,39 +434,39 @@ return rst - 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 ( + registrar.command.CATEGORY_NONE) + cats.setdefault(cat, []).append(f) rst = [] if not h: @@ -459,15 +474,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) + + # List per category. + for cat in CATEGORY_ORDER: + catfns = cats.get(cat, []) + if catfns: + if len(cats) > 1: + catname = gettext(CATEGORY_NAMES[cat]) + rst.append("\n%s:\n" % catname) + rst.append("\n") + appendcmds(catfns) ex = opts.get anyopts = (ex(r'keyword') or not (ex(r'command') or ex(r'extension'))) @@ -499,7 +541,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 -r 091f9b8a1051 -r 170926caf44c mercurial/registrar.py --- a/mercurial/registrar.py Sun Oct 14 13:35:47 2018 +0200 +++ b/mercurial/registrar.py Sat Oct 13 05:03:50 2018 -0700 @@ -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 @@ -164,14 +168,17 @@ descriptions and examples. """ + # Command categories for grouping them in help output. + CATEGORY_NONE = 'none' + 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: