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 (77 lines) | |||
M | mercurial/registrar.py (8 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: |
# 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)"), | ||||
} | } | ||||
# 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): | 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 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: | |||||
rst.append("\n%s:\n" % cat) | |||||
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. | ||||
""" | """ | ||||
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' | ||||