Details
Details
- Reviewers
- None
- Group Reviewers
hg-reviewers - Commits
- rHG170926caf44c: help: adding support for command categories
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Lint
Lint Skipped - Unit
Unit Tests Skipped
| hg-reviewers |
| Lint Skipped |
| Unit Tests Skipped |
| Path | Packages | |||
|---|---|---|---|---|
| M | doc/gendoc.py (3 lines) | |||
| M | mercurial/help.py (83 lines) | |||
| M | mercurial/registrar.py (11 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| rdamazio | Oct 13 2018, 8:03 AM |
| (["hgignore.5"], '', loaddoc('hgignore.5')), | (["hgignore.5"], '', loaddoc('hgignore.5')), | ||||
| (["hgrc.5"], '', loaddoc('hgrc.5')), | (["hgrc.5"], '', loaddoc('hgrc.5')), | ||||
| (["hgignore.5.gendoc"], '', loaddoc('hgignore')), | (["hgignore.5.gendoc"], '', loaddoc('hgignore')), | ||||
| (["hgrc.5.gendoc"], '', loaddoc('config')), | (["hgrc.5.gendoc"], '', loaddoc('config')), | ||||
| ] | ] | ||||
| helpprinter(ui, helptable + extrahelptable, None, include=[topic]) | helpprinter(ui, helptable + extrahelptable, None, include=[topic]) | ||||
| def helpprinter(ui, helptable, sectionfunc, include=[], exclude=[]): | 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: | if exclude and names[0] in exclude: | ||||
| continue | continue | ||||
| if include and names[0] not in include: | if include and names[0] not in include: | ||||
| continue | continue | ||||
| for name in names: | for name in names: | ||||
| ui.write(".. _%s:\n" % name) | ui.write(".. _%s:\n" % name) | ||||
| ui.write("\n") | ui.write("\n") | ||||
| if sectionfunc: | if sectionfunc: | ||||
| encoding, | encoding, | ||||
| error, | error, | ||||
| extensions, | extensions, | ||||
| fancyopts, | fancyopts, | ||||
| filemerge, | filemerge, | ||||
| fileset, | fileset, | ||||
| minirst, | minirst, | ||||
| pycompat, | pycompat, | ||||
| registrar, | |||||
| revset, | revset, | ||||
| templatefilters, | templatefilters, | ||||
| templatefuncs, | templatefuncs, | ||||
| templatekw, | templatekw, | ||||
| util, | util, | ||||
| ) | ) | ||||
| from .hgweb import ( | from .hgweb import ( | ||||
| webcommands, | webcommands, | ||||
| ) | ) | ||||
| _exclkeywords = { | _exclkeywords = { | ||||
| "(ADVANCED)", | "(ADVANCED)", | ||||
| "(DEPRECATED)", | "(DEPRECATED)", | ||||
| "(EXPERIMENTAL)", | "(EXPERIMENTAL)", | ||||
| # i18n: "(ADVANCED)" is a keyword, must be translated consistently | # i18n: "(ADVANCED)" is a keyword, must be translated consistently | ||||
| _("(ADVANCED)"), | _("(ADVANCED)"), | ||||
| # i18n: "(DEPRECATED)" is a keyword, must be translated consistently | # i18n: "(DEPRECATED)" is a keyword, must be translated consistently | ||||
| _("(DEPRECATED)"), | _("(DEPRECATED)"), | ||||
| # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently | # i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently | ||||
| _("(EXPERIMENTAL)"), | _("(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): | def listexts(header, exts, indent=1, showdeprecated=False): | ||||
| '''return a text listing of the given extensions''' | '''return a text listing of the given extensions''' | ||||
| rst = [] | rst = [] | ||||
| if exts: | if exts: | ||||
| for name, desc in sorted(exts.iteritems()): | for name, desc in sorted(exts.iteritems()): | ||||
| if not showdeprecated and any(w in desc for w in _exclkeywords): | if not showdeprecated and any(w in desc for w in _exclkeywords): | ||||
| continue | continue | ||||
| rst.append('%s:%s: %s\n' % (' ' * indent, name, desc)) | rst.append('%s:%s: %s\n' % (' ' * indent, name, desc)) | ||||
| elif not ui.quiet: | elif not ui.quiet: | ||||
| rst.append(_('\n(some details hidden, use --verbose ' | rst.append(_('\n(some details hidden, use --verbose ' | ||||
| 'to show complete help)')) | 'to show complete help)')) | ||||
| return rst | return rst | ||||
| def helplist(select=None, **opts): | def helplist(select=None, **opts): | ||||
| # list of commands | # Category -> list of commands | ||||
| if name == "shortlist": | cats = {} | ||||
| header = _('basic commands:\n\n') | # Command -> short description | ||||
| elif name == "debug": | |||||
| header = _('debug commands (internal and unsupported):\n\n') | |||||
| else: | |||||
| header = _('list of commands:\n\n') | |||||
| h = {} | h = {} | ||||
| cmds = {} | # Command -> string showing synonyms | ||||
| syns = {} | |||||
| for c, e in commands.table.iteritems(): | for c, e in commands.table.iteritems(): | ||||
| fs = cmdutil.parsealiases(c) | fs = cmdutil.parsealiases(c) | ||||
| f = fs[0] | f = fs[0] | ||||
| syns[f] = ', '.join(fs) | |||||
| func = e[0] | |||||
| p = '' | p = '' | ||||
| if c.startswith("^"): | if c.startswith("^"): | ||||
| p = '^' | p = '^' | ||||
| if select and not select(p + f): | if select and not select(p + f): | ||||
| continue | continue | ||||
| if (not select and name != 'shortlist' and | if (not select and name != 'shortlist' and | ||||
| e[0].__module__ != commands.__name__): | func.__module__ != commands.__name__): | ||||
| continue | continue | ||||
| if name == "shortlist" and not p: | if name == "shortlist" and not p: | ||||
| continue | continue | ||||
| doc = pycompat.getdoc(e[0]) | doc = pycompat.getdoc(func) | ||||
| if filtercmd(ui, f, name, doc): | if filtercmd(ui, f, name, doc): | ||||
| continue | continue | ||||
| doc = gettext(doc) | doc = gettext(doc) | ||||
| if not doc: | if not doc: | ||||
| doc = _("(no help text available)") | doc = _("(no help text available)") | ||||
| h[f] = doc.splitlines()[0].rstrip() | 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 = [] | rst = [] | ||||
| if not h: | if not h: | ||||
| if not ui.quiet: | if not ui.quiet: | ||||
| rst.append(_('no commands defined\n')) | rst.append(_('no commands defined\n')) | ||||
| return rst | return rst | ||||
| # Output top header. | |||||
| if not ui.quiet: | if not ui.quiet: | ||||
| rst.append(header) | if name == "shortlist": | ||||
| fns = sorted(h) | rst.append(_('basic commands:\n\n')) | ||||
| for f in fns: | elif name == "debug": | ||||
| rst.append(_('debug commands (internal and unsupported):\n\n')) | |||||
| else: | |||||
| rst.append(_('list of commands:\n')) | |||||
| def appendcmds(cmds): | |||||
| cmds = sorted(cmds) | |||||
| for c in cmds: | |||||
| if ui.verbose: | if ui.verbose: | ||||
| commacmds = cmds[f].replace("|",", ") | rst.append(" :%s: %s\n" % (syns[c], h[c])) | ||||
| rst.append(" :%s: %s\n" % (commacmds, h[f])) | |||||
| else: | else: | ||||
| rst.append(' :%s: %s\n' % (f, h[f])) | 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 | ex = opts.get | ||||
| anyopts = (ex(r'keyword') or not (ex(r'command') or ex(r'extension'))) | anyopts = (ex(r'keyword') or not (ex(r'command') or ex(r'extension'))) | ||||
| if not name and anyopts: | if not name and anyopts: | ||||
| exts = listexts(_('enabled extensions:'), extensions.enabled()) | exts = listexts(_('enabled extensions:'), extensions.enabled()) | ||||
| if exts: | if exts: | ||||
| rst.append('\n') | rst.append('\n') | ||||
| rst.extend(exts) | rst.extend(exts) | ||||
| "of commands)\n")) | "of commands)\n")) | ||||
| else: | else: | ||||
| if name == 'shortlist': | if name == 'shortlist': | ||||
| rst.append(_("\n(use 'hg help' for the full list of commands " | rst.append(_("\n(use 'hg help' for the full list of commands " | ||||
| "or 'hg -v' for details)\n")) | "or 'hg -v' for details)\n")) | ||||
| elif name and not full: | elif name and not full: | ||||
| rst.append(_("\n(use 'hg help %s' to show the full help " | rst.append(_("\n(use 'hg help %s' to show the full help " | ||||
| "text)\n") % name) | "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 " | rst.append(_("\n(use 'hg help -v -e %s' to show built-in " | ||||
| "aliases and global options)\n") % name) | "aliases and global options)\n") % name) | ||||
| else: | else: | ||||
| rst.append(_("\n(use 'hg help -v%s' to show built-in aliases " | rst.append(_("\n(use 'hg help -v%s' to show built-in aliases " | ||||
| "and global options)\n") | "and global options)\n") | ||||
| % (name and " " + name or "")) | % (name and " " + name or "")) | ||||
| return rst | return rst | ||||
| The `intents` argument defines a set of intended actions or capabilities | The `intents` argument defines a set of intended actions or capabilities | ||||
| the command is taking. These intents can be used to affect the construction | the command is taking. These intents can be used to affect the construction | ||||
| of the repository object passed to the command. For example, commands | of the repository object passed to the command. For example, commands | ||||
| declaring that they are read-only could receive a repository that doesn't | declaring that they are read-only could receive a repository that doesn't | ||||
| have any methods allowing repository mutation. Other intents could be used | have any methods allowing repository mutation. Other intents could be used | ||||
| to prevent the command from running if the requested intent could not be | to prevent the command from running if the requested intent could not be | ||||
| fulfilled. | 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: | The following intents are defined: | ||||
| readonly | readonly | ||||
| The command is read-only | The command is read-only | ||||
| The signature of the decorated function looks like this: | The signature of the decorated function looks like this: | ||||
| def cmd(ui[, repo] [, <args>] [, <options>]) | def cmd(ui[, repo] [, <args>] [, <options>]) | ||||
| `repo` is required if `norepo` is False. | `repo` is required if `norepo` is False. | ||||
| `<args>` are positional args (or `*args`) arguments, of non-option | `<args>` are positional args (or `*args`) arguments, of non-option | ||||
| arguments from the command line. | arguments from the command line. | ||||
| `<options>` are keyword arguments (or `**options`) of option arguments | `<options>` are keyword arguments (or `**options`) of option arguments | ||||
| from the command line. | from the command line. | ||||
| See the WritingExtensions and MercurialApi documentation for more exhaustive | See the WritingExtensions and MercurialApi documentation for more exhaustive | ||||
| descriptions and examples. | descriptions and examples. | ||||
| """ | """ | ||||
| # Command categories for grouping them in help output. | |||||
| CATEGORY_NONE = 'none' | |||||
| def _doregister(self, func, name, options=(), synopsis=None, | def _doregister(self, func, name, options=(), synopsis=None, | ||||
| norepo=False, optionalrepo=False, inferrepo=False, | norepo=False, optionalrepo=False, inferrepo=False, | ||||
| intents=None): | intents=None, helpcategory=None): | ||||
| func.norepo = norepo | func.norepo = norepo | ||||
| func.optionalrepo = optionalrepo | func.optionalrepo = optionalrepo | ||||
| func.inferrepo = inferrepo | func.inferrepo = inferrepo | ||||
| func.intents = intents or set() | func.intents = intents or set() | ||||
| func.helpcategory = helpcategory | |||||
| if synopsis: | if synopsis: | ||||
| self._table[name] = func, list(options), synopsis | self._table[name] = func, list(options), synopsis | ||||
| else: | else: | ||||
| self._table[name] = func, list(options) | self._table[name] = func, list(options) | ||||
| return func | return func | ||||
| INTENT_READONLY = b'readonly' | INTENT_READONLY = b'readonly' | ||||