x
%s
diff --git a/studio/backend/core/inference/_html_to_md.py b/studio/backend/core/inference/_html_to_md.py
index 7d88803dcb..a73b931969 100644
--- a/studio/backend/core/inference/_html_to_md.py
+++ b/studio/backend/core/inference/_html_to_md.py
@@ -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: 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
# 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 x
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 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()
diff --git a/studio/backend/tests/test_web_fetch_extraction.py b/studio/backend/tests/test_web_fetch_extraction.py
index a157f1e9aa..016eb29458 100644
--- a/studio/backend/tests/test_web_fetch_extraction.py
+++ b/studio/backend/tests/test_web_fetch_extraction.py
@@ -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 elements owe two closing backticks. Tracking open/closed as a
+ # flag let the first answer for both and left the delimiters odd.
+ body = "x
%s
" % (
+ "Body text here. " * 20,
+ )
+ out = html_to_markdown(f"{body}", main_content = True)
+ assert out.count("`") % 2 == 0
+ assert "``x``" in out
+
+
def test_header_inside_open_inline_code_leaves_delimiters_paired():
# The opened outside the header, so closing it in the frame left unpaired.
body = "headT
tail%s
" % (