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.
281 lines
8.7 KiB
281 lines
8.7 KiB
"""
|
|
.. autofunction:: sphinxcontrib.bibtex.style.template.join(\
|
|
sep='', sep2=None, last_sep=None, other=None)
|
|
|
|
.. autofunction:: sphinxcontrib.bibtex.style.template.sentence(\
|
|
capfirst=False, capitalize=False, add_period=True, \
|
|
sep=', ', sep2=None, last_sep=None, other=None)
|
|
|
|
.. autofunction:: sphinxcontrib.bibtex.style.template.names(\
|
|
role, sep='', sep2=None, last_sep=None, other=None)
|
|
|
|
.. autofunction:: sphinxcontrib.bibtex.style.template.entry_label()
|
|
|
|
.. autofunction:: sphinxcontrib.bibtex.style.template.reference()
|
|
|
|
.. autofunction:: sphinxcontrib.bibtex.style.template.footnote_reference()
|
|
"""
|
|
|
|
from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, cast
|
|
|
|
import docutils.nodes
|
|
import pybtex_docutils
|
|
from pybtex.richtext import Text
|
|
from pybtex.style.template import (
|
|
FieldIsMissing,
|
|
Node,
|
|
_format_list,
|
|
field,
|
|
first_of,
|
|
optional,
|
|
tag,
|
|
)
|
|
from sphinx.util.nodes import make_refnode
|
|
|
|
from sphinxcontrib.bibtex.nodes import raw_latex
|
|
from sphinxcontrib.bibtex.richtext import BaseReferenceText
|
|
|
|
if TYPE_CHECKING:
|
|
from pybtex.backends import BaseBackend
|
|
from pybtex.richtext import BaseText
|
|
from pybtex.style import FormattedEntry
|
|
from sphinx.builders import Builder
|
|
|
|
|
|
# extended from pybtex: also copies the docstring into the wrapped object
|
|
def node(f):
|
|
n = Node(f.__name__, f)
|
|
n.__doc__ = f.__doc__
|
|
return n
|
|
|
|
|
|
# copied from pybtex join but extended to allow "et al" formatting
|
|
@node
|
|
def join(children, data, sep="", sep2=None, last_sep=None, other=None):
|
|
"""Join text fragments together."""
|
|
|
|
if sep2 is None:
|
|
sep2 = sep
|
|
if last_sep is None:
|
|
last_sep = sep
|
|
parts = [part for part in _format_list(children, data) if part]
|
|
if len(parts) <= 1:
|
|
return Text(*parts)
|
|
elif len(parts) == 2:
|
|
return Text(sep2).join(parts)
|
|
elif other is None:
|
|
return Text(last_sep).join([Text(sep).join(parts[:-1]), parts[-1]])
|
|
else:
|
|
return Text(parts[0], other)
|
|
|
|
|
|
@node
|
|
def join2(children, data, sep1="", sep2=""):
|
|
"""Join text fragments together."""
|
|
if not children:
|
|
return Text()
|
|
else:
|
|
return join(sep=sep1)[children[0], join(sep=sep2)[children[1:]]].format_data(
|
|
data
|
|
)
|
|
|
|
|
|
# copied from pybtex names but using the new join
|
|
@node
|
|
def sentence(
|
|
children,
|
|
data,
|
|
capfirst=False,
|
|
capitalize=False,
|
|
add_period=True,
|
|
sep=", ",
|
|
sep2=None,
|
|
last_sep=None,
|
|
other=None,
|
|
):
|
|
"""Join text fragments, capitalize the first letter,
|
|
and add a period to the end.
|
|
"""
|
|
text = join(sep=sep, sep2=sep2, last_sep=last_sep, other=other)[
|
|
children
|
|
].format_data(data)
|
|
if capfirst:
|
|
text = text.capfirst()
|
|
if capitalize:
|
|
text = text.capitalize()
|
|
if add_period:
|
|
text = text.add_period()
|
|
return text
|
|
|
|
|
|
# copied from pybtex names but using the new join allowing "et al" formatting
|
|
@node
|
|
def names(children, data, role, **kwargs):
|
|
"""Return formatted names."""
|
|
assert not children
|
|
try:
|
|
persons = data["entry"].persons[role]
|
|
except KeyError:
|
|
raise FieldIsMissing(role, data["entry"])
|
|
style = data["style"]
|
|
formatted_names = [
|
|
style.person.style_plugin.format(person, style.person.abbreviate)
|
|
for person in persons
|
|
]
|
|
return join(**kwargs)[formatted_names].format_data(data)
|
|
|
|
|
|
@node
|
|
def entry_label(children, data) -> "BaseText":
|
|
"""Node for inserting the label of a formatted entry."""
|
|
assert not children
|
|
entry = cast("FormattedEntry", data["formatted_entry"])
|
|
return Text(entry.label)
|
|
|
|
|
|
class SphinxReferenceInfo(NamedTuple):
|
|
"""Tuple containing reference info to enable sphinx to resolve a reference
|
|
to a citation.
|
|
"""
|
|
|
|
builder: "Builder" #: The Sphinx builder.
|
|
fromdocname: str #: Document name of the citation reference.
|
|
todocname: str #: Document name of the bibliography.
|
|
citation_id: str #: Unique id of the citation within the bibliography.
|
|
title: str #: Title attribute for reference node.
|
|
pre_text: str #: Text to come before citation.
|
|
post_text: str #: Text to come after citation.
|
|
|
|
|
|
class SphinxReferenceText(BaseReferenceText[SphinxReferenceInfo]):
|
|
"""Pybtex rich text class generating
|
|
a docutils reference node to a citation
|
|
for use with :class:`SphinxReferenceInfo`.
|
|
"""
|
|
|
|
def render(self, backend: "BaseBackend") -> List[docutils.nodes.Element]:
|
|
assert isinstance(
|
|
backend, pybtex_docutils.Backend
|
|
), "SphinxReferenceText only supports the docutils backend"
|
|
info = self.info[0]
|
|
if info.builder.name == "latex":
|
|
key = f"cite.{info.todocname}:{info.citation_id}"
|
|
return (
|
|
[raw_latex(f"\\hyperlink{{{key}}}{{")]
|
|
+ super().render(backend)
|
|
+ [raw_latex("}")]
|
|
)
|
|
elif info.builder.name == "rinoh":
|
|
children = super().render(backend)
|
|
refid = f"%{info.todocname}#{info.citation_id}"
|
|
refnode = docutils.nodes.citation_reference(
|
|
text=children[0], refid=refid, reftitle=info.title
|
|
)
|
|
refnode.extend(children[1:])
|
|
return [refnode]
|
|
else:
|
|
children = super().render(backend)
|
|
# make_refnode only takes a single child
|
|
refnode2 = make_refnode(
|
|
builder=info.builder,
|
|
fromdocname=info.fromdocname,
|
|
todocname=info.todocname,
|
|
targetid=info.citation_id,
|
|
child=children[0],
|
|
title=info.title,
|
|
)
|
|
refnode2.extend(children[1:]) # type: ignore
|
|
return [refnode2]
|
|
|
|
|
|
@node
|
|
def reference(children, data: Dict[str, Any]):
|
|
"""Pybtex node for inserting a docutils reference node to a citation.
|
|
The children of the node
|
|
comprise the content of the reference, and any referencing information
|
|
is stored in the *reference_info* key of the *data*.
|
|
The data must also contain a *style* key pointing to the corresponding
|
|
:class:`~sphinxcontrib.bibtex.style.referencing.BaseReferenceStyle`.
|
|
"""
|
|
parts = _format_list(children, data)
|
|
info = data["reference_info"]
|
|
assert isinstance(info, SphinxReferenceInfo)
|
|
return SphinxReferenceText(info, *parts)
|
|
|
|
|
|
@node
|
|
def pre_text(children, data: Dict[str, Any]):
|
|
assert not children
|
|
info = data["reference_info"]
|
|
assert isinstance(info, SphinxReferenceInfo)
|
|
return Text(info.pre_text)
|
|
|
|
|
|
@node
|
|
def post_text(children, data: Dict[str, Any]):
|
|
assert not children
|
|
info = data["reference_info"]
|
|
assert isinstance(info, SphinxReferenceInfo)
|
|
return Text(info.post_text)
|
|
|
|
|
|
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.
|
|
|
|
|
|
class FootReferenceText(BaseReferenceText[FootReferenceInfo]):
|
|
"""Pybtex rich text class generating
|
|
a docutils footnote_reference node to a citation
|
|
for use with :class:`FootReferenceInfo`.
|
|
"""
|
|
|
|
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]
|
|
|
|
|
|
@node
|
|
def footnote_reference(children, data: Dict[str, Any]):
|
|
"""Pybtex node for inserting a footnote_reference docutils node.
|
|
Any referencing information
|
|
is stored in the *reference_info* key of the *data*.
|
|
The data must also contain a *style* key pointing to the corresponding
|
|
:class:`~sphinxcontrib.bibtex.style.referencing.BaseReferenceStyle`.
|
|
"""
|
|
assert not children
|
|
info = data["reference_info"]
|
|
assert isinstance(info, FootReferenceInfo)
|
|
# we need to give the footnote text some fake content
|
|
# otherwise pybtex richtext engine will mess things up
|
|
return FootReferenceText(info, "#")
|
|
|
|
|
|
@node
|
|
def year(children, data: Dict[str, Any]) -> "BaseText":
|
|
assert not children
|
|
return first_of[optional[field("year")], "n.d."].format_data(data)
|
|
|
|
|
|
@node
|
|
def author_or_editor_or_title(children, data, **kwargs):
|
|
assert not children
|
|
return first_of[
|
|
optional[names("author", **kwargs)],
|
|
optional[names("editor", **kwargs)],
|
|
tag("em")[field("title")],
|
|
].format_data(data)
|
|
|