summaryrefslogtreecommitdiff
path: root/sphinx/util/docfields.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/util/docfields.py')
-rw-r--r--sphinx/util/docfields.py74
1 files changed, 51 insertions, 23 deletions
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 6d48e910c..49ee26789 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -8,19 +8,23 @@
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Type, Union, cast
from docutils import nodes
from docutils.nodes import Node
+from docutils.parsers.rst.states import Inliner
from sphinx import addnodes
from sphinx.environment import BuildEnvironment
+from sphinx.locale import __
+from sphinx.util import logging
from sphinx.util.typing import TextlikeNode
if TYPE_CHECKING:
from sphinx.directive import ObjectDescription
+logger = logging.getLogger(__name__)
+
def _is_single_paragraph(node: nodes.field_body) -> bool:
"""True if the node only contains one paragraph (and system messages)."""
@@ -62,39 +66,58 @@ class Field:
def make_xref(self, rolename: str, domain: str, target: str,
innernode: Type[TextlikeNode] = addnodes.literal_emphasis,
- contnode: Node = None, env: BuildEnvironment = None) -> Node:
+ contnode: Node = None, env: BuildEnvironment = None,
+ inliner: Inliner = None, location: Node = None) -> Node:
+ # note: for backwards compatibility env is last, but not optional
+ assert env is not None
+ assert (inliner is None) == (location is None), (inliner, location)
if not rolename:
return contnode or innernode(target, target)
- refnode = addnodes.pending_xref('', refdomain=domain, refexplicit=False,
- reftype=rolename, reftarget=target)
- refnode += contnode or innernode(target, target)
- if env:
+ # The domain is passed from DocFieldTransformer. So it surely exists.
+ # So we don't need to take care the env.get_domain() raises an exception.
+ role = env.get_domain(domain).role(rolename)
+ if role is None or inliner is None:
+ if role is None and inliner is not None:
+ msg = "Problem in %s domain: field is supposed "
+ msg += "to use role '%s', but that role is not in the domain."
+ logger.warning(__(msg), domain, rolename, location=location)
+ refnode = addnodes.pending_xref('', refdomain=domain, refexplicit=False,
+ reftype=rolename, reftarget=target)
+ refnode += contnode or innernode(target, target)
env.get_domain(domain).process_field_xref(refnode)
- return refnode
+ return refnode
+ lineno = logging.get_source_line(location)[1]
+ ns, messages = role(rolename, target, target, lineno, inliner, {}, [])
+ return nodes.inline(target, '', *ns)
def make_xrefs(self, rolename: str, domain: str, target: str,
innernode: Type[TextlikeNode] = addnodes.literal_emphasis,
- contnode: Node = None, env: BuildEnvironment = None) -> List[Node]:
- return [self.make_xref(rolename, domain, target, innernode, contnode, env)]
+ contnode: Node = None, env: BuildEnvironment = None,
+ inliner: Inliner = None, location: Node = None) -> List[Node]:
+ return [self.make_xref(rolename, domain, target, innernode, contnode,
+ env, inliner, location)]
def make_entry(self, fieldarg: str, content: List[Node]) -> Tuple[str, List[Node]]:
return (fieldarg, content)
def make_field(self, types: Dict[str, List[Node]], domain: str,
- item: Tuple, env: BuildEnvironment = None) -> nodes.field:
+ item: Tuple, env: BuildEnvironment = None,
+ inliner: Inliner = None, location: Node = None) -> nodes.field:
fieldarg, content = item
fieldname = nodes.field_name('', self.label)
if fieldarg:
fieldname += nodes.Text(' ')
fieldname.extend(self.make_xrefs(self.rolename, domain,
- fieldarg, nodes.Text, env=env))
+ fieldarg, nodes.Text,
+ env=env, inliner=inliner, location=location))
if len(content) == 1 and (
isinstance(content[0], nodes.Text) or
(isinstance(content[0], nodes.inline) and len(content[0]) == 1 and
isinstance(content[0][0], nodes.Text))):
content = self.make_xrefs(self.bodyrolename, domain,
- content[0].astext(), contnode=content[0], env=env)
+ content[0].astext(), contnode=content[0],
+ env=env, inliner=inliner, location=location)
fieldbody = nodes.field_body('', nodes.paragraph('', '', *content))
return nodes.field('', fieldname, fieldbody)
@@ -121,13 +144,15 @@ class GroupedField(Field):
self.can_collapse = can_collapse
def make_field(self, types: Dict[str, List[Node]], domain: str,
- items: Tuple, env: BuildEnvironment = None) -> nodes.field:
+ items: Tuple, env: BuildEnvironment = None,
+ inliner: Inliner = None, location: Node = None) -> nodes.field:
fieldname = nodes.field_name('', self.label)
listnode = self.list_type()
for fieldarg, content in items:
par = nodes.paragraph()
par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
- addnodes.literal_strong, env=env))
+ addnodes.literal_strong,
+ env=env, inliner=inliner, location=location))
par += nodes.Text(' -- ')
par += content
listnode += nodes.list_item('', par)
@@ -170,7 +195,8 @@ class TypedField(GroupedField):
self.typerolename = typerolename
def make_field(self, types: Dict[str, List[Node]], domain: str,
- items: Tuple, env: BuildEnvironment = None) -> nodes.field:
+ items: Tuple, env: BuildEnvironment = None,
+ inliner: Inliner = None, location: Node = None) -> nodes.field:
def handle_item(fieldarg: str, content: str) -> nodes.paragraph:
par = nodes.paragraph()
par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
@@ -184,7 +210,8 @@ class TypedField(GroupedField):
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
typename = fieldtype[0].astext()
par.extend(self.make_xrefs(self.typerolename, domain, typename,
- addnodes.literal_emphasis, env=env))
+ addnodes.literal_emphasis, env=env,
+ inliner=inliner, location=location))
else:
par += fieldtype
par += nodes.Text(')')
@@ -227,7 +254,7 @@ class DocFieldTransformer:
"""Transform a single field list *node*."""
typemap = self.typemap
- entries: List[Union[nodes.field, Tuple[Field, Any]]] = []
+ entries: List[Union[nodes.field, Tuple[Field, Any, Node]]] = []
groupindices: Dict[str, int] = {}
types: Dict[str, Dict] = {}
@@ -317,16 +344,16 @@ class DocFieldTransformer:
# get one entry per field
if typedesc.is_grouped:
if typename in groupindices:
- group = cast(Tuple[Field, List], entries[groupindices[typename]])
+ group = cast(Tuple[Field, List, Node], entries[groupindices[typename]])
else:
groupindices[typename] = len(entries)
- group = (typedesc, [])
+ group = (typedesc, [], field)
entries.append(group)
new_entry = typedesc.make_entry(fieldarg, [translatable_content])
group[1].append(new_entry)
else:
new_entry = typedesc.make_entry(fieldarg, [translatable_content])
- entries.append((typedesc, new_entry))
+ entries.append((typedesc, new_entry, field))
# step 2: all entries are collected, construct the new field list
new_list = nodes.field_list()
@@ -335,10 +362,11 @@ class DocFieldTransformer:
# pass-through old field
new_list += entry
else:
- fieldtype, items = entry
+ fieldtype, items, location = entry
fieldtypes = types.get(fieldtype.name, {})
env = self.directive.state.document.settings.env
- new_list += fieldtype.make_field(fieldtypes, self.directive.domain,
- items, env=env)
+ inliner = self.directive.state.inliner
+ new_list += fieldtype.make_field(fieldtypes, self.directive.domain, items,
+ env=env, inliner=inliner, location=location)
node.replace_self(new_list)