Source code for sphinxcontrib.bibtex.transforms
"""
.. autoclass:: BibliographyTransform
:show-inheritance:
.. autoattribute:: default_priority
.. automethod:: run
.. autofunction:: node_text_transform
.. autofunction:: transform_url_command
"""
import docutils.nodes
from typing import TYPE_CHECKING, cast
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__)
[docs]def node_text_transform(node, transform):
"""Apply transformation to all Text nodes within node."""
for child in node.children:
if isinstance(child, docutils.nodes.Text):
node.replace(child, transform(child))
else:
node_text_transform(child, transform)
[docs]def transform_url_command(textnode):
"""Convert '\\\\url{...}' into a proper docutils hyperlink."""
text = textnode.astext()
if '\\url' in text:
text1, _, text = text.partition('\\url')
text2, _, text3 = text.partition('}')
text2 = text2.lstrip(' {')
ref = docutils.nodes.reference(refuri=text2)
ref += docutils.nodes.Text(text2)
node = docutils.nodes.inline()
node += transform_url_command(docutils.nodes.Text(text1))
node += ref
node += transform_url_command(docutils.nodes.Text(text3))
return node
else:
return textnode
[docs]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')()
[docs] 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'))
for bibnode in self.document.traverse(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 citation_ref.keys]
if backrefs:
citation_node['backrefs'] = backrefs
citation_node += docutils.nodes.label(
'', citation.label, support_smartquotes=False)
citation_node += self.backend.paragraph(
citation.formatted_entry)
citation_node['docname'] = bib_key.docname
node_text_transform(citation_node, transform_url_command)
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())