"""Docutils transforms used by Sphinx when reading documents."""
from __future__ import annotations
from typing import TYPE_CHECKING, cast
from docutils import nodes
from sphinx import addnodes
from sphinx.transforms import SphinxTransform
if TYPE_CHECKING:
from typing import Any
from docutils.nodes import Node
from sphinx.application import Sphinx
from sphinx.util.typing import ExtensionMetadata
class RefOnlyListChecker(nodes.GenericNodeVisitor):
"""Raise `nodes.NodeFound` if non-simple list item is encountered.
Here 'simple' means a list item containing only a paragraph with a
single reference in it.
"""
def default_visit(self, node: Node) -> None:
raise nodes.NodeFound
def visit_bullet_list(self, node: nodes.bullet_list) -> None:
pass
def visit_list_item(self, node: nodes.list_item) -> None:
children: list[Node] = [
child for child in node.children if not isinstance(child, nodes.Invisible)
]
if len(children) != 1:
raise nodes.NodeFound
if not isinstance(children[0], nodes.paragraph):
raise nodes.NodeFound
para = children[0]
if len(para) != 1:
raise nodes.NodeFound
if not isinstance(para[0], addnodes.pending_xref):
raise nodes.NodeFound
raise nodes.SkipChildren
def invisible_visit(self, node: Node) -> None:
"""Invisible nodes should be ignored."""
pass
class RefOnlyBulletListTransform(SphinxTransform):
"""Change refonly bullet lists to use compact_paragraphs.
Specifically implemented for 'Indices and Tables' section, which looks
odd when html_compact_lists is false.
"""
default_priority = 100
def apply(self, **kwargs: Any) -> None:
if self.config.html_compact_lists:
return
def check_refonly_list(node: Node) -> bool:
"""Check for list with only references in it."""
visitor = RefOnlyListChecker(self.document)
try:
node.walk(visitor)
except nodes.NodeFound:
return False
else:
return True
for node in self.document.findall(nodes.bullet_list):
if check_refonly_list(node):
for item in node.findall(nodes.list_item):
para = cast('nodes.paragraph', item[0])
ref = cast('nodes.reference', para[0])
compact_para = addnodes.compact_paragraph()
compact_para += ref
item.replace(para, compact_para)
def setup(app: Sphinx) -> ExtensionMetadata:
app.add_transform(RefOnlyBulletListTransform)
return {
'version': 'builtin',
'parallel_read_safe': True,
'parallel_write_safe': True,
}