This repository provides User Manual for setting up a Docker environment tailored for testing DGTD code.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
4.9 KiB

8 months ago
"""
.. autoclass:: BibliographyTransform
:show-inheritance:
.. autoattribute:: default_priority
.. automethod:: run
"""
from itertools import zip_longest
from typing import TYPE_CHECKING, cast
import docutils.nodes
from pybtex.plugin import find_plugin
from sphinx.transforms.post_transforms import SphinxPostTransform
from sphinx.util.logging import getLogger
from .directives import BibliographyKey
from .nodes import bibliography as bibliography_node
if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment
from .domain import BibtexDomain
logger = getLogger(__name__)
def node_text_transform(node: docutils.nodes.Element) -> None:
"""Apply extra text transformations to a node."""
for child, next_child in zip_longest(node.children[:], node.children[1:]):
if isinstance(child, docutils.nodes.Text):
if child.endswith(r"\url ") and isinstance(next_child, docutils.nodes.Text):
node.replace(child, docutils.nodes.Text(child[:-5]))
ref_node = docutils.nodes.reference(refuri=next_child.astext())
ref_node += next_child
node.replace(next_child, ref_node)
elif isinstance(child, docutils.nodes.Element): # pragma: no branch
node_text_transform(child)
class BibliographyTransform(SphinxPostTransform):
"""A docutils transform to generate citation entries for
bibliography nodes.
"""
# transform must be applied before sphinx runs its ReferencesResolver
# which has priority 10, so when ReferencesResolver calls the cite domain
# resolve_xref, the target is present and all will work fine
default_priority = 5
backend = find_plugin("pybtex.backends", "docutils")()
def run(self, **kwargs):
"""Transform each
:class:`~sphinxcontrib.bibtex.nodes.bibliography` node into a
list of citations.
"""
env = cast("BuildEnvironment", self.document.settings.env)
domain = cast("BibtexDomain", env.get_domain("cite"))
# Can just use "findall" once docutils 0.18+ is required
meth = "findall" if hasattr(self.document, "findall") else "traverse"
for bibnode in getattr(self.document, meth)(bibliography_node):
# reminder: env.docname may be equal to 'index' instead of
# bibnode['docname'] in post-transform phase (e.g. latex builder)
bib_key = BibliographyKey(docname=bibnode["docname"], id_=bibnode["ids"][0])
bibliography = domain.bibliographies[bib_key]
citations = [
citation
for citation in domain.citations
if citation.bibliography_key == bib_key
]
# create citation nodes for all references
if bibliography.list_ == "enumerated":
nodes = docutils.nodes.enumerated_list()
nodes["enumtype"] = bibliography.enumtype
if bibliography.start >= 1:
nodes["start"] = bibliography.start
env.temp_data["bibtex_enum_count"] = bibliography.start
else:
nodes["start"] = env.temp_data.setdefault("bibtex_enum_count", 1)
elif bibliography.list_ == "bullet":
nodes = docutils.nodes.bullet_list()
else: # "citation"
nodes = []
for citation in citations:
citation_node = bibliography.citation_nodes[citation.key]
if bibliography.list_ in {"enumerated", "bullet"}:
citation_node += self.backend.paragraph(citation.formatted_entry)
else: # "citation"
# backrefs only supported in same document
backrefs = [
citation_ref.citation_ref_id
for citation_ref in domain.citation_refs
if bib_key.docname == citation_ref.docname
and citation.key
in {target.key for target in citation_ref.targets}
]
if backrefs:
citation_node["backrefs"] = backrefs
citation_node += docutils.nodes.label(
"", citation.formatted_entry.label, support_smartquotes=False
)
citation_node += self.backend.paragraph(citation.formatted_entry)
citation_node["docname"] = bib_key.docname
node_text_transform(citation_node)
nodes.append(citation_node)
if bibliography.list_ == "enumerated":
env.temp_data["bibtex_enum_count"] += 1
if citations:
final_node = domain.bibliography_header.deepcopy()
final_node += nodes
bibnode.replace_self(final_node)
else:
bibnode.replace_self(docutils.nodes.target())