usse/scrape/venv/lib/python3.10/site-packages/sphinx/domains/rst.py
2023-12-22 15:26:01 +01:00

300 lines
10 KiB
Python

"""The reStructuredText domain."""
from __future__ import annotations
import re
from typing import TYPE_CHECKING, Any, cast
from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.locale import _, __
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.nodes import make_id, make_refnode
if TYPE_CHECKING:
from collections.abc import Iterator
from docutils.nodes import Element
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.environment import BuildEnvironment
from sphinx.util.typing import OptionSpec
logger = logging.getLogger(__name__)
dir_sig_re = re.compile(r'\.\. (.+?)::(.*)$')
class ReSTMarkup(ObjectDescription[str]):
"""
Description of generic reST markup.
"""
option_spec: OptionSpec = {
'no-index': directives.flag,
'no-index-entry': directives.flag,
'no-contents-entry': directives.flag,
'no-typesetting': directives.flag,
'noindex': directives.flag,
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
}
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
node_id = make_id(self.env, self.state.document, self.objtype, name)
signode['ids'].append(node_id)
self.state.document.note_explicit_target(signode)
domain = cast(ReSTDomain, self.env.get_domain('rst'))
domain.note_object(self.objtype, name, node_id, location=signode)
if 'no-index-entry' not in self.options:
indextext = self.get_index_text(self.objtype, name)
if indextext:
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
def get_index_text(self, objectname: str, name: str) -> str:
return ''
def _object_hierarchy_parts(self, sig_node: desc_signature) -> tuple[str, ...]:
if 'fullname' not in sig_node:
return ()
directive_names = []
for parent in self.env.ref_context.get('rst:directives', ()):
directive_names += parent.split(':')
name = sig_node['fullname']
return tuple(directive_names + name.split(':'))
def _toc_entry_name(self, sig_node: desc_signature) -> str:
if not sig_node.get('_toc_parts'):
return ''
config = self.env.app.config
objtype = sig_node.parent.get('objtype')
*parents, name = sig_node['_toc_parts']
if objtype == 'directive:option':
return f':{name}:'
if config.toc_object_entries_show_parents in {'domain', 'all'}:
name = ':'.join(sig_node['_toc_parts'])
if objtype == 'role':
return f':{name}:'
if objtype == 'directive':
return f'.. {name}::'
return ''
def parse_directive(d: str) -> tuple[str, str]:
"""Parse a directive signature.
Returns (directive, arguments) string tuple. If no arguments are given,
returns (directive, '').
"""
dir = d.strip()
if not dir.startswith('.'):
# Assume it is a directive without syntax
return (dir, '')
m = dir_sig_re.match(dir)
if not m:
return (dir, '')
parsed_dir, parsed_args = m.groups()
if parsed_args.strip():
return (parsed_dir.strip(), ' ' + parsed_args.strip())
else:
return (parsed_dir.strip(), '')
class ReSTDirective(ReSTMarkup):
"""
Description of a reST directive.
"""
def handle_signature(self, sig: str, signode: desc_signature) -> str:
name, args = parse_directive(sig)
desc_name = f'.. {name}::'
signode['fullname'] = name.strip()
signode += addnodes.desc_name(desc_name, desc_name)
if len(args) > 0:
signode += addnodes.desc_addname(args, args)
return name
def get_index_text(self, objectname: str, name: str) -> str:
return _('%s (directive)') % name
def before_content(self) -> None:
if self.names:
directives = self.env.ref_context.setdefault('rst:directives', [])
directives.append(self.names[0])
def after_content(self) -> None:
directives = self.env.ref_context.setdefault('rst:directives', [])
if directives:
directives.pop()
class ReSTDirectiveOption(ReSTMarkup):
"""
Description of an option for reST directive.
"""
option_spec: OptionSpec = ReSTMarkup.option_spec.copy()
option_spec.update({
'type': directives.unchanged,
})
def handle_signature(self, sig: str, signode: desc_signature) -> str:
try:
name, argument = re.split(r'\s*:\s+', sig.strip(), maxsplit=1)
except ValueError:
name, argument = sig, None
desc_name = f':{name}:'
signode['fullname'] = name.strip()
signode += addnodes.desc_name(desc_name, desc_name)
if argument:
signode += addnodes.desc_annotation(' ' + argument, ' ' + argument)
if self.options.get('type'):
text = ' (%s)' % self.options['type']
signode += addnodes.desc_annotation(text, text)
return name
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
domain = cast(ReSTDomain, self.env.get_domain('rst'))
directive_name = self.current_directive
if directive_name:
prefix = '-'.join([self.objtype, directive_name])
objname = ':'.join([directive_name, name])
else:
prefix = self.objtype
objname = name
node_id = make_id(self.env, self.state.document, prefix, name)
signode['ids'].append(node_id)
self.state.document.note_explicit_target(signode)
domain.note_object(self.objtype, objname, node_id, location=signode)
if directive_name:
key = name[0].upper()
pair = [_('%s (directive)') % directive_name,
_(':%s: (directive option)') % name]
self.indexnode['entries'].append(('pair', '; '.join(pair), node_id, '', key))
else:
key = name[0].upper()
text = _(':%s: (directive option)') % name
self.indexnode['entries'].append(('single', text, node_id, '', key))
@property
def current_directive(self) -> str:
directives = self.env.ref_context.get('rst:directives')
if directives:
return directives[-1]
else:
return ''
class ReSTRole(ReSTMarkup):
"""
Description of a reST role.
"""
def handle_signature(self, sig: str, signode: desc_signature) -> str:
desc_name = f':{sig}:'
signode['fullname'] = sig.strip()
signode += addnodes.desc_name(desc_name, desc_name)
return sig
def get_index_text(self, objectname: str, name: str) -> str:
return _('%s (role)') % name
class ReSTDomain(Domain):
"""ReStructuredText domain."""
name = 'rst'
label = 'reStructuredText'
object_types = {
'directive': ObjType(_('directive'), 'dir'),
'directive:option': ObjType(_('directive-option'), 'dir'),
'role': ObjType(_('role'), 'role'),
}
directives = {
'directive': ReSTDirective,
'directive:option': ReSTDirectiveOption,
'role': ReSTRole,
}
roles = {
'dir': XRefRole(),
'role': XRefRole(),
}
initial_data: dict[str, dict[tuple[str, str], str]] = {
'objects': {}, # fullname -> docname, objtype
}
@property
def objects(self) -> dict[tuple[str, str], tuple[str, str]]:
return self.data.setdefault('objects', {}) # (objtype, fullname) -> (docname, node_id)
def note_object(self, objtype: str, name: str, node_id: str, location: Any = None) -> None:
if (objtype, name) in self.objects:
docname, node_id = self.objects[objtype, name]
logger.warning(__('duplicate description of %s %s, other instance in %s') %
(objtype, name, docname), location=location)
self.objects[objtype, name] = (self.env.docname, node_id)
def clear_doc(self, docname: str) -> None:
for (typ, name), (doc, _node_id) in list(self.objects.items()):
if doc == docname:
del self.objects[typ, name]
def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> None:
# XXX check duplicates
for (typ, name), (doc, node_id) in otherdata['objects'].items():
if doc in docnames:
self.objects[typ, name] = (doc, node_id)
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element,
) -> Element | None:
objtypes = self.objtypes_for_role(typ)
if not objtypes:
return None
for objtype in objtypes:
result = self.objects.get((objtype, target))
if result:
todocname, node_id = result
return make_refnode(builder, fromdocname, todocname, node_id,
contnode, target + ' ' + objtype)
return None
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
) -> list[tuple[str, Element]]:
results: list[tuple[str, Element]] = []
for objtype in self.object_types:
result = self.objects.get((objtype, target))
if result:
todocname, node_id = result
results.append(
('rst:' + self.role_for_objtype(objtype), # type: ignore[operator]
make_refnode(builder, fromdocname, todocname, node_id,
contnode, target + ' ' + objtype)))
return results
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
for (typ, name), (docname, node_id) in self.data['objects'].items():
yield name, name, typ, docname, node_id, 1
def setup(app: Sphinx) -> dict[str, Any]:
app.add_domain(ReSTDomain)
return {
'version': 'builtin',
'env_version': 2,
'parallel_read_safe': True,
'parallel_write_safe': True,
}