diff --git a/src/mistune/renderers/markdown.py b/src/mistune/renderers/markdown.py index 8c00917..1bb0062 100644 --- a/src/mistune/renderers/markdown.py +++ b/src/mistune/renderers/markdown.py @@ -152,7 +152,7 @@ def block_quote(self, token: Dict[str, Any], state: BlockState) -> str: # strip the children's trailing blank lines first so the quote marker is # not added to a dangling empty line; stripping it back off afterwards # would also eat a ">" that ends the content (an autolink or HTML tag). - text = self.render_children(token, state).rstrip("\n") + text = _render_block_quote_children(self, token, state).rstrip("\n") text = indent(text, "> ", lambda _: True) return text + "\n\n" @@ -216,6 +216,19 @@ def _escape_title(title: str) -> str: return title.replace("\\", "\\\\").replace('"', '\\"') +def _render_block_quote_children(renderer: MarkdownRenderer, token: Dict[str, Any], state: BlockState) -> str: + children = token["children"] + parts: list[str] = [] + previous_type = "" + for child in children: + child_type = child["type"] + if parts and previous_type != "blank_line" and child_type != "blank_line": + parts[-1] = parts[-1].rstrip("\n") + "\n" + parts.append(renderer.render_token(child, state)) + previous_type = child_type + return "".join(parts) + + def _escape_block_prefix(text: str) -> str: """Backslash-escape a leading block marker on each line so that literal text is not re-parsed as a list, heading or block quote.""" diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 2076eb0..50a2ef7 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -112,6 +112,13 @@ def test_block_quote_content_ending_in_gt(self): ): self.assert_round_trip(text) + def test_block_quote_adjacent_child_blocks(self): + reformat = create_markdown(renderer=MarkdownRenderer()) + self.assertEqual( + reformat("> #### Lorem\n> Ipsum"), + "> #### Lorem\n> Ipsum\n", + ) + def test_multiline_setext_heading(self): # a setext heading may span several lines; re-emitting it as an ATX # heading ("# ...") drops every line after the first out of the