From 73d18a2f0b31c448adc1e78f1bb5c237c80c990f Mon Sep 17 00:00:00 2001 From: Jiahao Guo Date: Sun, 5 Oct 2025 02:45:43 +0800 Subject: [PATCH] directive matching enhancement and doc correction --- docs/conf.rst | 4 +- src/sphinxnotes/mock/__init__.py | 66 ++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/docs/conf.rst b/docs/conf.rst index 916e837..8367ecb 100644 --- a/docs/conf.rst +++ b/docs/conf.rst @@ -15,12 +15,12 @@ The extension provides the following configuration: - mock ``toctree`` directive in default mode - mock ``contents`` directive in ``hide`` mode -:mock_default_mode: (Type: ``str``, ''Default: ``'hide'``) +:mock_default_mode: (Type: ``str``, Default: ``'hide'``) The default mode for mocking a directive/role. Available values: :hide: Hide the directive, it will not be seen on the document. - :literal_block: Show the raw text of directive in a `literal block`__ + :literal: Show the raw text of directive in a `literal block`__ __ https://docutils.sourceforge.io/docs/user/rst/quickref.html#literal-blocks diff --git a/src/sphinxnotes/mock/__init__.py b/src/sphinxnotes/mock/__init__.py index db43ccc..1b8ad7a 100644 --- a/src/sphinxnotes/mock/__init__.py +++ b/src/sphinxnotes/mock/__init__.py @@ -3,7 +3,7 @@ from sphinx.util import logging from sphinx.application import Sphinx -from sphinx.config import Config +from sphinx.config import Config, ENUM from sphinx.util.docutils import SphinxDirective from docutils.parsers.rst import directives @@ -24,43 +24,69 @@ def __getitem__(self, _): return directives.unchanged -class MockDirective(SphinxDirective): +class _MockDirectiveLiteral(SphinxDirective): + """Mock directive that shows the directive as a literal block.""" optional_arguments = 1 final_argument_whitespace = True option_spec = MockOptionSpec() has_content = True def run(self) -> List[nodes.Node]: - mode = None - for d in self.config.mock_directives: - name = d if isinstance(d, str) else d[0] - if self.name != name: - continue - mode = self.config.mock_default_mode if isinstance(d, str) else d[1] - break + literal = nodes.literal_block(self.block_text, self.block_text) + literal['language'] = 'rst' + return [literal] - if mode == 'literal': - literal = nodes.literal_block(self.block_text, self.block_text) - literal['language'] = 'rst' - return [literal] - elif mode == 'hide': - return [] - else: - raise ValueError('unsupported mock mode') +class _MockDirectiveHide(SphinxDirective): + """Mock directive that hides the directive content.""" + optional_arguments = 1 + final_argument_whitespace = True + option_spec = MockOptionSpec() + has_content = True + + def run(self) -> List[nodes.Node]: + return [] + + +_MOCK_DIRECTIVE_CLASSES = { + 'literal': _MockDirectiveLiteral, + 'hide': _MockDirectiveHide, +} def _config_inited(app:Sphinx, config:Config) -> None: for d in config.mock_directives: name = d if isinstance(d, str) else d[0] - app.add_directive(name, MockDirective, override=True) + mode = config.mock_default_mode if isinstance(d, str) else d[1] + + if mode not in ('literal', 'hide'): + raise ValueError( + f'Invalid mock mode for directive "{name}": {mode}. ' + f'Must be "literal" or "hide"' + ) + + directive_class = _MOCK_DIRECTIVE_CLASSES[mode] + app.add_directive(name, directive_class, override=True) def setup(app:Sphinx) -> Dict: """Sphinx extension entrypoint.""" - app.add_config_value('mock_directives', [], 'env') - app.add_config_value('mock_default_mode', 'hide', 'env') + app.add_config_value( + 'mock_directives', + default=[], + rebuild='env', + types=list, + description='List of directive names to mock. Each item can be a string (directive name) ' + 'or a tuple (directive name, mode).' + ) + app.add_config_value( + 'mock_default_mode', + default='hide', + rebuild='env', + types=ENUM('hide', 'literal'), + description='Default mode for mocking directives. Valid values: "hide", "literal".' + ) app.connect('config-inited', _config_inited) return {'version': __version__}