127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
|
"""The index domain."""
|
||
|
|
||
|
from __future__ import annotations
|
||
|
|
||
|
from typing import TYPE_CHECKING, Any
|
||
|
|
||
|
from docutils import nodes
|
||
|
from docutils.parsers.rst import directives
|
||
|
|
||
|
from sphinx import addnodes
|
||
|
from sphinx.domains import Domain
|
||
|
from sphinx.util import logging
|
||
|
from sphinx.util.docutils import ReferenceRole, SphinxDirective
|
||
|
from sphinx.util.index_entries import split_index_msg
|
||
|
from sphinx.util.nodes import process_index_entry
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from collections.abc import Iterable
|
||
|
|
||
|
from docutils.nodes import Node, system_message
|
||
|
|
||
|
from sphinx.application import Sphinx
|
||
|
from sphinx.environment import BuildEnvironment
|
||
|
from sphinx.util.typing import OptionSpec
|
||
|
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class IndexDomain(Domain):
|
||
|
"""Mathematics domain."""
|
||
|
name = 'index'
|
||
|
label = 'index'
|
||
|
|
||
|
@property
|
||
|
def entries(self) -> dict[str, list[tuple[str, str, str, str, str | None]]]:
|
||
|
return self.data.setdefault('entries', {})
|
||
|
|
||
|
def clear_doc(self, docname: str) -> None:
|
||
|
self.entries.pop(docname, None)
|
||
|
|
||
|
def merge_domaindata(self, docnames: Iterable[str], otherdata: dict[str, Any]) -> None:
|
||
|
for docname in docnames:
|
||
|
self.entries[docname] = otherdata['entries'][docname]
|
||
|
|
||
|
def process_doc(self, env: BuildEnvironment, docname: str, document: Node) -> None:
|
||
|
"""Process a document after it is read by the environment."""
|
||
|
entries = self.entries.setdefault(env.docname, [])
|
||
|
for node in list(document.findall(addnodes.index)):
|
||
|
try:
|
||
|
for (entry_type, value, _target_id, _main, _category_key) in node['entries']:
|
||
|
split_index_msg(entry_type, value)
|
||
|
except ValueError as exc:
|
||
|
logger.warning(str(exc), location=node)
|
||
|
node.parent.remove(node)
|
||
|
else:
|
||
|
for entry in node['entries']:
|
||
|
entries.append(entry)
|
||
|
|
||
|
|
||
|
class IndexDirective(SphinxDirective):
|
||
|
"""
|
||
|
Directive to add entries to the index.
|
||
|
"""
|
||
|
has_content = False
|
||
|
required_arguments = 1
|
||
|
optional_arguments = 0
|
||
|
final_argument_whitespace = True
|
||
|
option_spec: OptionSpec = {
|
||
|
'name': directives.unchanged,
|
||
|
}
|
||
|
|
||
|
def run(self) -> list[Node]:
|
||
|
arguments = self.arguments[0].split('\n')
|
||
|
|
||
|
if 'name' in self.options:
|
||
|
targetname = self.options['name']
|
||
|
targetnode = nodes.target('', '', names=[targetname])
|
||
|
else:
|
||
|
targetid = 'index-%s' % self.env.new_serialno('index')
|
||
|
targetnode = nodes.target('', '', ids=[targetid])
|
||
|
|
||
|
self.state.document.note_explicit_target(targetnode)
|
||
|
indexnode = addnodes.index()
|
||
|
indexnode['entries'] = []
|
||
|
indexnode['inline'] = False
|
||
|
self.set_source_info(indexnode)
|
||
|
for entry in arguments:
|
||
|
indexnode['entries'].extend(process_index_entry(entry, targetnode['ids'][0]))
|
||
|
return [indexnode, targetnode]
|
||
|
|
||
|
|
||
|
class IndexRole(ReferenceRole):
|
||
|
def run(self) -> tuple[list[Node], list[system_message]]:
|
||
|
target_id = 'index-%s' % self.env.new_serialno('index')
|
||
|
if self.has_explicit_title:
|
||
|
# if an explicit target is given, process it as a full entry
|
||
|
title = self.title
|
||
|
entries = process_index_entry(self.target, target_id)
|
||
|
else:
|
||
|
# otherwise we just create a single entry
|
||
|
if self.target.startswith('!'):
|
||
|
title = self.title[1:]
|
||
|
entries = [('single', self.target[1:], target_id, 'main', None)]
|
||
|
else:
|
||
|
title = self.title
|
||
|
entries = [('single', self.target, target_id, '', None)]
|
||
|
|
||
|
index = addnodes.index(entries=entries)
|
||
|
target = nodes.target('', '', ids=[target_id])
|
||
|
text = nodes.Text(title)
|
||
|
self.set_source_info(index)
|
||
|
return [index, target, text], []
|
||
|
|
||
|
|
||
|
def setup(app: Sphinx) -> dict[str, Any]:
|
||
|
app.add_domain(IndexDomain)
|
||
|
app.add_directive('index', IndexDirective)
|
||
|
app.add_role('index', IndexRole())
|
||
|
|
||
|
return {
|
||
|
'version': 'builtin',
|
||
|
'env_version': 1,
|
||
|
'parallel_read_safe': True,
|
||
|
'parallel_write_safe': True,
|
||
|
}
|