diff --git a/myst_parser/config/main.py b/myst_parser/config/main.py index 4f3889d2..f22791a4 100644 --- a/myst_parser/config/main.py +++ b/myst_parser/config/main.py @@ -166,6 +166,14 @@ def _test_slug_func(text: str) -> str: return text[::-1] +def check_fence_as_directive( + inst: "MdParserConfig", field: dc.Field, value: Any +) -> None: + """Check that the extensions are a sequence of known strings""" + deep_iterable(instance_of(str), instance_of((list, tuple, set)))(inst, field, value) + setattr(inst, field.name, set(value)) + + @dc.dataclass() class MdParserConfig: """Configuration options for the Markdown Parser. @@ -250,6 +258,16 @@ def __repr__(self) -> str: }, ) + fence_as_directive: Set[str] = dc.field( + default_factory=set, + metadata={ + "validator": check_fence_as_directive, + "help": "Interpret a code fence as a directive, for certain language names. " + "This can be useful for fences like dot and mermaid, " + "and interoperability with other Markdown renderers.", + }, + ) + number_code_blocks: Sequence[str] = dc.field( default_factory=list, metadata={ diff --git a/myst_parser/mdit_to_docutils/base.py b/myst_parser/mdit_to_docutils/base.py index 653ce6a9..c85f4541 100644 --- a/myst_parser/mdit_to_docutils/base.py +++ b/myst_parser/mdit_to_docutils/base.py @@ -743,6 +743,13 @@ def render_fence(self, token: SyntaxTreeNode) -> None: return self.render_restructuredtext(token) if name.startswith("{") and name.endswith("}"): return self.render_directive(token, name[1:-1], arguments) + if name in self.md_config.fence_as_directive: + options = {k: str(v) for k, v in token.attrs.items()} + if "id" in options: + options["name"] = options.pop("id") + return self.render_directive( + token, name, arguments, additional_options=options + ) if not name and self.sphinx_env is not None: # use the current highlight setting, via the ``highlight`` directive, @@ -1664,7 +1671,12 @@ def render_restructuredtext(self, token: SyntaxTreeNode) -> None: self.current_node.extend(newdoc.children) def render_directive( - self, token: SyntaxTreeNode, name: str, arguments: str + self, + token: SyntaxTreeNode, + name: str, + arguments: str, + *, + additional_options: dict[str, str] | None = None, ) -> None: """Render special fenced code blocks as directives. @@ -1673,7 +1685,13 @@ def render_directive( :param arguments: The remaining text on the same line as the directive name. """ position = token_line(token) - nodes_list = self.run_directive(name, arguments, token.content, position) + nodes_list = self.run_directive( + name, + arguments, + token.content, + position, + additional_options=additional_options, + ) self.current_node += nodes_list def run_directive( diff --git a/tests/test_renderers/fixtures/myst-config.txt b/tests/test_renderers/fixtures/myst-config.txt index 62226c9a..86e36d76 100644 --- a/tests/test_renderers/fixtures/myst-config.txt +++ b/tests/test_renderers/fixtures/myst-config.txt @@ -468,3 +468,31 @@ My paragraph reversed . + +[fence_as_directive] --myst-fence-as-directive=unknown,admonition --myst-enable-extensions=attrs_block +. +```unknown +``` + +{#myname .class1} +{a=b} +```admonition title +content +``` +. + + + + Unknown directive type: 'unknown' [myst.directive_unknown] + + + 'admonition': Unknown option keys: ['a'] (allowed: ['class', 'name']) [myst.directive_parse] + + + title + <paragraph> + content + +<string>:1: (WARNING/2) Unknown directive type: 'unknown' [myst.directive_unknown] +<string>:6: (WARNING/2) 'admonition': Unknown option keys: ['a'] (allowed: ['class', 'name']) [myst.directive_parse] +.