gendoc.py
684 lines
| 21.3 KiB
| text/x-python
|
PythonLexer
/ doc / gendoc.py
Gregory Szorc
|
r46434 | #!/usr/bin/env python3 | ||
Takumi IINO
|
r19425 | """usage: %s DOC ... | ||
where DOC is the name of a document | ||||
""" | ||||
Pulkit Goyal
|
r28966 | |||
import os | ||||
import sys | ||||
import textwrap | ||||
Ludovic Chabant
|
r52913 | import argparse | ||
Gregory Szorc
|
r27330 | |||
Matt Harbison
|
r41040 | try: | ||
import msvcrt | ||||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r41040 | msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) | ||
msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY) | ||||
except ImportError: | ||||
pass | ||||
Gregory Szorc
|
r27330 | # This script is executed during installs and may not have C extensions | ||
# available. Relax C module requirements. | ||||
Augie Fackler
|
r43906 | os.environ['HGMODULEPOLICY'] = 'allow' | ||
Benoit Boissinot
|
r1814 | # import from the live mercurial repo | ||
Matt Harbison
|
r50045 | sys.path.insert(0, os.path.abspath("..")) | ||
Augie Fackler
|
r43346 | from mercurial import demandimport | ||
demandimport.enable() | ||||
Pulkit Goyal
|
r28966 | from mercurial import ( | ||
commands, | ||||
Matt Harbison
|
r41059 | encoding, | ||
Pulkit Goyal
|
r28966 | extensions, | ||
Kyle Lippincott
|
r47266 | fancyopts, | ||
Pulkit Goyal
|
r28966 | help, | ||
minirst, | ||||
Matt Harbison
|
r41040 | pycompat, | ||
Pulkit Goyal
|
r28966 | ui as uimod, | ||
) | ||||
from mercurial.i18n import ( | ||||
gettext, | ||||
_, | ||||
) | ||||
Kyle Lippincott
|
r45509 | from mercurial.utils import stringutil | ||
Pulkit Goyal
|
r28966 | |||
table = commands.table | ||||
globalopts = commands.globalopts | ||||
helptable = help.helptable | ||||
loaddoc = help.loaddoc | ||||
Benoit Boissinot
|
r1814 | |||
Augie Fackler
|
r43346 | |||
Benoit Boissinot
|
r1814 | def get_desc(docstr): | ||
if not docstr: | ||||
Matt Harbison
|
r41040 | return b"", b"" | ||
Benoit Boissinot
|
r1814 | # sanitize | ||
Matt Harbison
|
r41040 | docstr = docstr.strip(b"\n") | ||
Benoit Boissinot
|
r1814 | docstr = docstr.rstrip() | ||
shortdesc = docstr.splitlines()[0].strip() | ||||
Matt Harbison
|
r41040 | i = docstr.find(b"\n") | ||
Benoit Boissinot
|
r1814 | if i != -1: | ||
Augie Fackler
|
r43346 | desc = docstr[i + 2 :] | ||
Benoit Boissinot
|
r1814 | else: | ||
Erik Zielke
|
r12780 | desc = shortdesc | ||
Matt Harbison
|
r41040 | desc = textwrap.dedent(desc.decode('latin1')).encode('latin1') | ||
Erik Zielke
|
r12780 | |||
Benoit Boissinot
|
r1814 | return (shortdesc, desc) | ||
Augie Fackler
|
r43346 | |||
Benoit Boissinot
|
r1814 | def get_opts(opts): | ||
FUJIWARA Katsunori
|
r11321 | for opt in opts: | ||
if len(opt) == 5: | ||||
shortopt, longopt, default, desc, optlabel = opt | ||||
else: | ||||
shortopt, longopt, default, desc = opt | ||||
Matt Harbison
|
r41040 | optlabel = _(b"VALUE") | ||
Benoit Boissinot
|
r1814 | allopts = [] | ||
if shortopt: | ||||
Matt Harbison
|
r41040 | allopts.append(b"-%s" % shortopt) | ||
Benoit Boissinot
|
r1814 | if longopt: | ||
Matt Harbison
|
r41040 | allopts.append(b"--%s" % longopt) | ||
FUJIWARA Katsunori
|
r20081 | if isinstance(default, list): | ||
Matt Harbison
|
r41040 | allopts[-1] += b" <%s[+]>" % optlabel | ||
FUJIWARA Katsunori
|
r20081 | elif (default is not None) and not isinstance(default, bool): | ||
Matt Harbison
|
r41040 | allopts[-1] += b" <%s>" % optlabel | ||
if b'\n' in desc: | ||||
Simon Heimberg
|
r20655 | # only remove line breaks and indentation | ||
Matt Harbison
|
r41040 | desc = b' '.join(l.lstrip() for l in desc.split(b'\n')) | ||
Kyle Lippincott
|
r47266 | if isinstance(default, fancyopts.customopt): | ||
default = default.getdefaultvalue() | ||||
Kyle Lippincott
|
r45509 | if default: | ||
default = stringutil.forcebytestr(default) | ||||
desc += _(b" (default: %s)") % default | ||||
Matt Harbison
|
r41040 | yield (b", ".join(allopts), desc) | ||
Benoit Boissinot
|
r1814 | |||
Augie Fackler
|
r43346 | |||
Erik Zielke
|
r12756 | def get_cmd(cmd, cmdtable): | ||
Benoit Boissinot
|
r1814 | d = {} | ||
Erik Zielke
|
r12756 | attr = cmdtable[cmd] | ||
Matt Harbison
|
r41040 | cmds = cmd.lstrip(b"^").split(b"|") | ||
Benoit Boissinot
|
r1814 | |||
Matt Harbison
|
r41040 | d[b'cmd'] = cmds[0] | ||
d[b'aliases'] = cmd.split(b"|")[1:] | ||||
d[b'desc'] = get_desc(gettext(pycompat.getdoc(attr[0]))) | ||||
d[b'opts'] = list(get_opts(attr[1])) | ||||
Matt Mackall
|
r7376 | |||
Matt Harbison
|
r41040 | s = b'hg ' + cmds[0] | ||
Matt Mackall
|
r7376 | if len(attr) > 2: | ||
Matt Harbison
|
r41040 | if not attr[2].startswith(b'hg'): | ||
s += b' ' + attr[2] | ||||
Matt Mackall
|
r7376 | else: | ||
s = attr[2] | ||||
Matt Harbison
|
r41040 | d[b'synopsis'] = s.strip() | ||
Matt Mackall
|
r7376 | |||
Benoit Boissinot
|
r1814 | return d | ||
Augie Fackler
|
r43346 | |||
Ludovic Chabant
|
r52913 | def showdoc(ui, debugcmds=False): | ||
Benoit Boissinot
|
r1814 | # print options | ||
Matt Harbison
|
r41040 | ui.write(minirst.section(_(b"Options"))) | ||
FUJIWARA Katsunori
|
r20081 | multioccur = False | ||
Benoit Boissinot
|
r1814 | for optstr, desc in get_opts(globalopts): | ||
Matt Harbison
|
r41040 | ui.write(b"%s\n %s\n\n" % (optstr, desc)) | ||
if optstr.endswith(b"[+]>"): | ||||
FUJIWARA Katsunori
|
r20081 | multioccur = True | ||
if multioccur: | ||||
Matt Harbison
|
r41040 | ui.write(_(b"\n[+] marked option can be specified multiple times\n")) | ||
ui.write(b"\n") | ||||
Benoit Boissinot
|
r1814 | |||
# print cmds | ||||
Matt Harbison
|
r41040 | ui.write(minirst.section(_(b"Commands"))) | ||
Ludovic Chabant
|
r52913 | commandprinter( | ||
ui, | ||||
table, | ||||
minirst.subsection, | ||||
minirst.subsubsection, | ||||
debugcmds=debugcmds, | ||||
) | ||||
Erik Zielke
|
r12756 | |||
Takumi IINO
|
r19233 | # print help topics | ||
# The config help topic is included in the hgrc.5 man page. | ||||
Ludovic Chabant
|
r52913 | topics = findtopics(helptable, exclude=[b'config']) | ||
helpprinter(ui, topics, minirst.section) | ||||
Erik Zielke
|
r12756 | |||
Matt Harbison
|
r41040 | ui.write(minirst.section(_(b"Extensions"))) | ||
Augie Fackler
|
r43346 | ui.write( | ||
_( | ||||
b"This section contains help for extensions that are " | ||||
b"distributed together with Mercurial. Help for other " | ||||
b"extensions is available in the help system." | ||||
) | ||||
) | ||||
ui.write( | ||||
( | ||||
b"\n\n" | ||||
b".. contents::\n" | ||||
b" :class: htmlonly\n" | ||||
b" :local:\n" | ||||
b" :depth: 1\n\n" | ||||
) | ||||
) | ||||
Erik Zielke
|
r12781 | |||
for extensionname in sorted(allextensionnames()): | ||||
Jun Wu
|
r27660 | mod = extensions.load(ui, extensionname, None) | ||
Dan Villiom Podlaski Christiansen
|
r18748 | ui.write(minirst.subsection(extensionname)) | ||
r52737 | ext_doc = help.ext_help(ui, mod) | |||
ui.write(b"%s\n\n" % ext_doc) | ||||
Erik Zielke
|
r12781 | cmdtable = getattr(mod, 'cmdtable', None) | ||
if cmdtable: | ||||
Matt Harbison
|
r41040 | ui.write(minirst.subsubsection(_(b'Commands'))) | ||
Augie Fackler
|
r43346 | commandprinter( | ||
ui, | ||||
cmdtable, | ||||
minirst.subsubsubsection, | ||||
minirst.subsubsubsubsection, | ||||
Ludovic Chabant
|
r52913 | debugcmds=debugcmds, | ||
Augie Fackler
|
r43346 | ) | ||
Erik Zielke
|
r12781 | |||
Ludovic Chabant
|
r52914 | def showcommandlist(ui, debugcmds=False): | ||
"""Render a plain text list of all command names | ||||
Args: | ||||
ui: the UI object to output to | ||||
debugcmds: whether to include debug commands | ||||
""" | ||||
cmdnames = allcommandnames(table, debugcmds=debugcmds) | ||||
for mainname in cmdnames.keys(): | ||||
# Make does not like semicolons in filenames (or what it | ||||
# considers as filenames). We use command names as targets so | ||||
# it applies here. For now let's skip commands with semicolons | ||||
# in them (at this time it only includes the `admin::verify` | ||||
# advanced command). | ||||
if b'::' in mainname: | ||||
continue | ||||
ui.write(mainname) | ||||
ui.write(b" ") | ||||
def showtopiclist(ui): | ||||
"""Render a plain text list of all help topic names | ||||
Args: | ||||
ui: the UI object to output to | ||||
""" | ||||
for topic in helptable: | ||||
topicname = topic[0][0] | ||||
if help.filtertopic(ui, topicname): | ||||
continue | ||||
ui.write(topicname) | ||||
ui.write(b" ") | ||||
def showextensionlist(ui): | ||||
"""Render a plain text list of all extension names | ||||
Args: | ||||
ui: the UI object to output to | ||||
""" | ||||
for extensionname in allextensionnames(): | ||||
ui.write(extensionname) | ||||
ui.write(b" ") | ||||
def showhelpindex(ui, debugcmds=False): | ||||
"""Render restructured text for a complete mercurial help index | ||||
This index will show a list of commands, followed by a list of help topics, | ||||
and finally a list of extensions. These lists are split in categories and | ||||
ordered 'nicely' as defined by alphabetical and categeory order. | ||||
Each entry in this index is a reference to the specific help page of the | ||||
command, topic, or extension at hand. | ||||
""" | ||||
ui.write(minirst.section(_(b"Mercurial Distributed SCM"))) | ||||
missingdoc = _(b"(no help text available)") | ||||
cats, h, syns = help._getcategorizedhelpcmds(ui, table, None) | ||||
ui.write(minirst.subsection(_(b"Commands"))) | ||||
for cat in help.CATEGORY_ORDER: | ||||
catfns = sorted(cats.get(cat, [])) | ||||
if not catfns: | ||||
continue | ||||
catname = gettext(help.CATEGORY_NAMES[cat]) | ||||
ui.write(minirst.subsubsection(catname)) | ||||
for c in catfns: | ||||
url = b'hg-%s.html' % c | ||||
ui.write(b" :`%s <%s>`__: %s" % (c, url, h[c])) | ||||
syns[c].remove(c) | ||||
if syns[c]: | ||||
ui.write(_(b" (aliases: *%s*)") % (b', '.join(syns[c]))) | ||||
ui.write(b"\n") | ||||
ui.write(b"\n\n") | ||||
ui.write(b"\n\n") | ||||
ui.write(minirst.subsection(_(b"Additional Help Topics"))) | ||||
topiccats, topicsyns = help._getcategorizedhelptopics(ui, helptable) | ||||
for cat in help.TOPIC_CATEGORY_ORDER: | ||||
topics = topiccats.get(cat, []) | ||||
if not topics: | ||||
continue | ||||
catname = gettext(help.TOPIC_CATEGORY_NAMES[cat]) | ||||
ui.write(minirst.subsubsection(catname)) | ||||
for t, desc in topics: | ||||
url = b'topic-%s.html' % t | ||||
ui.write(b" :`%s <%s>`__: %s" % (t, url, desc)) | ||||
topicsyns[t].remove(t) | ||||
if topicsyns[t]: | ||||
ui.write(_(b" (aliases: *%s*)") % (b', '.join(topicsyns[t]))) | ||||
ui.write(b"\n") | ||||
ui.write(b"\n\n") | ||||
ui.write(b"\n\n") | ||||
# Add an alphabetical list of extensions, categorized by group. | ||||
sectionkeywords = [ | ||||
(b"(ADVANCED)", _(b"(ADVANCED)")), | ||||
(b"(EXPERIMENTAL)", _(b"(EXPERIMENTAL)")), | ||||
(b"(DEPRECATED)", _(b"(DEPRECATED)")), | ||||
] | ||||
extensionsections = [ | ||||
(b"Extensions", []), | ||||
(b"Advanced Extensions", []), | ||||
(b"Experimental Extensions", []), | ||||
(b"Deprecated Extensions", []), | ||||
] | ||||
for extensionname in allextensionnames(): | ||||
mod = extensions.load(ui, extensionname, None) | ||||
shortdoc, longdoc = _splitdoc(mod) | ||||
for i, kwds in enumerate(sectionkeywords): | ||||
if any([kwd in shortdoc for kwd in kwds]): | ||||
extensionsections[i + 1][1].append( | ||||
(extensionname, mod, shortdoc) | ||||
) | ||||
break | ||||
else: | ||||
extensionsections[0][1].append((extensionname, mod, shortdoc)) | ||||
for sectiontitle, extinfos in extensionsections: | ||||
ui.write(minirst.subsection(_(sectiontitle))) | ||||
for extinfo in sorted(extinfos, key=lambda ei: ei[0]): | ||||
extensionname, mod, shortdoc = extinfo | ||||
url = b'ext-%s.html' % extensionname | ||||
ui.write( | ||||
minirst.subsubsection(b'`%s <%s>`__' % (extensionname, url)) | ||||
) | ||||
ui.write(shortdoc) | ||||
ui.write(b'\n\n') | ||||
cmdtable = getattr(mod, 'cmdtable', None) | ||||
if cmdtable: | ||||
cmdnames = allcommandnames(cmdtable, debugcmds=debugcmds) | ||||
for f in sorted(cmdnames.keys()): | ||||
d = get_cmd(cmdnames[f], cmdtable) | ||||
ui.write(b':%s: ' % d[b'cmd']) | ||||
ui.write(d[b'desc'][0] or (missingdoc + b"\n")) | ||||
ui.write(b'\n') | ||||
ui.write(b'\n') | ||||
def showcommand(ui, mainname): | ||||
# Always pass debugcmds=True so that we find whatever command we are told | ||||
# to display. | ||||
cmdnames = allcommandnames(table, debugcmds=True) | ||||
allnames = cmdnames[mainname] | ||||
d = get_cmd(allnames, table) | ||||
header = _rendertpl( | ||||
'cmdheader.txt', | ||||
{ | ||||
'cmdname': mainname, | ||||
'cmdtitle': minirst.section(b'hg ' + mainname), | ||||
'cmdshortdesc': minirst.subsection(d[b'desc'][0]), | ||||
'cmdlongdesc': d[b'desc'][1], | ||||
'cmdsynopsis': d[b'synopsis'], | ||||
}, | ||||
) | ||||
ui.write(header.encode()) | ||||
_optionsprinter(ui, d, minirst.subsubsection) | ||||
if d[b'aliases']: | ||||
ui.write(minirst.subsubsection(_(b"Aliases"))) | ||||
ui.write(b"::\n\n ") | ||||
ui.write(b", ".join(d[b'aliases'])) | ||||
ui.write(b"\n") | ||||
def _splitdoc(obj): | ||||
objdoc = pycompat.getdoc(obj) | ||||
firstnl = objdoc.find(b'\n') | ||||
if firstnl > 0: | ||||
shortdoc = objdoc[:firstnl] | ||||
longdoc = objdoc[firstnl + 1 :] | ||||
else: | ||||
shortdoc = objdoc | ||||
longdoc = '' | ||||
return shortdoc.lstrip(), longdoc.lstrip() | ||||
def _rendertpl(tplname, data): | ||||
tplpath = os.path.join(os.path.dirname(__file__), 'templates', tplname) | ||||
with open(tplpath, 'r') as f: | ||||
tpl = f.read() | ||||
if isinstance(tpl, bytes): | ||||
tpl = tpl.decode() | ||||
for k in data: | ||||
data[k] = data[k].decode() | ||||
return tpl % data | ||||
Ludovic Chabant
|
r52913 | def gettopicstable(): | ||
Takumi IINO
|
r19424 | extrahelptable = [ | ||
Matt Harbison
|
r41040 | ([b"common"], b'', loaddoc(b'common'), help.TOPIC_CATEGORY_MISC), | ||
([b"hg.1"], b'', loaddoc(b'hg.1'), help.TOPIC_CATEGORY_CONFIG), | ||||
([b"hg-ssh.8"], b'', loaddoc(b'hg-ssh.8'), help.TOPIC_CATEGORY_CONFIG), | ||||
Augie Fackler
|
r43346 | ( | ||
[b"hgignore.5"], | ||||
b'', | ||||
loaddoc(b'hgignore.5'), | ||||
help.TOPIC_CATEGORY_CONFIG, | ||||
), | ||||
Matt Harbison
|
r41040 | ([b"hgrc.5"], b'', loaddoc(b'hgrc.5'), help.TOPIC_CATEGORY_CONFIG), | ||
Ludovic Chabant
|
r52913 | ([b"hg-ssh.8.gendoc"], b'', b'', help.TOPIC_CATEGORY_CONFIG), | ||
Augie Fackler
|
r43346 | ( | ||
[b"hgignore.5.gendoc"], | ||||
b'', | ||||
loaddoc(b'hgignore'), | ||||
help.TOPIC_CATEGORY_CONFIG, | ||||
), | ||||
( | ||||
[b"hgrc.5.gendoc"], | ||||
b'', | ||||
loaddoc(b'config'), | ||||
help.TOPIC_CATEGORY_CONFIG, | ||||
), | ||||
Takumi IINO
|
r19424 | ] | ||
Ludovic Chabant
|
r52913 | return helptable + extrahelptable | ||
Takumi IINO
|
r19424 | |||
Augie Fackler
|
r43346 | |||
Ludovic Chabant
|
r52913 | def findtopics(helptable, include=[], exclude=[]): | ||
"""Find topics whose names match the given include/exclude rules | ||||
Note that exclude rules take precedence over include rules. | ||||
""" | ||||
found = [] | ||||
rdamazio@google.com
|
r40327 | for h in helptable: | ||
names, sec, doc = h[0:3] | ||||
Takumi IINO
|
r19233 | if exclude and names[0] in exclude: | ||
continue | ||||
if include and names[0] not in include: | ||||
continue | ||||
Ludovic Chabant
|
r52913 | found.append((names, sec, doc)) | ||
return found | ||||
def showtopic(ui, topic, wraptpl=False): | ||||
"""Render a help topic | ||||
Args: | ||||
ui: the UI object to output to | ||||
topic: the topic name to output | ||||
wraptpl: whether to wrap the output in the individual help topic | ||||
pages' header/footer | ||||
""" | ||||
found = findtopics(gettopicstable(), include=[topic]) | ||||
if not found: | ||||
ui.write_err(_(b"ERROR: no such topic: %s\n") % topic) | ||||
sys.exit(1) | ||||
if wraptpl: | ||||
header = _rendertpl( | ||||
'topicheader.txt', | ||||
{'topicname': topic, 'topictitle': minirst.section(found[0][1])}, | ||||
) | ||||
ui.write(header.encode()) | ||||
helpprinter(ui, found, None) | ||||
return True | ||||
def helpprinter(ui, topics, sectionfunc): | ||||
"""Print a help topic | ||||
Args: | ||||
ui: the UI object to output to | ||||
topics: a list of help topics to output | ||||
sectionfunc: a callback to write the section title | ||||
""" | ||||
for h in topics: | ||||
names, sec, doc = h[0:3] | ||||
Takumi IINO
|
r19233 | for name in names: | ||
Matt Harbison
|
r41040 | ui.write(b".. _%s:\n" % name) | ||
ui.write(b"\n") | ||||
Takumi IINO
|
r19233 | if sectionfunc: | ||
ui.write(sectionfunc(sec)) | ||||
Augie Fackler
|
r21793 | if callable(doc): | ||
Yuya Nishihara
|
r26413 | doc = doc(ui) | ||
Takumi IINO
|
r19233 | ui.write(doc) | ||
Matt Harbison
|
r41040 | ui.write(b"\n") | ||
Takumi IINO
|
r19233 | |||
Augie Fackler
|
r43346 | |||
Ludovic Chabant
|
r52914 | def showextension(ui, extensionname, debugcmds=False): | ||
"""Render the help text for an extension | ||||
Args: | ||||
ui: the UI object to output to | ||||
extensionname: the name of the extension to output | ||||
debugcmds: whether to include the extension's debug commands, if any | ||||
""" | ||||
mod = extensions.load(ui, extensionname, None) | ||||
header = _rendertpl( | ||||
'extheader.txt', | ||||
{'extname': extensionname, 'exttitle': minirst.section(extensionname)}, | ||||
) | ||||
ui.write(header.encode()) | ||||
shortdoc, longdoc = _splitdoc(mod) | ||||
if shortdoc: | ||||
ui.write(b"%s\n\n" % gettext(shortdoc)) | ||||
if longdoc: | ||||
ui.write(minirst.subsection(_(b"Description"))) | ||||
ui.write(b"%s\n\n" % gettext(longdoc)) | ||||
cmdtable = getattr(mod, 'cmdtable', None) | ||||
if cmdtable: | ||||
ui.write(minirst.subsection(_(b'Commands'))) | ||||
commandprinter( | ||||
ui, | ||||
cmdtable, | ||||
minirst.subsubsection, | ||||
minirst.subsubsubsection, | ||||
debugcmds=debugcmds, | ||||
) | ||||
Ludovic Chabant
|
r52913 | def commandprinter(ui, cmdtable, sectionfunc, subsectionfunc, debugcmds=False): | ||
Sietse Brouwer
|
r42438 | """Render restructuredtext describing a list of commands and their | ||
documentations, grouped by command category. | ||||
Args: | ||||
ui: UI object to write the output to | ||||
cmdtable: a dict that maps a string of the command name plus its aliases | ||||
(separated with pipes) to a 3-tuple of (the command's function, a list | ||||
of its option descriptions, and a string summarizing available | ||||
options). Example, with aliases added for demonstration purposes: | ||||
'phase|alias1|alias2': ( | ||||
<function phase at 0x7f0816b05e60>, | ||||
[ ('p', 'public', False, 'set changeset phase to public'), | ||||
..., | ||||
('r', 'rev', [], 'target revision', 'REV')], | ||||
'[-p|-d|-s] [-f] [-r] [REV...]' | ||||
) | ||||
sectionfunc: minirst function to format command category headers | ||||
subsectionfunc: minirst function to format command headers | ||||
""" | ||||
Ludovic Chabant
|
r52913 | h = allcommandnames(cmdtable, debugcmds=debugcmds) | ||
Benoit Boissinot
|
r1814 | cmds = h.keys() | ||
Sietse Brouwer
|
r42435 | def helpcategory(cmd): | ||
"""Given a canonical command name from `cmds` (above), retrieve its | ||||
help category. If helpcategory is None, default to CATEGORY_NONE. | ||||
""" | ||||
fullname = h[cmd] | ||||
details = cmdtable[fullname] | ||||
helpcategory = details[0].helpcategory | ||||
return helpcategory or help.registrar.command.CATEGORY_NONE | ||||
Sietse Brouwer
|
r42436 | cmdsbycategory = {category: [] for category in help.CATEGORY_ORDER} | ||
for cmd in cmds: | ||||
# If a command category wasn't registered, the command won't get | ||||
# rendered below, so we raise an AssertionError. | ||||
if helpcategory(cmd) not in cmdsbycategory: | ||||
raise AssertionError( | ||||
"The following command did not register its (category) in " | ||||
Augie Fackler
|
r43346 | "help.CATEGORY_ORDER: %s (%s)" % (cmd, helpcategory(cmd)) | ||
) | ||||
Sietse Brouwer
|
r42436 | cmdsbycategory[helpcategory(cmd)].append(cmd) | ||
Sietse Brouwer
|
r42435 | # Print the help for each command. We present the commands grouped by | ||
# category, and we use help.CATEGORY_ORDER as a guide for a helpful order | ||||
# in which to present the categories. | ||||
for category in help.CATEGORY_ORDER: | ||||
categorycmds = cmdsbycategory[category] | ||||
if not categorycmds: | ||||
# Skip empty categories | ||||
continue | ||||
# Print a section header for the category. | ||||
# For now, the category header is at the same level as the headers for | ||||
# the commands in the category; this is fixed in the next commit. | ||||
ui.write(sectionfunc(help.CATEGORY_NAMES[category])) | ||||
# Print each command in the category | ||||
for f in sorted(categorycmds): | ||||
Sietse Brouwer
|
r42434 | d = get_cmd(h[f], cmdtable) | ||
Sietse Brouwer
|
r42438 | ui.write(subsectionfunc(d[b'cmd'])) | ||
Sietse Brouwer
|
r42434 | # short description | ||
ui.write(d[b'desc'][0]) | ||||
Martin Geisler
|
r12813 | # synopsis | ||
Sietse Brouwer
|
r42434 | ui.write(b"::\n\n") | ||
synopsislines = d[b'synopsis'].splitlines() | ||||
for line in synopsislines: | ||||
# some commands (such as rebase) have a multi-line | ||||
# synopsis | ||||
ui.write(b" %s\n" % line) | ||||
ui.write(b'\n') | ||||
# description | ||||
ui.write(b"%s\n\n" % d[b'desc'][1]) | ||||
Ludovic Chabant
|
r52913 | |||
Sietse Brouwer
|
r42434 | # options | ||
Ludovic Chabant
|
r52913 | def _optsection(s): | ||
return b"%s:\n\n" % s | ||||
_optionsprinter(ui, d, _optsection) | ||||
Sietse Brouwer
|
r42434 | # aliases | ||
if d[b'aliases']: | ||||
Kyle Lippincott
|
r47268 | # Note the empty comment, this is required to separate this | ||
# (which should be a blockquote) from any preceding things (such | ||||
# as a definition list). | ||||
ui.write( | ||||
_(b"..\n\n aliases: %s\n\n") % b" ".join(d[b'aliases']) | ||||
) | ||||
Benoit Boissinot
|
r1814 | |||
Augie Fackler
|
r43346 | |||
Ludovic Chabant
|
r52913 | def _optionsprinter(ui, cmd, sectionfunc): | ||
"""Outputs the list of options for a given command object""" | ||||
opt_output = list(cmd[b'opts']) | ||||
if opt_output: | ||||
opts_len = max([len(line[0]) for line in opt_output]) | ||||
ui.write(sectionfunc(_(b"Options"))) | ||||
multioccur = False | ||||
for optstr, desc in opt_output: | ||||
if desc: | ||||
s = b"%-*s %s" % (opts_len, optstr, desc) | ||||
else: | ||||
s = optstr | ||||
ui.write(b"%s\n" % s) | ||||
if optstr.endswith(b"[+]>"): | ||||
multioccur = True | ||||
if multioccur: | ||||
ui.write( | ||||
_(b"\n[+] marked option can be specified multiple times\n") | ||||
) | ||||
ui.write(b"\n") | ||||
def allcommandnames(cmdtable, debugcmds=False): | ||||
"""Get a collection of all command names in the given command table | ||||
Args: | ||||
cmdtable: the command table to get the names from | ||||
debugcmds: whether to include debug commands | ||||
Returns a dictionary where the keys are the main command names, and the | ||||
values are the "raw" names (in the form of `name|alias1|alias2`). | ||||
""" | ||||
allcmdnames = {} | ||||
for rawnames, attr in cmdtable.items(): | ||||
mainname = rawnames.split(b"|")[0].lstrip(b"^") | ||||
if not debugcmds and mainname.startswith(b"debug"): | ||||
continue | ||||
allcmdnames[mainname] = rawnames | ||||
return allcmdnames | ||||
Erik Zielke
|
r12781 | def allextensionnames(): | ||
Ludovic Chabant
|
r52913 | """Get a set of all known extension names""" | ||
Matt Harbison
|
r41040 | return set(extensions.enabled().keys()) | set(extensions.disabled().keys()) | ||
Erik Zielke
|
r12781 | |||
Augie Fackler
|
r43346 | |||
Benoit Boissinot
|
r1814 | if __name__ == "__main__": | ||
Ludovic Chabant
|
r52913 | parser = argparse.ArgumentParser( | ||
prog='gendoc', description="Generate mercurial documentation files" | ||||
) | ||||
parser.add_argument('doc', default='hg.1.gendoc', nargs='?') | ||||
parser.add_argument( | ||||
'-d', | ||||
'--debug-cmds', | ||||
action='store_true', | ||||
help="Show debug commands in help pages", | ||||
) | ||||
args = parser.parse_args() | ||||
doc = encoding.strtolocal(args.doc) | ||||
debugcmds = args.debug_cmds | ||||
Takumi IINO
|
r19425 | |||
Yuya Nishihara
|
r30559 | ui = uimod.ui.load() | ||
Kyle Lippincott
|
r47267 | # Trigger extensions to load. This is disabled by default because it uses | ||
# the current user's configuration, which is often not what is wanted. | ||||
if encoding.environ.get(b'GENDOC_LOAD_CONFIGURED_EXTENSIONS', b'0') != b'0': | ||||
extensions.loadall(ui) | ||||
Ludovic Chabant
|
r52913 | # ui.debugflag determines if the help module returns debug commands to us. | ||
ui.debugflag = debugcmds | ||||
Ludovic Chabant
|
r52914 | # Render the 'all-in-one' giant documentation file | ||
Matt Harbison
|
r41040 | if doc == b'hg.1.gendoc': | ||
Yuya Nishihara
|
r26412 | showdoc(ui) | ||
Ludovic Chabant
|
r52914 | # Render a command/help-topic/extension name list (for internal use) | ||
elif doc == b'commandlist': | ||||
showcommandlist(ui, debugcmds=debugcmds) | ||||
elif doc == b'topiclist': | ||||
showtopiclist(ui) | ||||
elif doc == b'extensionlist': | ||||
showextensionlist(ui) | ||||
# Render the help index/main page | ||||
elif doc == b'index': | ||||
showhelpindex(ui, debugcmds=debugcmds) | ||||
# Render an individual command/help-topic/extension page | ||||
elif doc.startswith(b'cmd-'): | ||||
showcommand(ui, doc[4:]) | ||||
elif doc.startswith(b'topic-'): | ||||
showtopic(ui, doc[6:], wraptpl=True) | ||||
elif doc.startswith(b'ext-'): | ||||
showextension(ui, doc[4:], debugcmds=debugcmds) | ||||
# Render a help-topic page without any title/footer, for later inclusion | ||||
# into a hand-written help text file | ||||
Takumi IINO
|
r19425 | else: | ||
Ludovic Chabant
|
r52913 | showtopic(ui, doc) | ||