Sashmark / Blog

How to underline text in Markdown: pandoc test results

Bobby Huang ·

Under Underscores in plain CommonMark, you'll find a spec search and pandoc output for one and two underscores. The section Bracketed spans with an underline class shows which pandoc readers made a u element from a span in this test. Under Raw HTML u and ins tags, you'll see pandoc output for hand-typed u and ins tags. Test lines where pandoc kept the brackets as text are collected under When the brackets stay as text. The section Test a line for underline output walks through numbered steps for your own files.

Key takeaways

  • Spec search counts and the underscore test sit under Underscores in plain CommonMark.
  • Reader settings that gave a u element from a span are listed under Bracketed spans with an underline class.
  • Output for hand-typed u and ins tags is shown in the section Raw HTML u and ins tags.
  • Escaped, spaced and code-span versions of the span are tested under When the brackets stay as text.
  • A span wrapped in bold markers gets its own test in the section Underline inside bold.

You put underscores around a name to underline it, and pandoc handed back an <em> element. Two underscores gave you <strong>. Neither run produced an underline element in this test.

Here's how to underline text in Markdown using what pandoc 3.8.3 accepted on one machine: a bracketed span with an underline class, and the raw HTML <u> and <ins> tags. The outputs quoted here came from those tests.

Underscores in plain CommonMark

Underlining gets no syntax of its own in CommonMark. A case-insensitive grep of spec release 0.31.2 for "underline" matched 10 lines, and each one belongs to the text on setext heading underlines (lines 996 to 1598, plus line 8672). Searches for <u> and <ins> in the spec matched 0 lines each.

In the spec, underscores belong to emphasis. Lines 5771 to 5773 say emphasis starts and ends with the same character, _ or *, and lines 5781 to 5784 say the same for strong emphasis.

The test file ul-emph.md held one line:

_Jane Doe_ and __Doe Holdings__

On that file, pandoc -f commonmark -t html ul-emph.md put <em> around the name and <strong> around the company, both inside one paragraph. Switching to the markdown reader returned an identical line. A single underscore per side made <em>, a pair made <strong>, and no <u> element appeared. The guide to bold and italic markers lays out the star and underscore rules in full.

This test also searched pandoc's extension lists for commonmark and markdown for the word underline. No extension name matched.

Bracketed spans with an underline class

Pandoc calls the bracket form the bracketed_spans extension. The text goes between square brackets, and a class sits in curly braces straight after them. Pandoc's extension lists, printed during this test, showed +bracketed_spans under the markdown reader and -bracketed_spans under commonmark. That means markdown has the extension on from the start and commonmark has it off.

The test file ul-span.md:

[Jane Doe]{.underline}

Three reader settings ran on it:

  • pandoc -f commonmark -t html ul-span.md returned <p>[Jane Doe]{.underline}</p>. The brackets and the braces came through as plain text.
  • pandoc -f commonmark+bracketed_spans -t html ul-span.md returned <p><u>Jane Doe</u></p>.
  • pandoc -f markdown -t html ul-span.md gave the identical <u> output without any extension flag.

The span behaved the same way mid-sentence. The file ul-x-inline.md held this line:

Jane Doe reads the [Doe Holdings]{.underline} memo today.

Both commonmark+bracketed_spans and markdown returned this line: <p>Jane Doe reads the <u>Doe Holdings</u> memo today.</p>. Plain commonmark, lacking the extension, gave the sentence back as typed, wrapped in a paragraph.

A shorter class name split the two readers. For [Jane Doe]{.ul}, the markdown reader returned <p><u>Jane Doe</u></p>. The commonmark+bracketed_spans setting returned <p><span class="ul">Jane Doe</span></p> instead. With the full .underline class, both settings produced a <u> element.

Raw HTML u and ins tags

Another route skips Markdown syntax: type the tag yourself. Section 6.6 of the spec (lines 8177 to 8182) treats text inside angle brackets that looks like a tag as raw HTML and sends it to the output unescaped. That passage also says the tag names it accepts include ones outside current HTML.

The test file ul-html.md:

<u>Jane Doe</u>

The two readers, commonmark and markdown, each returned <p><u>Jane Doe</u></p>, with the tag kept as typed.

