Source code for sphinxcontrib.bibtex.foot_roles
"""
.. autoclass:: FootReferenceInfo
:members:
.. autoclass:: FootReferenceText
:members:
.. autoclass:: FootCiteRole
:show-inheritance:
.. automethod:: result_nodes
"""
from typing import TYPE_CHECKING, cast, Tuple, List, NamedTuple
import docutils.nodes
import pybtex_docutils
from docutils.nodes import make_id
from pybtex.plugin import find_plugin
from sphinx.roles import XRefRole
from sphinx.util.logging import getLogger
from .richtext import BaseReferenceText
from .style.referencing import format_references
from .transforms import node_text_transform
if TYPE_CHECKING:
from sphinx.environment import BuildEnvironment
from .domain import BibtexDomain
from .foot_domain import BibtexFootDomain
from pybtex.backends import BaseBackend
logger = getLogger(__name__)
[docs]class FootReferenceInfo(NamedTuple):
"""Tuple containing reference info to enable sphinx to resolve a footnote
reference.
"""
key: str #: Citation key.
document: "docutils.nodes.document" #: Current docutils document.
refname: str #: Citation reference name.
[docs]class FootReferenceText(BaseReferenceText[FootReferenceInfo]):
"""Pybtex rich text class for footnote references with the docutils
backend, for use with :class:`FootReferenceInfo`.
"""
[docs] def render(self, backend: "BaseBackend"):
assert isinstance(backend, pybtex_docutils.Backend), \
"FootReferenceText only supports the docutils backend"
info = self.info[0]
# see docutils.parsers.rst.states.Body.footnote_reference()
refnode = docutils.nodes.footnote_reference(
'[#%s]_' % info.key, refname=info.refname, auto=1)
info.document.note_autofootnote_ref(refnode)
info.document.note_footnote_ref(refnode)
return [refnode]
[docs]class FootCiteRole(XRefRole):
"""Class for processing the :rst:role:`footcite` role."""
[docs] def result_nodes(self, document: "docutils.nodes.document",
env: "BuildEnvironment", node: "docutils.nodes.Element",
is_ref: bool
) -> Tuple[List["docutils.nodes.Node"],
List["docutils.nodes.system_message"]]:
"""Transform node into footnote references, and
add footnotes to a node stored in the environment's temporary data
if they are not yet present.
.. seealso::
The node containing all footnotes is inserted into the document by
:meth:`.foot_directives.FootBibliographyDirective.run`.
"""
if not node.get('refdomain'):
assert node['reftype'] == 'footcite'
node['refdomain'] = 'footcite'
node['reftype'] = 'p'
foot_domain = cast("BibtexFootDomain", self.env.get_domain('footcite'))
keys = [key.strip() for key in self.target.split(',')] # type: ignore
try:
foot_bibliography = env.temp_data["bibtex_foot_bibliography"]
except KeyError:
env.temp_data["bibtex_foot_bibliography"] = foot_bibliography = \
foot_domain.bibliography_header.deepcopy()
foot_old_refs = env.temp_data.setdefault("bibtex_foot_old_refs", set())
foot_new_refs = env.temp_data.setdefault("bibtex_foot_new_refs", set())
style = find_plugin(
'pybtex.style.formatting',
self.config.bibtex_default_style)()
references = []
domain = cast("BibtexDomain", self.env.get_domain('cite'))
# count only incremented at directive, see foot_directives run method
footbibliography_count = env.temp_data.setdefault(
"bibtex_footbibliography_count", 0)
footcite_names = env.temp_data.setdefault(
"bibtex_footcite_names", {})
for key in keys:
entry = domain.bibdata.data.entries.get(key)
if entry is not None:
formatted_entry = style.format_entry(label='', entry=entry)
if key not in (foot_old_refs | foot_new_refs):
footnote = docutils.nodes.footnote(auto=1)
# no automatic ids for footnotes: force non-empty template
template: str = \
env.app.config.bibtex_footcite_id \
if env.app.config.bibtex_footcite_id \
else "footcite-{key}"
raw_id = template.format(
footbibliography_count=footbibliography_count + 1,
key=entry.key)
# format name with make_id for consistency with cite role
name = make_id(raw_id)
footnote['names'] += [name]
footcite_names[entry.key] = name
footnote += domain.backend.paragraph(formatted_entry)
document.note_autofootnote(footnote)
document.note_explicit_target(footnote, footnote)
node_text_transform(footnote)
foot_bibliography += footnote
foot_new_refs.add(key)
references.append(
(entry, formatted_entry,
FootReferenceInfo(
key=entry.key, refname=footcite_names[entry.key],
document=document)))
else:
logger.warning('could not find bibtex key "%s"' % key,
location=(env.docname, self.lineno),
type="bibtex", subtype="key_not_found")
ref_nodes = format_references(
foot_domain.reference_style, FootReferenceText, node['reftype'],
references).render(domain.backend)
return ref_nodes, []