mirror of
https://github.com/facebook/sapling.git
synced 2024-10-16 11:52:02 +03:00
1bf045df79
Summary: Aliases can be additionally configured with documentation by adding an entry of the form: ``` [alias] aliasname:doc = help text ``` The help text for the alias will be displayed if the user runs `hg help aliasname`, and in the list of commands when the user runs `hg help commands`. Reviewed By: phillco Differential Revision: D10428399 fbshipit-source-id: 14338902fce6efab4c8d3b2541cdcf5b15385784
820 lines
27 KiB
Python
820 lines
27 KiB
Python
# help.py - help data for mercurial
|
|
#
|
|
# Copyright 2006 Matt Mackall <mpm@selenic.com>
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
from __future__ import absolute_import
|
|
|
|
import itertools
|
|
import os
|
|
import textwrap
|
|
|
|
from . import (
|
|
cmdutil,
|
|
encoding,
|
|
error,
|
|
extensions,
|
|
filemerge,
|
|
fileset,
|
|
minirst,
|
|
pycompat,
|
|
revset,
|
|
templatefilters,
|
|
templatekw,
|
|
templater,
|
|
util,
|
|
)
|
|
from .hgweb import webcommands
|
|
from .i18n import _, gettext
|
|
|
|
|
|
_exclkeywords = {
|
|
"(ADVANCED)",
|
|
"(DEPRECATED)",
|
|
"(EXPERIMENTAL)",
|
|
# i18n: "(ADVANCED)" is a keyword, must be translated consistently
|
|
_("(ADVANCED)"),
|
|
# i18n: "(DEPRECATED)" is a keyword, must be translated consistently
|
|
_("(DEPRECATED)"),
|
|
# i18n: "(EXPERIMENTAL)" is a keyword, must be translated consistently
|
|
_("(EXPERIMENTAL)"),
|
|
}
|
|
|
|
|
|
def listexts(header, exts, indent=1, showdeprecated=False):
|
|
"""return a text listing of the given extensions"""
|
|
rst = []
|
|
if exts:
|
|
for name, desc in sorted(exts.iteritems()):
|
|
if not showdeprecated and any(w in desc for w in _exclkeywords):
|
|
continue
|
|
rst.append("%s:%s: %s\n" % (" " * indent, name, desc))
|
|
if rst:
|
|
rst.insert(0, "\n%s\n\n" % header)
|
|
return rst
|
|
|
|
|
|
def extshelp(ui):
|
|
rst = loaddoc("extensions")(ui).splitlines(True)
|
|
rst.extend(
|
|
listexts(_("Enabled extensions:"), extensions.enabled(), showdeprecated=True)
|
|
)
|
|
rst.extend(listexts(_("Disabled extensions:"), extensions.disabled()))
|
|
doc = "".join(rst)
|
|
return doc
|
|
|
|
|
|
def optrst(header, options, verbose):
|
|
data = []
|
|
multioccur = False
|
|
for option in options:
|
|
if len(option) == 5:
|
|
shortopt, longopt, default, desc, optlabel = option
|
|
else:
|
|
shortopt, longopt, default, desc = option
|
|
optlabel = _("VALUE") # default label
|
|
|
|
if not verbose and any(w in desc for w in _exclkeywords):
|
|
continue
|
|
|
|
so = ""
|
|
if shortopt:
|
|
so = "-" + shortopt
|
|
lo = "--" + longopt
|
|
if default:
|
|
# default is of unknown type, and in Python 2 we abused
|
|
# the %s-shows-repr property to handle integers etc. To
|
|
# match that behavior on Python 3, we do str(default) and
|
|
# then convert it to bytes.
|
|
desc += _(" (default: %s)") % pycompat.bytestr(default)
|
|
|
|
if isinstance(default, list):
|
|
lo += " %s [+]" % optlabel
|
|
multioccur = True
|
|
elif (default is not None) and not isinstance(default, bool):
|
|
lo += " %s" % optlabel
|
|
|
|
data.append((so, lo, desc))
|
|
|
|
if not data:
|
|
return ""
|
|
|
|
if multioccur:
|
|
header += _(" ([+] can be repeated)")
|
|
|
|
rst = ["\n%s:\n\n" % header]
|
|
rst.extend(minirst.maketable(data, 1))
|
|
|
|
return "".join(rst)
|
|
|
|
|
|
def indicateomitted(rst, omitted, notomitted=None):
|
|
rst.append("\n\n.. container:: omitted\n\n %s\n\n" % omitted)
|
|
if notomitted:
|
|
rst.append("\n\n.. container:: notomitted\n\n %s\n\n" % notomitted)
|
|
|
|
|
|
def filtercmd(ui, cmd, kw, doc):
|
|
if not ui.debugflag and cmd.startswith("debug") and kw != "debug":
|
|
return True
|
|
if not ui.verbose and doc and any(w in doc for w in _exclkeywords):
|
|
return True
|
|
return False
|
|
|
|
|
|
def topicmatch(ui, commands, kw):
|
|
"""Return help topics matching kw.
|
|
|
|
Returns {'section': [(name, summary), ...], ...} where section is
|
|
one of topics, commands, extensions, or extensioncommands.
|
|
"""
|
|
kw = encoding.lower(kw)
|
|
|
|
def lowercontains(container):
|
|
return kw in encoding.lower(container) # translated in helptable
|
|
|
|
results = {"topics": [], "commands": [], "extensions": [], "extensioncommands": []}
|
|
for names, header, doc in helptable:
|
|
# Old extensions may use a str as doc.
|
|
if (
|
|
sum(map(lowercontains, names))
|
|
or lowercontains(header)
|
|
or (callable(doc) and lowercontains(doc(ui)))
|
|
):
|
|
results["topics"].append((names[0], header))
|
|
for cmd, entry in commands.table.iteritems():
|
|
if len(entry) == 3:
|
|
summary = entry[2]
|
|
else:
|
|
summary = ""
|
|
# translate docs *before* searching there
|
|
docs = _(pycompat.getdoc(entry[0])) or ""
|
|
if kw in cmd or lowercontains(summary) or lowercontains(docs):
|
|
doclines = docs.splitlines()
|
|
if doclines:
|
|
summary = doclines[0]
|
|
cmdname = cmd.partition("|")[0].lstrip("^")
|
|
if filtercmd(ui, cmdname, kw, docs):
|
|
continue
|
|
results["commands"].append((cmdname, summary))
|
|
for name, docs in itertools.chain(
|
|
extensions.enabled(False).iteritems(), extensions.disabled().iteritems()
|
|
):
|
|
if not docs:
|
|
continue
|
|
name = name.rpartition(".")[-1]
|
|
if lowercontains(name) or lowercontains(docs):
|
|
# extension docs are already translated
|
|
results["extensions"].append((name, docs.splitlines()[0]))
|
|
try:
|
|
mod = extensions.load(ui, name, "")
|
|
except ImportError:
|
|
# debug message would be printed in extensions.load()
|
|
continue
|
|
for cmd, entry in getattr(mod, "cmdtable", {}).iteritems():
|
|
if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
|
|
cmdname = cmd.partition("|")[0].lstrip("^")
|
|
cmddoc = pycompat.getdoc(entry[0])
|
|
if cmddoc:
|
|
cmddoc = gettext(cmddoc).splitlines()[0]
|
|
else:
|
|
cmddoc = _("(no help text available)")
|
|
if filtercmd(ui, cmdname, kw, cmddoc):
|
|
continue
|
|
results["extensioncommands"].append((cmdname, cmddoc))
|
|
return results
|
|
|
|
|
|
def loaddoc(topic, subdir=None):
|
|
"""Return a delayed loader for help/topic.txt."""
|
|
|
|
def loader(ui):
|
|
docdir = os.path.join(util.datapath, "help")
|
|
if subdir:
|
|
docdir = os.path.join(docdir, subdir)
|
|
path = os.path.join(docdir, topic + ".txt")
|
|
doc = gettext(util.readfile(path))
|
|
for rewriter in helphooks.get(topic, []):
|
|
doc = rewriter(ui, topic, doc)
|
|
return doc
|
|
|
|
return loader
|
|
|
|
|
|
internalstable = sorted(
|
|
[
|
|
(["bundles"], _("Bundles"), loaddoc("bundles", subdir="internals")),
|
|
(["censor"], _("Censor"), loaddoc("censor", subdir="internals")),
|
|
(
|
|
["changegroups"],
|
|
_("Changegroups"),
|
|
loaddoc("changegroups", subdir="internals"),
|
|
),
|
|
(["config"], _("Config Registrar"), loaddoc("config", subdir="internals")),
|
|
(
|
|
["requirements"],
|
|
_("Repository Requirements"),
|
|
loaddoc("requirements", subdir="internals"),
|
|
),
|
|
(["revlogs"], _("Revision Logs"), loaddoc("revlogs", subdir="internals")),
|
|
(
|
|
["wireprotocol"],
|
|
_("Wire Protocol"),
|
|
loaddoc("wireprotocol", subdir="internals"),
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def internalshelp(ui):
|
|
"""Generate the index for the "internals" topic."""
|
|
lines = ['To access a subtopic, use "hg help internals.{subtopic-name}"\n', "\n"]
|
|
for names, header, doc in internalstable:
|
|
lines.append(" :%s: %s\n" % (names[0], header))
|
|
|
|
return "".join(lines)
|
|
|
|
|
|
helptable = sorted(
|
|
[
|
|
(["bundlespec"], _("Bundle File Formats"), loaddoc("bundlespec")),
|
|
(["color"], _("Colorizing Outputs"), loaddoc("color")),
|
|
(["config", "hgrc"], _("Configuration Files"), loaddoc("config")),
|
|
(["dates"], _("Date Formats"), loaddoc("dates")),
|
|
(["flags"], _("Command-line flags"), loaddoc("flags")),
|
|
(["patterns"], _("File Name Patterns"), loaddoc("patterns")),
|
|
(["environment", "env"], _("Environment Variables"), loaddoc("environment")),
|
|
(
|
|
["revisions", "revs", "revsets", "revset", "multirevs", "mrevs"],
|
|
_("Specifying Revisions"),
|
|
loaddoc("revisions"),
|
|
),
|
|
(["filesets", "fileset"], _("Specifying File Sets"), loaddoc("filesets")),
|
|
(["diffs"], _("Diff Formats"), loaddoc("diffs")),
|
|
(
|
|
["merge-tools", "mergetools", "mergetool"],
|
|
_("Merge Tools"),
|
|
loaddoc("merge-tools"),
|
|
),
|
|
(
|
|
["templating", "templates", "template", "style"],
|
|
_("Template Usage"),
|
|
loaddoc("templates"),
|
|
),
|
|
(["urls"], _("URL Paths"), loaddoc("urls")),
|
|
(["extensions"], _("Using Additional Features"), extshelp),
|
|
(["subrepos", "subrepo"], _("Subrepositories"), loaddoc("subrepos")),
|
|
(["hgweb"], _("Configuring hgweb"), loaddoc("hgweb")),
|
|
(["glossary"], _("Glossary"), loaddoc("glossary")),
|
|
(
|
|
["hgignore", "ignore"],
|
|
_("Syntax for Mercurial Ignore Files"),
|
|
loaddoc("hgignore"),
|
|
),
|
|
(["phases"], _("Working with Phases"), loaddoc("phases")),
|
|
(
|
|
["scripting"],
|
|
_("Using Mercurial from scripts and automation"),
|
|
loaddoc("scripting"),
|
|
),
|
|
(["internals"], _("Technical implementation topics"), internalshelp),
|
|
(["pager"], _("Pager Support"), loaddoc("pager")),
|
|
]
|
|
)
|
|
|
|
# Maps topics with sub-topics to a list of their sub-topics.
|
|
subtopics = {"internals": internalstable}
|
|
|
|
# Map topics to lists of callable taking the current topic help and
|
|
# returning the updated version
|
|
helphooks = {}
|
|
|
|
|
|
def addtopichook(topic, rewriter):
|
|
helphooks.setdefault(topic, []).append(rewriter)
|
|
|
|
|
|
def makeitemsdoc(ui, topic, doc, marker, items, dedent=False):
|
|
"""Extract docstring from the items key to function mapping, build a
|
|
single documentation block and use it to overwrite the marker in doc.
|
|
"""
|
|
entries = []
|
|
for name in sorted(items):
|
|
text = (pycompat.getdoc(items[name]) or "").rstrip()
|
|
if not text or not ui.verbose and any(w in text for w in _exclkeywords):
|
|
continue
|
|
text = gettext(text)
|
|
if dedent:
|
|
# Abuse latin1 to use textwrap.dedent() on bytes.
|
|
text = textwrap.dedent(text.decode("latin1")).encode("latin1")
|
|
lines = text.splitlines()
|
|
doclines = [(lines[0])]
|
|
for l in lines[1:]:
|
|
# Stop once we find some Python doctest
|
|
if l.strip().startswith(">>>"):
|
|
break
|
|
if dedent:
|
|
doclines.append(l.rstrip())
|
|
else:
|
|
doclines.append(" " + l.strip())
|
|
entries.append("\n".join(doclines))
|
|
entries = "\n\n".join(entries)
|
|
return doc.replace(marker, entries)
|
|
|
|
|
|
def makesubcmdlist(cmd, subcommands, verbose, quiet):
|
|
cmdhelp = []
|
|
for name, entry in subcommands.items():
|
|
aliases = cmdutil.parsealiases(name)
|
|
name = ", ".join(aliases) if verbose else aliases[0]
|
|
doc = pycompat.getdoc(entry[0]) or ""
|
|
doc, __, rest = doc.strip().partition("\n")
|
|
if verbose and rest.strip():
|
|
if len(entry) > 2: # synopsis
|
|
name = "{} {}".format(name, entry[2])
|
|
cmdhelp.append(" :%s: %s\n" % (name, doc))
|
|
rst = ["\n%s:\n\n" % _("subcommands")]
|
|
rst.extend(sorted(cmdhelp))
|
|
if not quiet:
|
|
rst.append(
|
|
_("\n(use 'hg help %s [subcommand]' to show complete subcommand help)\n")
|
|
% cmd
|
|
)
|
|
return rst
|
|
|
|
|
|
def addtopicsymbols(topic, marker, symbols, dedent=False):
|
|
def add(ui, topic, doc):
|
|
return makeitemsdoc(ui, topic, doc, marker, symbols, dedent=dedent)
|
|
|
|
addtopichook(topic, add)
|
|
|
|
|
|
addtopicsymbols(
|
|
"bundlespec", ".. bundlecompressionmarker", util.bundlecompressiontopics()
|
|
)
|
|
addtopicsymbols("filesets", ".. predicatesmarker", fileset.symbols)
|
|
addtopicsymbols("merge-tools", ".. internaltoolsmarker", filemerge.internalsdoc)
|
|
addtopicsymbols("revisions", ".. predicatesmarker", revset.symbols)
|
|
addtopicsymbols("templates", ".. keywordsmarker", templatekw.keywords)
|
|
addtopicsymbols("templates", ".. filtersmarker", templatefilters.filters)
|
|
addtopicsymbols("templates", ".. functionsmarker", templater.funcs)
|
|
addtopicsymbols("hgweb", ".. webcommandsmarker", webcommands.commands, dedent=True)
|
|
|
|
helphomecommands = [
|
|
("Get the latest commits from the server", ["pull"]),
|
|
("View commits", ["ssl", "show", "diff"]),
|
|
("Check out a commit", ["checkout"]),
|
|
(
|
|
"Work with your checkout",
|
|
["status", "add", "remove", "revert", "forget", "purge", "shelve"],
|
|
),
|
|
("Commit changes and modify commits", ["commit", "amend", "metaedit"]),
|
|
("Rearrange commits", ["rebase", "graft", "hide", "unhide"]),
|
|
(
|
|
"Work with stacks of commits",
|
|
["previous", "next", "split", "fold", "histedit", "absorb"],
|
|
),
|
|
("Undo changes", ["uncommit", "unamend", "undo", "redo"]),
|
|
("Other commands", ["config", "grep", "journal", "rage"]),
|
|
]
|
|
|
|
helphometopics = {"revisions", "filesets", "glossary", "patterns", "templating"}
|
|
|
|
|
|
class _helpdispatch(object):
|
|
def __init__(
|
|
self, ui, commands, unknowncmd=False, full=False, subtopic=None, **opts
|
|
):
|
|
self.ui = ui
|
|
self.commands = commands
|
|
self.subtopic = subtopic
|
|
self.unknowncmd = unknowncmd
|
|
self.full = full
|
|
self.opts = opts
|
|
|
|
self.commandindex = {}
|
|
for name, cmd in commands.table.iteritems():
|
|
for n in name.lstrip("^").split("|"):
|
|
self.commandindex[n] = cmd
|
|
|
|
def dispatch(self, name):
|
|
queries = []
|
|
if self.unknowncmd:
|
|
queries += [self.helpextcmd]
|
|
if self.opts.get("extension"):
|
|
queries += [self.helpext]
|
|
if self.opts.get("command"):
|
|
queries += [self.helpcmd]
|
|
if not queries:
|
|
queries = (self.helptopic, self.helpcmd, self.helpext, self.helpextcmd)
|
|
for f in queries:
|
|
try:
|
|
return f(name, self.subtopic)
|
|
except error.UnknownCommand:
|
|
pass
|
|
else:
|
|
if self.unknowncmd:
|
|
raise error.UnknownCommand(name)
|
|
else:
|
|
msg = _("no such help topic: %s") % name
|
|
hint = _("try 'hg help --keyword %s'") % name
|
|
raise error.Abort(msg, hint=hint)
|
|
|
|
def helpcmd(self, name, subtopic=None):
|
|
try:
|
|
cmd, args, aliases, entry = cmdutil.findsubcmd(
|
|
name.split(), self.commands.table, strict=self.unknowncmd, partial=True
|
|
)
|
|
except error.AmbiguousCommand as inst:
|
|
# py3k fix: except vars can't be used outside the scope of the
|
|
# except block, nor can be used inside a lambda. python issue4617
|
|
prefix = inst.args[0]
|
|
select = lambda c: c.lstrip("^").partition("|")[0].startswith(prefix)
|
|
rst = self.helplist(name, select)
|
|
return rst
|
|
except error.UnknownSubcommand as inst:
|
|
cmd, subcmd, __ = inst
|
|
msg = _("'%s' has no such subcommand: %s") % (cmd, subcmd)
|
|
hint = _("run 'hg help %s' to see available subcommands") % cmd
|
|
raise error.Abort(msg, hint=hint)
|
|
|
|
rst = []
|
|
|
|
# check if it's an invalid alias and display its error if it is
|
|
if getattr(entry[0], "badalias", None):
|
|
rst.append(entry[0].badalias + "\n")
|
|
if entry[0].unknowncmd:
|
|
try:
|
|
rst.extend(self.helpextcmd(entry[0].cmdname))
|
|
except error.UnknownCommand:
|
|
pass
|
|
return rst
|
|
|
|
# synopsis
|
|
if len(entry) > 2:
|
|
if entry[2].startswith("hg"):
|
|
rst.append("%s\n" % entry[2])
|
|
else:
|
|
rst.append("hg %s %s\n" % (cmd, entry[2]))
|
|
else:
|
|
rst.append("hg %s\n" % cmd)
|
|
# aliases
|
|
if self.full and not self.ui.quiet and len(aliases) > 1:
|
|
rst.append(_("\naliases: %s\n") % ", ".join(aliases[1:]))
|
|
rst.append("\n")
|
|
|
|
# description
|
|
doc = gettext(pycompat.getdoc(entry[0]))
|
|
if not doc:
|
|
doc = _("(no help text available)")
|
|
if util.safehasattr(entry[0], "definition"): # aliased command
|
|
aliasdoc = ""
|
|
if util.safehasattr(entry[0], "aliasdoc") and entry[0].aliasdoc is not None:
|
|
aliasdoc = entry[0].aliasdoc + "\n\n"
|
|
source = entry[0].source
|
|
if entry[0].definition.startswith("!"): # shell alias
|
|
doc = _("%sshell alias for::\n\n %s\n\ndefined by: %s\n") % (
|
|
aliasdoc,
|
|
entry[0].definition[1:],
|
|
source,
|
|
)
|
|
else:
|
|
doc = _("%salias for: hg %s\n\n%s\n\ndefined by: %s\n") % (
|
|
aliasdoc,
|
|
entry[0].definition,
|
|
doc,
|
|
source,
|
|
)
|
|
doc = doc.splitlines(True)
|
|
if self.ui.quiet or not self.full:
|
|
rst.append(doc[0])
|
|
else:
|
|
rst.extend(doc)
|
|
rst.append("\n")
|
|
|
|
# check if this command shadows a non-trivial (multi-line)
|
|
# extension help text
|
|
try:
|
|
mod = extensions.find(name)
|
|
doc = gettext(pycompat.getdoc(mod)) or ""
|
|
if "\n" in doc.strip():
|
|
msg = _("(use 'hg help -e %s' to show help for the %s extension)") % (
|
|
name,
|
|
name,
|
|
)
|
|
rst.append("\n%s\n" % msg)
|
|
except KeyError:
|
|
pass
|
|
|
|
# options
|
|
if not self.ui.quiet and entry[1]:
|
|
rst.append(optrst(_("Options"), entry[1], self.ui.verbose))
|
|
|
|
if self.ui.verbose:
|
|
rst.append(
|
|
optrst(_("Global options"), self.commands.globalopts, self.ui.verbose)
|
|
)
|
|
|
|
# subcommands
|
|
if entry[0].subcommands:
|
|
rst.extend(
|
|
makesubcmdlist(
|
|
cmd, entry[0].subcommands, self.ui.verbose, self.ui.quiet
|
|
)
|
|
)
|
|
|
|
if not self.ui.verbose:
|
|
if not self.full:
|
|
rst.append(_("\n(use 'hg %s -h' to show more help)\n") % name)
|
|
elif not self.ui.quiet:
|
|
rst.append(
|
|
_("\n(some details hidden, use --verbose to show complete help)")
|
|
)
|
|
|
|
return rst
|
|
|
|
def _helpcmddoc(self, cmd, doc):
|
|
if util.safehasattr(cmd, "aliasdoc") and cmd.aliasdoc is not None:
|
|
return gettext(cmd.aliasdoc)
|
|
doc = gettext(doc)
|
|
if doc:
|
|
doc = doc.splitlines()[0].rstrip()
|
|
if not doc:
|
|
doc = _("(no help text available)")
|
|
return doc
|
|
|
|
def _helpcmditem(self, name):
|
|
cmd = self.commandindex.get(name)
|
|
if cmd is None:
|
|
return None
|
|
doc = self._helpcmddoc(cmd[0], pycompat.getdoc(cmd[0]))
|
|
return " :%s: %s\n" % (name, doc)
|
|
|
|
def helplist(self, name, select=None, **opts):
|
|
h = {}
|
|
cmds = {}
|
|
for c, e in self.commands.table.iteritems():
|
|
if select and not select(c):
|
|
continue
|
|
f = c.lstrip("^").partition("|")[0]
|
|
doc = pycompat.getdoc(e[0])
|
|
if filtercmd(self.ui, f, name, doc):
|
|
continue
|
|
h[f] = self._helpcmddoc(e[0], doc)
|
|
cmds[f] = c.lstrip("^")
|
|
|
|
rst = []
|
|
if not h:
|
|
if not self.ui.quiet:
|
|
rst.append(_("no commands defined\n"))
|
|
return rst
|
|
|
|
if not self.ui.quiet:
|
|
if name == "debug":
|
|
header = _("Debug commands (internal and unsupported):\n\n")
|
|
else:
|
|
header = _("Commands:\n\n")
|
|
rst.append(header)
|
|
|
|
fns = sorted(h)
|
|
for f in fns:
|
|
if self.ui.verbose:
|
|
commacmds = cmds[f].replace("|", ", ")
|
|
rst.append(" :%s: %s\n" % (commacmds, h[f]))
|
|
else:
|
|
rst.append(" :%s: %s\n" % (f, h[f]))
|
|
|
|
return rst
|
|
|
|
def helphome(self):
|
|
rst = [
|
|
_("Mercurial Distributed SCM\n"),
|
|
"\n",
|
|
"hg COMMAND [OPTIONS]\n",
|
|
"\n",
|
|
"These are some common Mercurial commands. Use 'hg help commands' to list all "
|
|
"commands, and 'hg help COMMAND' to get help on a specific command.\n",
|
|
"\n",
|
|
]
|
|
|
|
for desc, commands in helphomecommands:
|
|
|
|
sectionrst = []
|
|
for command in commands:
|
|
cmdrst = self._helpcmditem(command)
|
|
if cmdrst:
|
|
sectionrst.append(cmdrst)
|
|
|
|
if sectionrst:
|
|
rst.append(desc + ":\n\n")
|
|
rst.extend(sectionrst)
|
|
rst.append("\n")
|
|
|
|
topics = []
|
|
for names, header, doc in helptable:
|
|
if names[0] in helphometopics:
|
|
topics.append((names[0], header))
|
|
if topics:
|
|
rst.append(_("\nAdditional help topics:\n\n"))
|
|
for t, desc in topics:
|
|
rst.append(" :%s: %s\n" % (t, desc.lower()))
|
|
|
|
localhelp = self.ui.config("help", "localhelp")
|
|
if localhelp:
|
|
rst.append("\n")
|
|
rst.append(localhelp)
|
|
|
|
return rst
|
|
|
|
def helptopic(self, name, subtopic=None):
|
|
# Look for sub-topic entry first.
|
|
header, doc = None, None
|
|
if subtopic and name in subtopics:
|
|
for names, header, doc in subtopics[name]:
|
|
if subtopic in names:
|
|
break
|
|
|
|
if not header:
|
|
for names, header, doc in helptable:
|
|
if name in names:
|
|
break
|
|
else:
|
|
raise error.UnknownCommand(name)
|
|
|
|
rst = [minirst.section(header)]
|
|
|
|
# description
|
|
if not doc:
|
|
rst.append(" %s\n" % _("(no help text available)"))
|
|
if callable(doc):
|
|
rst += [" %s\n" % l for l in doc(self.ui).splitlines()]
|
|
|
|
if not self.ui.verbose:
|
|
omitted = _("(some details hidden, use --verbose to show complete help)")
|
|
indicateomitted(rst, omitted)
|
|
|
|
try:
|
|
cmdutil.findcmd(name, self.commands.table)
|
|
rst.append(
|
|
_("\nuse 'hg help -c %s' to see help for the %s command\n")
|
|
% (name, name)
|
|
)
|
|
except error.UnknownCommand:
|
|
pass
|
|
return rst
|
|
|
|
def helpext(self, name, subtopic=None):
|
|
try:
|
|
mod = extensions.find(name)
|
|
doc = gettext(pycompat.getdoc(mod)) or _("no help text available")
|
|
except KeyError:
|
|
mod = None
|
|
doc = extensions.disabledext(name)
|
|
if not doc:
|
|
raise error.UnknownCommand(name)
|
|
|
|
if "\n" not in doc:
|
|
head, tail = doc, ""
|
|
else:
|
|
head, tail = doc.split("\n", 1)
|
|
rst = [_("%s extension - %s\n\n") % (name.rpartition(".")[-1], head)]
|
|
if tail:
|
|
rst.extend(tail.splitlines(True))
|
|
rst.append("\n")
|
|
|
|
if not self.ui.verbose:
|
|
omitted = _("(some details hidden, use --verbose to show complete help)")
|
|
indicateomitted(rst, omitted)
|
|
|
|
if mod:
|
|
try:
|
|
ct = mod.cmdtable
|
|
except AttributeError:
|
|
ct = {}
|
|
rst.extend(self.helplist(name, ct.__contains__))
|
|
else:
|
|
rst.append(
|
|
_(
|
|
"(use 'hg help extensions' for information on enabling"
|
|
" extensions)\n"
|
|
)
|
|
)
|
|
return rst
|
|
|
|
def helpextcmd(self, name, subtopic=None):
|
|
cmd, ext, mod = extensions.disabledcmd(
|
|
self.ui, name, self.ui.configbool("ui", "strict")
|
|
)
|
|
doc = gettext(pycompat.getdoc(mod))
|
|
if doc is None:
|
|
doc = _("(no help text available)")
|
|
else:
|
|
doc = doc.splitlines()[0]
|
|
|
|
rst = listexts(
|
|
_("'%s' is provided by the following extension:") % cmd,
|
|
{ext: doc},
|
|
indent=4,
|
|
showdeprecated=True,
|
|
)
|
|
rst.append("\n")
|
|
rst.append(
|
|
_("(use 'hg help extensions' for information on enabling extensions)\n")
|
|
)
|
|
return rst
|
|
|
|
|
|
def help_(ui, commands, name, unknowncmd=False, full=True, subtopic=None, **opts):
|
|
"""
|
|
Generate the help for 'name' as unformatted restructured text. If
|
|
'name' is None, describe the commands available.
|
|
"""
|
|
|
|
opts = pycompat.byteskwargs(opts)
|
|
dispatch = _helpdispatch(ui, commands, unknowncmd, full, subtopic, **opts)
|
|
|
|
rst = []
|
|
kw = opts.get("keyword")
|
|
if kw or name is None and any(opts[o] for o in opts):
|
|
matches = topicmatch(ui, commands, name or "")
|
|
helpareas = []
|
|
if opts.get("extension"):
|
|
helpareas += [("extensions", _("Extensions"))]
|
|
if opts.get("command"):
|
|
helpareas += [("commands", _("Commands"))]
|
|
if not helpareas:
|
|
helpareas = [
|
|
("topics", _("Topics")),
|
|
("commands", _("Commands")),
|
|
("extensions", _("Extensions")),
|
|
("extensioncommands", _("Extension Commands")),
|
|
]
|
|
for t, title in helpareas:
|
|
if matches[t]:
|
|
rst.append("%s:\n\n" % title)
|
|
rst.extend(minirst.maketable(sorted(matches[t]), 1))
|
|
rst.append("\n")
|
|
if not rst:
|
|
msg = _("no matches")
|
|
hint = _("try 'hg help' for a list of topics")
|
|
raise error.Abort(msg, hint=hint)
|
|
elif name == "commands":
|
|
if not ui.quiet:
|
|
rst = [_("Mercurial Distributed SCM\n"), "\n"]
|
|
rst.extend(dispatch.helplist(None, None, **pycompat.strkwargs(opts)))
|
|
elif name:
|
|
rst = dispatch.dispatch(name)
|
|
else:
|
|
rst = dispatch.helphome()
|
|
|
|
return "".join(rst)
|
|
|
|
|
|
def formattedhelp(ui, commands, name, keep=None, unknowncmd=False, full=True, **opts):
|
|
"""get help for a given topic (as a dotted name) as rendered rst
|
|
|
|
Either returns the rendered help text or raises an exception.
|
|
"""
|
|
if keep is None:
|
|
keep = []
|
|
else:
|
|
keep = list(keep) # make a copy so we can mutate this later
|
|
fullname = name
|
|
section = None
|
|
subtopic = None
|
|
if name and "." in name:
|
|
name, remaining = name.split(".", 1)
|
|
remaining = encoding.lower(remaining)
|
|
if "." in remaining:
|
|
subtopic, section = remaining.split(".", 1)
|
|
else:
|
|
if name in subtopics:
|
|
subtopic = remaining
|
|
else:
|
|
section = remaining
|
|
textwidth = ui.configint("ui", "textwidth")
|
|
termwidth = ui.termwidth() - 2
|
|
if textwidth <= 0 or termwidth < textwidth:
|
|
textwidth = termwidth
|
|
text = help_(
|
|
ui, commands, name, subtopic=subtopic, unknowncmd=unknowncmd, full=full, **opts
|
|
)
|
|
|
|
formatted, pruned = minirst.format(text, textwidth, keep=keep, section=section)
|
|
|
|
# We could have been given a weird ".foo" section without a name
|
|
# to look for, or we could have simply failed to found "foo.bar"
|
|
# because bar isn't a section of foo
|
|
if section and not (formatted and name):
|
|
raise error.Abort(_("help section not found: %s") % fullname)
|
|
|
|
if "verbose" in pruned:
|
|
keep.append("omitted")
|
|
else:
|
|
keep.append("notomitted")
|
|
formatted, pruned = minirst.format(text, textwidth, keep=keep, section=section)
|
|
return formatted
|