aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur Zamarin <arthurzam@gentoo.org>2023-10-23 20:03:29 +0300
committerArthur Zamarin <arthurzam@gentoo.org>2023-10-23 20:03:29 +0300
commit388e86dfe35acfc218cef2fc31f5c665fa0f8471 (patch)
tree92f09615d33874fc362a0123323ff4feab45fbbe
parenteclassdoc: parse default and initial values (diff)
downloadpkgcore-388e86dfe35acfc218cef2fc31f5c665fa0f8471.tar.gz
pkgcore-388e86dfe35acfc218cef2fc31f5c665fa0f8471.tar.bz2
pkgcore-388e86dfe35acfc218cef2fc31f5c665fa0f8471.zip
pmaint eclass: add devbook output format
Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
-rw-r--r--src/pkgcore/ebuild/eclass.py6
-rw-r--r--src/pkgcore/scripts/pmaint.py2
-rw-r--r--src/pkgcore/util/rst2devbook.py245
3 files changed, 252 insertions, 1 deletions
diff --git a/src/pkgcore/ebuild/eclass.py b/src/pkgcore/ebuild/eclass.py
index f1a0cacdf..f5ae214f7 100644
--- a/src/pkgcore/ebuild/eclass.py
+++ b/src/pkgcore/ebuild/eclass.py
@@ -720,3 +720,9 @@ class EclassDoc(AttrDict):
from docutils.writers import html5_polyglot
return self._to_docutils(html5_polyglot.Writer())
+
+ def to_devbook(self):
+ """Convert eclassdoc object to an HTML 5 document."""
+ from pkgcore.util.rst2devbook import DevBookWriter
+
+ return self._to_docutils(DevBookWriter(self.name))
diff --git a/src/pkgcore/scripts/pmaint.py b/src/pkgcore/scripts/pmaint.py
index 390f7b287..9c92f655e 100644
--- a/src/pkgcore/scripts/pmaint.py
+++ b/src/pkgcore/scripts/pmaint.py
@@ -501,7 +501,7 @@ eclass_opts.add_argument(
"--format",
help="output format",
default="man",
- choices=("rst", "man", "html"),
+ choices=("rst", "man", "html", "devbook"),
)
eclass_opts.add_argument(
"-r",
diff --git a/src/pkgcore/util/rst2devbook.py b/src/pkgcore/util/rst2devbook.py
new file mode 100644
index 000000000..f08041ccc
--- /dev/null
+++ b/src/pkgcore/util/rst2devbook.py
@@ -0,0 +1,245 @@
+"""A docutils's writer for DevBook format [#]_
+
+.. [#] https://devmanual.gentoo.org/appendices/devbook-guide/index.html
+"""
+
+from docutils import nodes, writers
+
+import lxml.etree as etree
+
+
+class DevBookWriter(writers.Writer):
+ """A docutils writer for DevBook."""
+
+ def __init__(self, eclass):
+ """Initialize the writer. Takes the root element of the resulting
+ DocBook output as its sole argument."""
+ super().__init__()
+ self.eclass = eclass
+
+ def translate(self):
+ """Call the translator to translate the document"""
+ self.visitor = DevBookTranslator(self.document, self.eclass)
+ self.document.walkabout(self.visitor)
+ self.output = self.visitor.astext()
+
+
+class DevBookTranslator(nodes.NodeVisitor):
+ """A docutils translator for DevBook."""
+
+ sections_tags = ("section", "subsection", "subsubsection")
+
+ def __init__(self, document: nodes.document, eclass: str):
+ super().__init__(document)
+ self.eclass = eclass
+
+ self.estack = []
+ self.tb = etree.TreeBuilder()
+ self.section_depth = 0
+
+ def astext(self) -> str:
+ doc = self.tb.close()
+ et = etree.ElementTree(doc)
+ return etree.tostring(
+ et, encoding="utf-8", xml_declaration=True, pretty_print=True
+ ).decode()
+
+ def _push_element(self, name: str, **kwargs):
+ e = self.tb.start(name, kwargs)
+ self.estack.append(e)
+ return e
+
+ def _pop_element(self):
+ e = self.estack.pop()
+ return self.tb.end(e.tag)
+
+ def visit_document(self, node):
+ self.tb.start("guide", {"self": f"eclass-reference/{self.eclass}/"})
+ self.tb.start("chapter", {})
+
+ def depart_document(self, node):
+ self.tb.end("chapter")
+ self.tb.end("guide")
+
+ def visit_Text(self, node):
+ self.tb.data(str(node).replace("\x00", ""))
+
+ def depart_Text(self, node):
+ pass
+
+ def visit_paragraph(self, node):
+ self._push_element("p")
+
+ def depart_paragraph(self, node):
+ self._pop_element()
+
+ def visit_attribution(self, node):
+ self._push_element("p")
+
+ def depart_attribution(self, node):
+ self._pop_element()
+
+ def visit_literal_block(self, node):
+ self._push_element("codesample", lang="ebuild")
+
+ def depart_literal_block(self, node):
+ self._pop_element()
+
+ def visit_literal(self, node):
+ self._push_element("c")
+
+ def depart_literal(self, node):
+ self._pop_element()
+
+ def visit_emphasis(self, node):
+ self._push_element("e")
+
+ def depart_emphasis(self, node):
+ self._pop_element()
+
+ def visit_strong(self, node):
+ self._push_element("b")
+
+ def depart_strong(self, node):
+ self._pop_element()
+
+ def visit_block_quote(self, node):
+ self._push_element("pre")
+
+ def depart_block_quote(self, node):
+ self._pop_element()
+
+ def visit_title(self, node):
+ self._push_element("title")
+
+ def depart_title(self, node):
+ self._pop_element()
+ if self.section_depth > 0:
+ self._push_element("body")
+
+ def visit_section(self, node):
+ if self.estack and self.estack[-1].tag == "body":
+ self._pop_element()
+ self._push_element(self.sections_tags[self.section_depth])
+ self.section_depth += 1
+
+ def depart_section(self, node):
+ self.section_depth -= 1
+ if self.estack[-1].tag == "body":
+ self._pop_element()
+ self._pop_element()
+
+ def visit_title_reference(self, node):
+ pass
+
+ def depart_title_reference(self, node):
+ pass
+
+ def visit_reference(self, node):
+ internal_ref = False
+
+ # internal ref style #1: it declares itself internal
+ if node.hasattr("internal"):
+ internal_ref = node["internal"]
+
+ # internal ref style #2: it hides as an external ref, with strange
+ # qualities.
+ if (
+ node.hasattr("anonymous")
+ and (node["anonymous"] == 1)
+ and node.hasattr("refuri")
+ and (node["refuri"][0] == "_")
+ ):
+ internal_ref = True
+ node["refuri"] = node["refuri"][1:]
+
+ assert not internal_ref
+
+ if node.hasattr("refid"):
+ assert False
+ self._push_element("link", {"linkend": node["refid"]})
+ elif node.hasattr("refuri"):
+ if internal_ref:
+ pass
+ # ref_name = os.path.splitext(node['refuri'])[0]
+ # self._push_element('link', {'linkend': ref_name})
+ else:
+ self._push_element("uri", link=node["refuri"])
+ else:
+ assert False
+
+ def depart_reference(self, node):
+ if node.hasattr("refid") or node.hasattr("refuri"):
+ self._pop_element()
+
+ def visit_bullet_list(self, node):
+ self._push_element("ul")
+
+ def depart_bullet_list(self, node):
+ self._pop_element()
+
+ def visit_enumerated_list(self, node):
+ self._push_element("ol")
+
+ def depart_enumerated_list(self, node):
+ self._pop_element()
+
+ def visit_list_item(self, node):
+ self._push_element("li")
+
+ def depart_list_item(self, node):
+ self._pop_element()
+
+ def visit_line_block(self, node):
+ pass
+
+ def depart_line_block(self, node):
+ pass
+
+ def visit_line(self, node):
+ self._push_element("p")
+
+ def depart_line(self, node):
+ self._pop_element()
+
+ #
+ # Definitions list block
+ #
+
+ def visit_definition_list(self, node):
+ self._push_element("dl")
+
+ def depart_definition_list(self, node):
+ self._pop_element()
+
+ def visit_definition_list_item(self, node):
+ pass
+
+ def depart_definition_list_item(self, node):
+ pass
+
+ def visit_term(self, node):
+ self._push_element("dt")
+
+ def depart_term(self, node):
+ self._pop_element()
+
+ def visit_definition(self, node):
+ self._push_element("dd")
+
+ def depart_definition(self, node):
+ self._pop_element()
+
+ ### Debugging blocks
+
+ def visit_problematic(self, node):
+ self._push_element("warning")
+
+ def depart_problematic(self, node):
+ self._pop_element()
+
+ def visit_system_message(self, node):
+ self._push_element("warning")
+
+ def depart_system_message(self, node):
+ self._pop_element()