The test file ul-ins.md swapped in the other tag:

<ins>Jane Doe</ins>

From the commonmark reader, the output was <p><ins>Jane Doe</ins></p>. From the markdown reader, it was three lines with no paragraph tags:

<ins>
Jane Doe
</ins>

With the tag placed mid-sentence, as in ul-x-ins-inline.md, both readers kept it inside one paragraph: <p>Jane Doe reads the <ins>Doe Holdings</ins> memo today.</p>. The <u> version of that sentence, ul-x-html-inline.md, came out the same way under both readers, with <u> where <ins> had been.

These typed tags were the one form tested here that put a <u> or <ins> element in the output of plain commonmark with no flag. For what can happen to raw HTML once pandoc is done, the raw HTML walkthrough explains block-level HTML and why blank lines matter.

When the brackets stay as text

A few test lines held on to their brackets and braces even with a reader that knows the span. The extension-off case reuses ul-x-inline.md; the other three had test files of their own.

  • Backslashes in front of the brackets. For \[Jane Doe\]{.underline}, both markdown and commonmark+bracketed_spans returned <p>[Jane Doe]{.underline}</p>. Per spec line 481, any ASCII punctuation character may take a backslash, and lines 332 to 337 list square brackets as ASCII punctuation. For more on escapes, read the post on backslash escapes.
  • A space before the curly brace. For [Jane Doe] {.underline}, both settings gave the line back as typed, space and all.
  • Backticks around the span. ul-x-code.md held the span between backticks, and pandoc returned <code>[Jane Doe]{.underline}</code> under commonmark and markdown.
  • With -bracketed_spans on the reader. From pandoc -f markdown-bracketed_spans -t html ul-x-inline.md, it produced <p>Jane Doe reads the [Doe Holdings] memo today.</p>. The brackets stayed, the curly-brace part was gone from the output, and no <u> element came out.

Here is the code span line from ul-x-code.md:

See `[Jane Doe]{.underline}` here.

Underline inside bold

One more test file wrapped the span in double stars:

**[Jane Doe]{.underline}**

Under markdown and commonmark+bracketed_spans, pandoc returned <p><strong><u>Jane Doe</u></strong></p>. The <u> element sat inside <strong>, matching the order of the markers in the file. Only this one order was tested.

Test a line for underline output

If an underline doesn't show up, copy that line out on its own.

  1. Save just that line in a new test file.
  2. Feed that file to pandoc -f commonmark -t html. This test ran the same command on ul-span.md. If pandoc prints the brackets and braces back, you got the same result plain commonmark gave here.
  3. Repeat the run with -f markdown. To stay on the commonmark reader, use -f commonmark+bracketed_spans instead.
  4. Send the output through grep. The -c flag prints the number of matching lines. Here, pandoc -f markdown -t html ul-span.md | grep -c '<u>' printed 1. The same pipe with pandoc -f commonmark -t html ul-span.md | grep -c '<u>' printed 0.
  5. If grep prints 0, look at the cases under When the brackets stay as text: a space before the brace, backslashes before the brackets, or backticks around the span.
  6. To keep a plain commonmark run, swap the span for a typed <u> tag, repeat step 2 and look for the tag in the output.

Where Sashmark fits

Plans for Sashmark v1 cover fixing words right where they sit in the rendered view. Also in the plan, a clean Round-Trip would hold untouched lines to their exact original bytes. The v1 plan also includes a Changes View, planned to flag what changed in a file after your last read of it, with no git needed. It is in development and has not been released. The app itself has no AI features. See the planned features, the help pages and the roadmap for where each part stands.

Progress notes: sashmark.com.

Questions

Does CommonMark have an underline syntax?
For spec search counts plus pandoc underscore output, see the section Underscores in plain CommonMark.
How do I underline text with pandoc?
Pandoc runs on a span with an underline class are in the section Bracketed spans with an underline class.
Can I use a u tag instead of Markdown syntax?
Pandoc output for typed u and ins tags is shown under Raw HTML u and ins tags.
Why are my square brackets still in the output?
Under When the brackets stay as text, you'll find the test lines where pandoc printed the brackets as written.

What Sashmark plans for v1: Features · Roadmap

View as Markdown