mirror of
https://github.com/usestrix/strix.git
synced 2026-08-28 05:25:00 +00:00
fix(viewer): keep balanced nested emphasis working
Restricting emphasis content to non-delimiters made `**bold with *italic* inside**` render its outer delimiters literally. Bold spans now only require a non-delimiter at each end (so `******` still stays literal) and italic content may contain complete <b>..</b> spans, which keeps nesting working while still ruling out crossed tags.
This commit is contained in:
parent
87d47eb390
commit
bc1b0b7ec2
2 changed files with 14 additions and 7 deletions
|
|
@ -417,14 +417,15 @@ def _inline_md(text: str) -> str:
|
|||
codes.append(match.group(1))
|
||||
return f"\x00{len(codes) - 1}\x00"
|
||||
|
||||
# Emphasis content may not contain its own delimiter or a tag, so the three passes can
|
||||
# never interleave into crossed markup (``<b><i>..</b></i>``), which reportlab rejects.
|
||||
# A run of asterisks such as a masked secret (``******``) therefore stays literal.
|
||||
# Bold spans must start and end with a non-delimiter, so a run of asterisks such as a masked
|
||||
# secret (``******``) stays literal. Italic content is plain text or complete ``<b>..</b>``
|
||||
# spans, so the passes can nest (``**a *b* c**``, ``*a **b** c*``) but can never interleave
|
||||
# into crossed markup (``<b><i>..</b></i>``), which reportlab rejects.
|
||||
seg = html.escape(re.sub(r"`([^`]+)`", _stash, text))
|
||||
seg = re.sub(r"\*\*\*([^*<>\n]+?)\*\*\*", r"<b><i>\1</i></b>", seg)
|
||||
seg = re.sub(r"\*\*([^*<>\n]+?)\*\*", r"<b>\1</b>", seg)
|
||||
seg = re.sub(r"__([^_<>\n]+?)__", r"<b>\1</b>", seg)
|
||||
seg = re.sub(r"\*([^*<>\n]+?)\*", r"<i>\1</i>", seg)
|
||||
seg = re.sub(r"\*\*\*(?=[^*])(.+?)(?<=[^*])\*\*\*", r"<b><i>\1</i></b>", seg)
|
||||
seg = re.sub(r"\*\*(?=[^*])(.+?)(?<=[^*])\*\*", r"<b>\1</b>", seg)
|
||||
seg = re.sub(r"__(?=[^_])(.+?)(?<=[^_])__", r"<b>\1</b>", seg)
|
||||
seg = re.sub(r"\*((?:[^*<>\n]|<b>[^<>*\n]*</b>)+?)\*", r"<i>\1</i>", seg)
|
||||
|
||||
def _restore(match: re.Match[str]) -> str:
|
||||
inner = html.escape(codes[int(match.group(1))])
|
||||
|
|
|
|||
|
|
@ -156,3 +156,9 @@ def test_generate_report_pdf_with_masked_secret_in_summary(tmp_path: Path) -> No
|
|||
def test_para_falls_back_to_plain_text_on_crossed_markup() -> None:
|
||||
para = _para("<b><i>x</b></i> & y", ParagraphStyle("t"))
|
||||
assert para.getPlainText() == "x & y"
|
||||
|
||||
|
||||
def test_inline_md_keeps_balanced_nested_emphasis() -> None:
|
||||
assert _inline_md("**bold with *italic* inside**") == "<b>bold with <i>italic</i> inside</b>"
|
||||
assert _inline_md("*outer **bold** inner*") == "<i>outer <b>bold</b> inner</i>"
|
||||
assert "******" in _inline_md("(observed as '******')") # a masked secret stays literal
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue