Source code for sphinxcontrib.bibtex.foot_roles

"""
.. autoclass:: FootCiteRole
    :show-inheritance:

    .. automethod:: run
"""

from typing import TYPE_CHECKING, List, Optional, Tuple, cast

import docutils.nodes
from docutils.nodes import make_id
from pybtex.database import Entry
from pybtex.plugin import find_plugin
from pybtex.style import FormattedEntry
from sphinx.util.docutils import SphinxRole
from sphinx.util.logging import getLogger

from .style.referencing import format_references
from .style.template import FootReferenceInfo
from .transforms import node_text_transform

if TYPE_CHECKING:
    from .domain import BibtexDomain
    from .foot_domain import BibtexFootDomain

logger = getLogger(__name__)


[docs] class FootCiteRole(SphinxRole): """Class for processing the :rst:role:`footcite` role."""
[docs] def run( self, ) -> 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`. """ foot_domain = cast("BibtexFootDomain", self.env.get_domain("footcite")) keys = [key.strip() for key in self.text.split(",")] try: foot_bibliography = self.env.temp_data["bibtex_foot_bibliography"] except KeyError: self.env.temp_data["bibtex_foot_bibliography"] = foot_bibliography = ( foot_domain.bibliography_header.deepcopy() ) foot_old_refs: set[str] = self.env.temp_data.setdefault( # type: ignore "bibtex_foot_old_refs", set() ) foot_new_refs: set[str] = self.env.temp_data.setdefault( # type: ignore "bibtex_foot_new_refs", set() ) style = find_plugin( "pybtex.style.formatting", self.config.bibtex_default_style )() references: List[Tuple[Entry, FormattedEntry, FootReferenceInfo]] = [] domain = cast("BibtexDomain", self.env.get_domain("cite")) # count only incremented at directive, see foot_directives run method footbibliography_count: int = self.env.temp_data.setdefault( # type: ignore "bibtex_footbibliography_count", 0 ) footcite_names: dict[str, str] = self.env.temp_data.setdefault( # type: ignore "bibtex_footcite_names", {} ) for key in keys: entry: Optional[Entry] = domain.bibdata.data.entries.get(key) if entry is not None: formatted_entry: FormattedEntry = 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 = ( self.env.app.config.bibtex_footcite_id if self.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) self.inliner.document.note_autofootnote(footnote) self.inliner.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=self.inliner.document, ), ) ) else: logger.warning( 'could not find bibtex key "%s"' % key, location=(self.env.docname, self.lineno), type="bibtex", subtype="key_not_found", ) _, _, role_name = self.name.partition(":") ref_nodes = format_references( foot_domain.reference_style, role_name or "p", references ).render(domain.backend) return ref_nodes, []