diff --git a/sphinx/application.py b/sphinx/application.py index 209c73202..05d302c81 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -83,6 +83,7 @@ builtin_extensions = ( 'sphinx.directives.code', 'sphinx.directives.other', 'sphinx.directives.patches', + 'sphinx.io', 'sphinx.parsers', 'sphinx.roles', 'sphinx.transforms.post_transforms', diff --git a/sphinx/io.py b/sphinx/io.py index c290e7831..418e91b12 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -120,7 +120,9 @@ def SphinxDummySourceClass(source, *args, **kwargs): return source -class SphinxFileInput(FileInput): +class SphinxBaseFileInput(FileInput): + """A base class of SphinxFileInput.""" + def __init__(self, app, env, *args, **kwds): # type: (Sphinx, BuildEnvironment, Any, Any) -> None self.app = app @@ -145,16 +147,7 @@ class SphinxFileInput(FileInput): # emit source-read event arg = [data] self.app.emit('source-read', self.env.docname, arg) - data = arg[0] - - parser = self.app.registry.get_source_parser(self.source_path) - docinfo, data = split_docinfo(data) - if 'restructuredtext' in parser.supported: - if self.env.config.rst_epilog: - data = data + '\n' + self.env.config.rst_epilog + '\n' - if self.env.config.rst_prolog: - data = self.env.config.rst_prolog + '\n' + data - return docinfo + data + return arg[0] def warn_and_replace(self, error): # type: (Any) -> Tuple @@ -172,12 +165,29 @@ class SphinxFileInput(FileInput): return (u'?', error.end) +class SphinxFileInput(SphinxBaseFileInput): + pass + + +class SphinxRSTFileInput(SphinxBaseFileInput): + def read(self): + # type: () -> unicode + data = SphinxBaseFileInput.read(self) + docinfo, data = split_docinfo(data) + if self.env.config.rst_epilog: + data = data + '\n' + self.env.config.rst_epilog + '\n' + if self.env.config.rst_prolog: + data = self.env.config.rst_prolog + '\n' + data + return docinfo + data + + def read_doc(app, env, filename): # type: (Sphinx, BuildEnvironment, unicode) -> nodes.document """Parse a document and convert to doctree.""" + input_class = app.registry.get_source_input(filename) reader = SphinxStandaloneReader() - source = SphinxFileInput(app, env, source=None, source_path=filename, - encoding=env.config.source_encoding) + source = input_class(app, env, source=None, source_path=filename, + encoding=env.config.source_encoding) parser = app.registry.create_source_parser(app, filename) pub = Publisher(reader=reader, @@ -190,3 +200,8 @@ def read_doc(app, env, filename): pub.set_source(source, filename) pub.publish() return pub.document + + +def setup(app): + app.registry.add_source_input('*', SphinxFileInput) + app.registry.add_source_input('restructuredtext', SphinxRSTFileInput) diff --git a/sphinx/registry.py b/sphinx/registry.py index 3723bcc29..b627f23af 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -30,6 +30,7 @@ if False: # For type annotation from typing import Any, Callable, Dict, Iterator, List, Type # NOQA from docutils import nodes # NOQA + from docutils.io import Input # NOQA from docutils.parsers import Parser # NOQA from sphinx.application import Sphinx # NOQA from sphinx.builders import Builder # NOQA @@ -50,6 +51,7 @@ class SphinxComponentRegistry(object): self.builders = {} # type: Dict[unicode, Type[Builder]] self.domains = {} # type: Dict[unicode, Type[Domain]] self.source_parsers = {} # type: Dict[unicode, Parser] + self.source_inputs = {} # type: Dict[unicode, Input] self.translators = {} # type: Dict[unicode, nodes.NodeVisitor] def add_builder(self, builder): @@ -190,6 +192,28 @@ class SphinxComponentRegistry(object): parser.set_application(app) return parser + def add_source_input(self, filetype, input_class): + # type: (unicode, Type[Input]) -> None + if filetype in self.source_inputs: + raise ExtensionError(__('source_input for %r is already registered') % filetype) + self.source_inputs[filetype] = input_class + + def get_source_input(self, filename): + # type: (unicode) -> Type[Input] + parser = self.get_source_parser(filename) + for filetype in parser.supported: + if filetype in self.source_inputs: + input_class = self.source_inputs[filetype] + break + else: + # use special source_input for unknown file-type '*' (if exists) + input_class = self.source_inputs.get('*') + + if input_class is None: + raise SphinxError(__('source_input for %s not registered') % filename) + else: + return input_class + def add_translator(self, name, translator): # type: (unicode, Type[nodes.NodeVisitor]) -> None self.translators[name] = translator