Count inline code by depth so every span closes
Nested <code><code>x</code></code> opens two spans, but open state was a flag, so the first end tag cleared it and the guard added last time made the second a no-op. Output went from four backticks to three and the rest of the page read as code. The state is a depth now, so each end tag owes one backtick and the recovered-header guard still holds: a frame closes only the spans opened inside it.
This commit is contained in:
parent
39f8aa42ba
commit
d9eb4a2f9c
2 changed files with 24 additions and 11 deletions
|
|
@ -234,7 +234,7 @@ class _HeaderFrame:
|
|||
link_seq: int,
|
||||
cell_seq: int,
|
||||
in_pre: bool,
|
||||
in_code: bool,
|
||||
in_code: int,
|
||||
bq_depth: int,
|
||||
list_depth: int,
|
||||
):
|
||||
|
|
@ -356,7 +356,9 @@ class _MarkdownRenderer(HTMLParser):
|
|||
# Pre/code state
|
||||
self._in_pre: bool = False
|
||||
self._pre_parts: list[str] = []
|
||||
self._in_inline_code: bool = False
|
||||
# Depth, not a flag: <code><code>x</code></code> opens two spans and each
|
||||
# end tag owes a backtick, else the delimiters stop pairing.
|
||||
self._inline_code_depth: int = 0
|
||||
|
||||
# Blockquote state: stack of buffers so nested blockquotes get the right ">" depth.
|
||||
self._bq_stack: list[list[str]] = []
|
||||
|
|
@ -592,8 +594,8 @@ class _MarkdownRenderer(HTMLParser):
|
|||
self._finish_link()
|
||||
# Inline code opened OUTSIDE the header is the page's; closing it here would leave the real
|
||||
# </code> to emit an unpaired backtick.
|
||||
if self._in_inline_code and not frame.outer_in_code:
|
||||
self._in_inline_code = False
|
||||
while self._inline_code_depth > frame.outer_in_code:
|
||||
self._inline_code_depth -= 1
|
||||
self._emit("`")
|
||||
# Before the cell: _finish_row emits, and an open <pre> would swallow the
|
||||
# row into the code block as CODE| | instead of a cell holding the code.
|
||||
|
|
@ -658,7 +660,7 @@ class _MarkdownRenderer(HTMLParser):
|
|||
self._link_seq if self._in_link else -1,
|
||||
self._cell_seq if self._in_cell else -1,
|
||||
self._in_pre,
|
||||
self._in_inline_code,
|
||||
self._inline_code_depth,
|
||||
len(self._bq_stack),
|
||||
len(self._list_stack),
|
||||
)
|
||||
|
|
@ -793,7 +795,7 @@ class _MarkdownRenderer(HTMLParser):
|
|||
self._in_pre = True
|
||||
|
||||
elif tag == "code" and not self._in_pre:
|
||||
self._in_inline_code = True
|
||||
self._inline_code_depth += 1
|
||||
self._emit("`")
|
||||
|
||||
elif tag == "table":
|
||||
|
|
@ -866,8 +868,8 @@ class _MarkdownRenderer(HTMLParser):
|
|||
|
||||
# Already closed means a header frame recovered it; a second backtick here would leave the
|
||||
# rest of the page formatted as code.
|
||||
elif tag == "code" and not self._in_pre and self._in_inline_code:
|
||||
self._in_inline_code = False
|
||||
elif tag == "code" and not self._in_pre and self._inline_code_depth:
|
||||
self._inline_code_depth -= 1
|
||||
self._emit("`")
|
||||
|
||||
elif tag in ("th", "td"):
|
||||
|
|
@ -900,7 +902,7 @@ class _MarkdownRenderer(HTMLParser):
|
|||
self._pre_parts.append(data)
|
||||
return
|
||||
# Preserve literal whitespace inside inline <code> spans.
|
||||
if self._in_inline_code:
|
||||
if self._inline_code_depth:
|
||||
self._count_header_text(data)
|
||||
self._emit(data)
|
||||
return
|
||||
|
|
@ -939,8 +941,8 @@ class _MarkdownRenderer(HTMLParser):
|
|||
if self._in_link:
|
||||
self._finish_link()
|
||||
|
||||
if self._in_inline_code:
|
||||
self._in_inline_code = False
|
||||
while self._inline_code_depth:
|
||||
self._inline_code_depth -= 1
|
||||
self._emit("`")
|
||||
|
||||
self._finish_cell()
|
||||
|
|
|
|||
|
|
@ -2051,6 +2051,17 @@ def test_header_size_is_independent_of_the_buffer_it_renders_through():
|
|||
assert len(kept) == 1
|
||||
|
||||
|
||||
def test_nested_inline_code_closes_every_span_it_opened():
|
||||
# Two <code> elements owe two closing backticks. Tracking open/closed as a
|
||||
# flag let the first </code> answer for both and left the delimiters odd.
|
||||
body = "<main><article><p><code><code>x</code></code></p><p>%s</p></article></main>" % (
|
||||
"Body text here. " * 20,
|
||||
)
|
||||
out = html_to_markdown(f"<body>{body}</body>", main_content = True)
|
||||
assert out.count("`") % 2 == 0
|
||||
assert "``x``" in out
|
||||
|
||||
|
||||
def test_header_inside_open_inline_code_leaves_delimiters_paired():
|
||||
# The <code> opened outside the header, so closing it in the frame left </code> unpaired.
|
||||
body = "<main><code>head<header><h1>T</h1></header>tail</code><p>%s</p></main>" % (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue