"""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
[docs]
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
[docs]
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()
[docs]
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
[docs]
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)
[docs]
def visit_document(self, node):
self.tb.start("guide", {"self": f"eclass-reference/{self.eclass}/"})
self.tb.start("chapter", {})
[docs]
def depart_document(self, node):
self.tb.end("chapter")
self.tb.end("guide")
[docs]
def visit_Text(self, node):
self.tb.data(str(node).replace("\x00", ""))
[docs]
def depart_Text(self, node):
pass
[docs]
def visit_paragraph(self, node):
self._push_element("p")
[docs]
def depart_paragraph(self, node):
self._pop_element()
[docs]
def visit_attribution(self, node):
self._push_element("p")
[docs]
def depart_attribution(self, node):
self._pop_element()
[docs]
def visit_literal_block(self, node):
self._push_element("codesample", lang="ebuild")
[docs]
def depart_literal_block(self, node):
self._pop_element()
[docs]
def visit_literal(self, node):
self._push_element("c")
[docs]
def depart_literal(self, node):
self._pop_element()
[docs]
def visit_emphasis(self, node):
self._push_element("e")
[docs]
def depart_emphasis(self, node):
self._pop_element()
[docs]
def visit_strong(self, node):
self._push_element("b")
[docs]
def depart_strong(self, node):
self._pop_element()
[docs]
def visit_block_quote(self, node):
self._push_element("pre")
[docs]
def depart_block_quote(self, node):
self._pop_element()
[docs]
def visit_title(self, node):
self._push_element("title")
[docs]
def depart_title(self, node):
self._pop_element()
if self.section_depth > 0:
self._push_element("body")
[docs]
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
[docs]
def depart_section(self, node):
self.section_depth -= 1
if self.estack[-1].tag == "body":
self._pop_element()
self._pop_element()
[docs]
def visit_title_reference(self, node):
pass
[docs]
def depart_title_reference(self, node):
pass
[docs]
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
[docs]
def depart_reference(self, node):
if node.hasattr("refid") or node.hasattr("refuri"):
self._pop_element()
[docs]
def visit_bullet_list(self, node):
self._push_element("ul")
[docs]
def depart_bullet_list(self, node):
self._pop_element()
[docs]
def visit_enumerated_list(self, node):
self._push_element("ol")
[docs]
def depart_enumerated_list(self, node):
self._pop_element()
[docs]
def visit_list_item(self, node):
self._push_element("li")
[docs]
def depart_list_item(self, node):
self._pop_element()
[docs]
def visit_line_block(self, node):
pass
[docs]
def depart_line_block(self, node):
pass
[docs]
def visit_line(self, node):
self._push_element("p")
[docs]
def depart_line(self, node):
self._pop_element()
#
# Definitions list block
#
[docs]
def visit_definition_list(self, node):
self._push_element("dl")
[docs]
def depart_definition_list(self, node):
self._pop_element()
[docs]
def visit_definition_list_item(self, node):
pass
[docs]
def depart_definition_list_item(self, node):
pass
[docs]
def visit_term(self, node):
self._push_element("dt")
[docs]
def depart_term(self, node):
self._pop_element()
[docs]
def visit_definition(self, node):
self._push_element("dd")
[docs]
def depart_definition(self, node):
self._pop_element()
### Debugging blocks
[docs]
def visit_problematic(self, node):
self._push_element("warning")
[docs]
def depart_problematic(self, node):
self._pop_element()
[docs]
def visit_system_message(self, node):
self._push_element("warning")
[docs]
def depart_system_message(self, node):
self._pop_element()