diff --git a/myst_parser/docutils_renderer.py b/myst_parser/docutils_renderer.py index 9caf091c..4c7340eb 100644 --- a/myst_parser/docutils_renderer.py +++ b/myst_parser/docutils_renderer.py @@ -584,12 +584,20 @@ def blocks_mathjax_processing(self) -> bool: def render_heading(self, token: SyntaxTreeNode) -> None: if self.md_env.get("match_titles", None) is False: + # this can occur if a nested parse is performed by a directive + # (such as an admonition) which contains a header. + # this would break the document structure self.create_warning( - "Header nested in this element can lead to unexpected outcomes", + "Disallowed nested header found, converting to rubric", line=token_line(token, default=0), subtype="nested_header", append_to=self.current_node, ) + rubric = nodes.rubric(token.content, "") + self.add_line_and_source_path(rubric, token) + with self.current_node_context(rubric, append=True): + self.render_children(token) + return # Test if we're replacing a section level first level = int(token.tag[1]) diff --git a/myst_parser/mocking.py b/myst_parser/mocking.py index 67ad4192..b562e144 100644 --- a/myst_parser/mocking.py +++ b/myst_parser/mocking.py @@ -133,7 +133,16 @@ def nested_parse( state_machine_class=None, state_machine_kwargs=None, ) -> None: - """Perform a nested parse of the input block, with ``node`` as the parent.""" + """Perform a nested parse of the input block, with ``node`` as the parent. + + :param block: The block of lines to parse. + :param input_offset: The offset of the first line of block, + to the starting line of the state (i.e. directive). + :param node: The parent node to attach the parsed content to. + :param match_titles: Whether to to allow the parsing of headings + (normally this is false, + since nested heading would break the document structure) + """ sm_match_titles = self.state_machine.match_titles render_match_titles = self._renderer.md_env.get("match_titles", None) self.state_machine.match_titles = self._renderer.md_env[ diff --git a/myst_parser/sphinx_renderer.py b/myst_parser/sphinx_renderer.py index 52118921..cecbc907 100644 --- a/myst_parser/sphinx_renderer.py +++ b/myst_parser/sphinx_renderer.py @@ -135,6 +135,11 @@ def render_heading(self, token: SyntaxTreeNode) -> None: The approach is similar to ``sphinx.ext.autosectionlabel`` """ super().render_heading(token) + + if not isinstance(self.current_node, nodes.section): + return + + # create the slug string slug = cast(str, token.attrGet("id")) if slug is None: return diff --git a/tests/test_renderers/fixtures/reporter_warnings.md b/tests/test_renderers/fixtures/reporter_warnings.md index 2fd6d677..3cbe211d 100644 --- a/tests/test_renderers/fixtures/reporter_warnings.md +++ b/tests/test_renderers/fixtures/reporter_warnings.md @@ -136,7 +136,7 @@ header nested in admonition # Header ``` . -:2: (WARNING/2) Header nested in this element can lead to unexpected outcomes +:2: (WARNING/2) Disallowed nested header found, converting to rubric . nested parse warning