D5065: help: adding support for command categories

rdamazio (Rodrigo Damazio Bovendorp) phabricator at mercurial-scm.org
Sun Oct 14 06:32:15 EDT 2018


rdamazio updated this revision to Diff 12120.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D5065?vs=12060&id=12120

REVISION DETAIL
  https://phab.mercurial-scm.org/D5065

AFFECTED FILES
  doc/gendoc.py
  mercurial/help.py
  mercurial/registrar.py

CHANGE DETAILS

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
@@ -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:
diff --git a/mercurial/help.py b/mercurial/help.py
--- a/mercurial/help.py
+++ b/mercurial/help.py
@@ -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 = []
@@ -421,53 +436,81 @@
 
 
     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:
             if not ui.quiet:
                 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 +542,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/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:



To: rdamazio, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list