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.
100 lines
3.1 KiB
100 lines
3.1 KiB
8 months ago
|
"""A Base class for additional parsers."""
|
||
|
|
||
|
from __future__ import annotations
|
||
|
|
||
|
from typing import TYPE_CHECKING
|
||
|
|
||
|
import docutils.parsers
|
||
|
import docutils.parsers.rst
|
||
|
from docutils import nodes
|
||
|
from docutils.parsers.rst import states
|
||
|
from docutils.statemachine import StringList
|
||
|
from docutils.transforms.universal import SmartQuotes
|
||
|
|
||
|
from sphinx.util.rst import append_epilog, prepend_prolog
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from docutils.transforms import Transform
|
||
|
|
||
|
from sphinx.application import Sphinx
|
||
|
from sphinx.config import Config
|
||
|
from sphinx.environment import BuildEnvironment
|
||
|
from sphinx.util.typing import ExtensionMetadata
|
||
|
|
||
|
|
||
|
class Parser(docutils.parsers.Parser):
|
||
|
"""
|
||
|
A base class of source parsers. The additional parsers should inherit this class instead
|
||
|
of ``docutils.parsers.Parser``. Compared with ``docutils.parsers.Parser``, this class
|
||
|
improves accessibility to Sphinx APIs.
|
||
|
|
||
|
The subclasses can access sphinx core runtime objects (app, config and env).
|
||
|
"""
|
||
|
|
||
|
#: The config object
|
||
|
config: Config
|
||
|
|
||
|
#: The environment object
|
||
|
env: BuildEnvironment
|
||
|
|
||
|
def set_application(self, app: Sphinx) -> None:
|
||
|
"""set_application will be called from Sphinx to set app and other instance variables
|
||
|
|
||
|
:param sphinx.application.Sphinx app: Sphinx application object
|
||
|
"""
|
||
|
self._app = app
|
||
|
self.config = app.config
|
||
|
self.env = app.env
|
||
|
|
||
|
|
||
|
class RSTParser(docutils.parsers.rst.Parser, Parser):
|
||
|
"""A reST parser for Sphinx."""
|
||
|
|
||
|
def get_transforms(self) -> list[type[Transform]]:
|
||
|
"""
|
||
|
Sphinx's reST parser replaces a transform class for smart-quotes by its own
|
||
|
|
||
|
refs: sphinx.io.SphinxStandaloneReader
|
||
|
"""
|
||
|
transforms = super().get_transforms()
|
||
|
transforms.remove(SmartQuotes)
|
||
|
return transforms
|
||
|
|
||
|
def parse(self, inputstring: str | StringList, document: nodes.document) -> None:
|
||
|
"""Parse text and generate a document tree."""
|
||
|
self.setup_parse(inputstring, document) # type: ignore[arg-type]
|
||
|
self.statemachine = states.RSTStateMachine(
|
||
|
state_classes=self.state_classes,
|
||
|
initial_state=self.initial_state,
|
||
|
debug=document.reporter.debug_flag,
|
||
|
)
|
||
|
|
||
|
# preprocess inputstring
|
||
|
if isinstance(inputstring, str):
|
||
|
lines = docutils.statemachine.string2lines(
|
||
|
inputstring, tab_width=document.settings.tab_width, convert_whitespace=True
|
||
|
)
|
||
|
|
||
|
inputlines = StringList(lines, document.current_source)
|
||
|
else:
|
||
|
inputlines = inputstring
|
||
|
|
||
|
self.decorate(inputlines)
|
||
|
self.statemachine.run(inputlines, document, inliner=self.inliner)
|
||
|
self.finish_parse()
|
||
|
|
||
|
def decorate(self, content: StringList) -> None:
|
||
|
"""Preprocess reST content before parsing."""
|
||
|
prepend_prolog(content, self.config.rst_prolog)
|
||
|
append_epilog(content, self.config.rst_epilog)
|
||
|
|
||
|
|
||
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
||
|
app.add_source_parser(RSTParser)
|
||
|
|
||
|
return {
|
||
|
'version': 'builtin',
|
||
|
'parallel_read_safe': True,
|
||
|
'parallel_write_safe': True,
|
||
|
}
|