Skip to content

simplifyHtml: render <b>, <i>, <s>, <strike> as Markdown emphasis instead of raw HTML #92

Description

@morisil

Problem

simplifyHtml keeps <b>, <i>, <s> and <strike> as raw HTML in the Markdown output, while their siblings <strong>, <em> and <del> become **…**, *…* and ~~…~~:

<p><b>bold</b> <strong>strong</strong> <i>it</i> <em>em</em> <s>gone</s></p>

renders as

<b>bold</b> **strong** <i>it</i> *em* <s>gone</s>

It shows up on real pages: the Hacker News dump fixture (markanywhere-html/src/commonTest/kotlin/dumps/HackerNewsTest.kt) has <b>[Hacker News](ref:2:news)</b> on its first line.

Current rationale

This is deliberate. b/i/s/strike are absent from MARKDOWN_NATIVE_TAGS (SimplifyHtml.kt), and the behaviour is pinned by SimplifyHtmlTest › "should preserve b i and s and strike tags as they are", whose comment gives the reason:

<b>, <i>, <s> elements were rescued from being "italic/bold" buttons into semantic roles, <strike> is depreciated, but we might still see it in the content

That refers to HTML5 giving these tags meanings of their own: <b> draws attention without implying importance, <i> marks idiomatic text or another voice, and <s> marks content that is no longer accurate. So writing them as **/*/~~ would turn them into <strong>/<em>/<del> if the Markdown were converted back to HTML. It came in with the original pipeline (#53) and nothing else argues for it.

Why reverse it

  • The main consumers read the Markdown; they don't convert it back to HTML. For an LLM or a human reader, **x** means "shown in bold". The difference between "draws attention" and "important" doesn't reach that reader either way, and they can't act on it.
  • The web doesn't follow the HTML5 meanings. Older sites, forums and plenty of CMS output use <b>/<i> simply for bold and italic. Wikipedia, for example, puts the article's subject in <b> and titles in <i>.
  • Raw tags are noise in a format meant to drop markup. They cost tokens, and a consumer that expects Markdown ends up handling stray HTML in ordinary text.
  • Little is lost. The only casualty is exact round-tripping from Markdown back to the original tag names, which no consumer relies on.

Proposed change

In simplifyHtml, rename the elements and emit them untagged, so the renderer writes native Markdown:

HTML becomes Markdown
<b> strong **…**
<i> em *…*
<s>, <strike> del ~~…~~

Keep <u> (and the rest of INLINE_FORMATTING_TAGS without a Markdown equivalent) as raw HTML, since Markdown has no underline.

Things to check:

  • Nesting and adjacency. For example <b><strong>x</strong></b>, <b>a</b><b>b</b>, <i> inside <em>, and emphasis in the middle of a word (foo<b>bar</b>baz). The existing delimiter-escaping logic (see EmphasisDelimiterRoundTripTest) must still produce Markdown that re-parses to the same structure, without growing on each round trip. Collapsing a redundant strong inside a strong is optional.
  • Empty icon elements such as <i class="fa-solid fa-sun"></i> must not become a bare **/*. dropBlankInlineFormatting should still remove them; add a test, since the element now goes through a renamed path.
  • HtmlToMarkdownTest already has an icon <i> fixture; keep it passing.
  • Don't touch the parser. A literal <b> written in Markdown source should still parse as a tagged b (see MarkanywhereParserTest, TransformationTest and the README examples). This change only affects the HTML → Markdown direction.

Tests to update

  • SimplifyHtmlTest › "should preserve b i and s and strike tags as they are": rewrite it to assert the mapping above, and give it a matching name.
  • HackerNewsTest: its first line should become **[Hacker News](ref:2:news)**.
  • Add a round-trip case to EmphasisDelimiterRoundTripTest for <b> containing a literal *.
  • Mention the behaviour change in the CHANGELOG/README if those describe simplifyHtml's tag handling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions