Repository: ajalt/mordant (kotlin)

Issue:
Markdown fails to render dollar sign
Attempting to render text with a dollar sign as Markdown fails with an exception.

Example:
```kotlin
Terminal().println(Markdown("Just $10"))
```

this fails with
```
java.lang.IllegalStateException: Unexpected token when parsing inlines: org.intellij.markdown.ast.LeafASTNode@4982cc36; [Markdown:DOLLAR:'$'}]
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.parseInlines(MarkdownRenderer.kt:262)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.innerInlines$lambda$11(MarkdownRenderer.kt:274)
	at kotlin.text.StringsKt__AppendableKt.appendElement(Appendable.kt:84)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinTo(_Collections.kt:3493)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinToString(_Collections.kt:3510)
	at kotlin.collections.CollectionsKt___CollectionsKt.joinToString$default(_Collections.kt:3509)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.innerInlines(MarkdownRenderer.kt:274)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.innerInlines$default(MarkdownRenderer.kt:272)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.parseStructure(MarkdownRenderer.kt:119)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.parseBlocks$lambda$0(MarkdownRenderer.kt:69)
	at com.github.ajalt.mordant.table.TableDslKt.verticalLayout(TableDsl.kt:375)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.parseBlocks(MarkdownRenderer.kt:57)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.parseFile(MarkdownRenderer.kt:53)
	at com.github.ajalt.mordant.markdown.MarkdownRenderer.render(MarkdownRenderer.kt:47)
	at com.github.ajalt.mordant.markdown.Markdown.document(Markdown.kt:25)
	at com.github.ajalt.mordant.markdown.Markdown.render(Markdown.kt:31)
	at com.github.ajalt.mordant.rendering.Widget$DefaultImpls.render$default(Widget.kt:7)
	at com.github.ajalt.mordant.terminal.Terminal.render(Terminal.kt:279)
	at com.github.ajalt.mordant.terminal.Terminal.println(Terminal.kt:238)
	at com.github.ajalt.mordant.terminal.Terminal.println$default(Terminal.kt:237)
```

What works are dollar signs within backticks: 
```kotlin
Terminal().println(Markdown("`this $ works`"))
```

Pull request:
Fixes #182

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06534a733..c9c410f2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
 # Changelog
 
 ## Unreleased
+### Fixed
+- Fix markdown rendering not supporting math blocks [(#182)](https://github.com/ajalt/mordant/issues/182)
 
 ## 2.7.0
 ### Added
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b82aa23a4..a4413138c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 1aa94a426..b740cf133 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
diff --git a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/markdown/MarkdownRenderer.kt b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/markdown/MarkdownRenderer.kt
index da00326f9..d367a52d0 100644
--- a/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/markdown/MarkdownRenderer.kt
+++ b/mordant/src/commonMain/kotlin/com/github/ajalt/mordant/markdown/MarkdownRenderer.kt
@@ -59,7 +59,10 @@ internal class MarkdownRenderer(
                 // skip the extra EOL after top level block, since the layout adds it for us
                 if (node.type == MarkdownTokenTypes.EOL
                     && i in 1..<nodes.lastIndex
-                    && nodes[i - 1].type !in listOf(MarkdownTokenTypes.EOL, MarkdownTokenTypes.WHITE_SPACE)
+                    && nodes[i - 1].type !in listOf(
+                        MarkdownTokenTypes.EOL,
+                        MarkdownTokenTypes.WHITE_SPACE
+                    )
                 ) {
                     continue
                 }
@@ -91,8 +94,10 @@ internal class MarkdownRenderer(
             }
 
             MarkdownElementTypes.CODE_FENCE -> {
-                val start = node.children.indexOfFirst { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }
-                val end = node.children.indexOfLast { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }
+                val start =
+                    node.children.indexOfFirst { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }
+                val end =
+                    node.children.indexOfLast { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }
                 val dropLast = if (end < 0) 0 else node.children.lastIndex - end
                 val inner = innerInlines(node, drop = start, dropLast = dropLast)
                 val content = Text(
@@ -102,7 +107,7 @@ internal class MarkdownRenderer(
                 if (theme.flag("markdown.code.block.border")) Panel(content) else content
             }
 
-            MarkdownElementTypes.CODE_BLOCK -> {
+            MarkdownElementTypes.CODE_BLOCK-> {
                 val content = Text(
                     theme.style("markdown.code.block")(innerInlines(node)),
                     whitespace = Whitespace.PRE_WRAP
@@ -127,14 +132,53 @@ internal class MarkdownRenderer(
                 )
             }
 
-            MarkdownElementTypes.SETEXT_1 -> setext(theme.string("markdown.h1.rule"), theme.style("markdown.h1"), node)
-            MarkdownElementTypes.SETEXT_2 -> setext(theme.string("markdown.h2.rule"), theme.style("markdown.h2"), node)
-            MarkdownElementTypes.ATX_1 -> atxHr(theme.string("markdown.h1.rule"), theme.style("markdown.h1"), node)
-            MarkdownElementTypes.ATX_2 -> atxHr(theme.string("markdown.h2.rule"), theme.style("markdown.h2"), node)
-            MarkdownElementTypes.ATX_3 -> atxHr(theme.string("markdown.h3.rule"), theme.style("markdown.h3"), node)
-            MarkdownElementTypes.ATX_4 -> atxHr(theme.string("markdown.h4.rule"), theme.style("markdown.h4"), node)
-            MarkdownElementTypes.ATX_5 -> atxHr(theme.string("markdown.h5.rule"), theme.style("markdown.h5"), node)
-            MarkdownElementTypes.ATX_6 -> atxHr(theme.string("markdown.h6.rule"), theme.style("markdown.h6"), node)
+            MarkdownElementTypes.SETEXT_1 -> setext(
+                theme.string("markdown.h1.rule"),
+                theme.style("markdown.h1"),
+                node
+            )
+
+            MarkdownElementTypes.SETEXT_2 -> setext(
+                theme.string("markdown.h2.rule"),
+                theme.style("markdown.h2"),
+                node
+            )
+
+            MarkdownElementTypes.ATX_1 -> atxHr(
+                theme.string("markdown.h1.rule"),
+                theme.style("markdown.h1"),
+                node
+            )
+
+            MarkdownElementTypes.ATX_2 -> atxHr(
+                theme.string("markdown.h2.rule"),
+                theme.style("markdown.h2"),
+                node
+            )
+
+            MarkdownElementTypes.ATX_3 -> atxHr(
+                theme.string("markdown.h3.rule"),
+                theme.style("markdown.h3"),
+                node
+            )
+
+            MarkdownElementTypes.ATX_4 -> atxHr(
+                theme.string("markdown.h4.rule"),
+                theme.style("markdown.h4"),
+                node
+            )
+
+            MarkdownElementTypes.ATX_5 -> atxHr(
+                theme.string("markdown.h5.rule"),
+                theme.style("markdown.h5"),
+                node
+            )
+
+            MarkdownElementTypes.ATX_6 -> atxHr(
+                theme.string("markdown.h6.rule"),
+                theme.style("markdown.h6"),
+                node
+            )
 
             GFMElementTypes.TABLE -> table {
                 borderType = when {
@@ -166,7 +210,7 @@ internal class MarkdownRenderer(
     private fun parseInlines(node: ASTNode): String {
         return when (node.type) {
             // ElementTypes
-            MarkdownElementTypes.CODE_SPAN -> {
+            MarkdownElementTypes.CODE_SPAN, GFMElementTypes.INLINE_MATH -> {
                 // Trim the code as a kludge to prevent the background style from extending if the
                 // code ends with whitespace. It would be better to fix this in the text wrapping code.
                 theme.style("markdown.code.span")(
@@ -177,6 +221,16 @@ internal class MarkdownRenderer(
                 )
             }
 
+            // For some reason, math blocks are in a paragraph, unlike code blocks
+            GFMElementTypes.BLOCK_MATH -> {
+                theme.style("markdown.code.block")(
+                    input.substring(
+                        node.children[1].startOffset,
+                        node.children.last().startOffset
+                    ).trim()
+                )
+            }
+
             MarkdownElementTypes.EMPH -> {
                 theme.style("markdown.emph")(innerInlines(node, drop = 1))
             }
@@ -251,6 +305,7 @@ internal class MarkdownRenderer(
             MarkdownTokenTypes.URL,
             MarkdownTokenTypes.WHITE_SPACE,
             GFMTokenTypes.TILDE,
+            GFMTokenTypes.DOLLAR,
             -> node.nodeText()
 
             MarkdownTokenTypes.EOL -> {
@@ -323,7 +378,7 @@ internal class MarkdownRenderer(
         return HorizontalRule(
             content,
             ruleCharacter = bar,
-            ruleStyle =  TextStyle(style.color, style.bgColor)
+            ruleStyle = TextStyle(style.color, style.bgColor)
         ).withPadding { vertical = theme.dimension("markdown.header.padding") }
     }
 
@@ -407,7 +462,8 @@ internal class MarkdownRenderer(
     }
 
     private fun findLinkText(node: ASTNode): String? {
-        return node.findChildOfType(MarkdownElementTypes.LINK_TEXT)?.let { innerInlines(it, drop = 1) }
+        return node.findChildOfType(MarkdownElementTypes.LINK_TEXT)
+            ?.let { innerInlines(it, drop = 1) }
     }
 }
 


Test patch:
diff --git a/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/markdown/MarkdownTest.kt b/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/markdown/MarkdownTest.kt
index fd17718ac..9350dbfc2 100644
--- a/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/markdown/MarkdownTest.kt
+++ b/mordant/src/commonTest/kotlin/com/github/ajalt/mordant/markdown/MarkdownTest.kt
@@ -20,8 +20,8 @@ import kotlin.test.Test
 
 // Header rules are styled by removing the bold from the theme style
 private val TextStyle.colorOnly get() = TextStyle(color, bgColor)
-private  const val NEL = "\u0085"
-private  const val LS = "\u2028"
+private const val NEL = "\u0085"
+private const val LS = "\u2028"
 
 class MarkdownTest {
     private val quote = Theme.Default.style("markdown.blockquote")
@@ -58,9 +58,29 @@ class MarkdownTest {
     @Test
     fun tilde() = doTest("~", "~")
 
+    @Test
+    @JsName("dollar_sign")
+    fun `dollar sign`() = doTest("$", "$")
+
+    @Test
+    @JsName("math_block")
+    fun `math block`() = doTest(
+        """
+This expression uses `\${'$'}` to display a dollar sign: ${'$'}\sqrt{\${'$'}4}${'$'}
+
+${'$'}${'$'}\sum_{k=1}^n a_k^2${'$'}${'$'}
+""",
+        """
+This expression uses ${code("\\$")} to display a dollar sign: ${code("\\sqrt{\\\$4}")}
+
+${codeBlock("\\sum_{k=1}^n a_k^2")}
+"""
+    )
+
     @Test
     @JsName("test_paragraphs")
-    fun `test paragraphs`() = doTest("""
+    fun `test paragraphs`() = doTest(
+        """
 Paragraph one
 wrapped line.
 
@@ -80,11 +100,13 @@ Paragraph two wrapped line.
 
 
 Paragraph three line breaks.
-""")
+"""
+    )
 
     @Test
     @JsName("test_paragraphs_wrapped")
-    fun `test paragraphs wrapped`() = doTest("""
+    fun `test paragraphs wrapped`() = doTest(
+        """
 Paragraph one
 wrapped line.
 
@@ -98,11 +120,13 @@ line.
 Paragraph
 two wrapped
 line.
-""", width = 11)
+""", width = 11
+    )
 
     @Test
     @JsName("test_paragraphs_crlf")
-    fun `test paragraphs crlf`() = doTest("""
+    fun `test paragraphs crlf`() = doTest(
+        """
 Paragraph one
 wrapped line.
 
@@ -114,21 +138,25 @@ line.
 Paragraph one wrapped line.
 
 Paragraph two wrapped line.
-""")
+"""
+    )
 
     @Test
     @JsName("test_quotes")
-    fun `test quotes`() = doTest("""
+    fun `test quotes`() = doTest(
+        """
 This paragraph
 has some "double quotes"
 and some 'single quotes'.
 """, """
 This paragraph has some "double quotes" and some 'single quotes'.
-""")
+"""
+    )
 
     @Test
     @JsName("test_emphasis")
-    fun `test emphasis`() = doTest("""
+    fun `test emphasis`() = doTest(
+        """
 An *em span*.
 
 An _em span_.
@@ -152,11 +180,13 @@ A ${bold("strong span")}.
 A ${(bold + italic)("strong em span")}.
 
 A ${strikethrough("strikethrough span")}.
-""")
+"""
+    )
 
     @Test
     @JsName("test_unordered_list")
-    fun `test unordered list`() = doTest("""
+    fun `test unordered list`() = doTest(
+        """
 - line 1
 - line 2a
   line 2b
@@ -165,11 +195,13 @@ A ${strikethrough("strikethrough span")}.
  • line 1
  • line 2a line 2b
  • line 3
-""")
+"""
+    )
 
     @Test
     @JsName("test_unordered_list_wrap")
-    fun `test unordered list wrap`() = doTest("""
+    fun `test unordered list wrap`() = doTest(
+        """
 - line 1
 - line 2a
   line 2b
@@ -179,12 +211,14 @@ A ${strikethrough("strikethrough span")}.
  • line 2a
    line 2b
  • line 3
-""", width = 10)
+""", width = 10
+    )
 
     // https://github.github.com/gfm/#example-306
     @Test
     @JsName("test_unordered_list_nested")
-    fun `test unordered list nested`() = doTest("""
+    fun `test unordered list nested`() = doTest(
+        """
 - a
   - b
   - c
@@ -199,11 +233,13 @@ A ${strikethrough("strikethrough span")}.
  • d
     • e
     • f
-""")
+"""
+    )
 
     @Test
     @JsName("test_ordered_list")
-    fun `test ordered list`() = doTest("""
+    fun `test ordered list`() = doTest(
+        """
 1. line 1
 1. line 2a
    line 2b
@@ -212,11 +248,13 @@ A ${strikethrough("strikethrough span")}.
  1. line 1
  2. line 2a line 2b
  3. line 3
-""")
+"""
+    )
 
     @Test
     @JsName("test_ordered_list_wrap")
-    fun `test ordered list wrap`() = doTest("""
+    fun `test ordered list wrap`() = doTest(
+        """
 1. line 1
 1. line 2a
   line 2b
@@ -226,11 +264,13 @@ A ${strikethrough("strikethrough span")}.
  2. line 2a
     line 2b
  3. line 3
-""", width = 11)
+""", width = 11
+    )
 
     @Test
     @JsName("test_ordered_list_loose")
-    fun `test ordered list loose`() = doTest("""
+    fun `test ordered list loose`() = doTest(
+        """
 1. a
 
 1. b
@@ -240,11 +280,13 @@ A ${strikethrough("strikethrough span")}.
  1. a
  2. b
  3. c
-""")
+"""
+    )
 
     @Test
     @JsName("test_ordered_list_nested")
-    fun `test ordered list nested`() = doTest("""
+    fun `test ordered list nested`() = doTest(
+        """
 1. a
     1. b
     ░
@@ -260,11 +302,13 @@ A ${strikethrough("strikethrough span")}.
  2. d
      1. e
      2. f
-""")
+"""
+    )
 
     @Test
     @JsName("block_quote")
-    fun `block quote`() = doTest("""
+    fun `block quote`() = doTest(
+        """
 > line 1
 > line 2
 >
@@ -273,13 +317,15 @@ A ${strikethrough("strikethrough span")}.
 ${quote("▎ line 1 line 2")}
 ${quote("▎")}
 ${quote("▎ line 3")}
-""")
+"""
+    )
 
 
     // https://github.github.com/gfm/#example-206
     @Test
     @JsName("block_quote_with_header")
-    fun `block quote with header`() = doTest("""
+    fun `block quote with header`() = doTest(
+        """
 ># Foo
 >bar
 > baz
@@ -288,12 +334,14 @@ ${quote("▎")}
 ${quote("▎ ══ ${bold("Foo")} ═══")}
 ${quote("▎")}
 ${quote("▎ bar baz")}
-""", width = 10)
+""", width = 10
+    )
 
     // https://github.github.com/gfm/#example-208
     @Test
     @JsName("indented_block_quote")
-    fun `indented block quote`() = doTest("""
+    fun `indented block quote`() = doTest(
+        """
    > # Foo
    > bar
  > baz
@@ -302,13 +350,15 @@ ${quote("▎")}
 ${quote("▎ ══ ${(bold)("Foo")} ═══")}
 ${quote("▎")}
 ${quote("▎ bar baz")}
-""", width = 10)
+""", width = 10
+    )
 
 
     @Suppress("MarkdownUnresolvedFileReference")
     @Test
     @JsName("visible_links")
-    fun `visible links`() = doTest("""
+    fun `visible links`() = doTest(
+        """
 [a reference link][a link]
 
 [a link]
@@ -352,11 +402,13 @@ ${linkText("www.example.com/url")}
 <${linkText("autolink@example.com")}>
 
 ${linkDest("[a link]: example.com")}
-""")
+"""
+    )
 
     @Test
     @JsName("link_with_hyperlinks")
-    fun `link with hyperlinks`() = doTest("""
+    fun `link with hyperlinks`() = doTest(
+        """
 [a reference link][a link]
 
 [a link]
@@ -404,11 +456,13 @@ ${linkText("www.example.com/url")}
 <${linkText("autolink@example.com")}>
 
 
-""".normalizeHyperlinks(), hyperlinks = true)
+""".normalizeHyperlinks(), hyperlinks = true
+    )
 
     @Test
     @JsName("image_tags")
-    fun `image tags`() = doTest("""
+    fun `image tags`() = doTest(
+        """
 ![an image]( example.png "a title" )
 
 ![](example.png "a title")
@@ -432,11 +486,13 @@ ${linkText("www.example.com/url")}
 🖼️ ${imgAlt("an image")}
 
 ${linkDest("[1]: example.png")}
-""")
+"""
+    )
 
     @Test
     @JsName("md_in_link_titles")
-    fun `md in link titles`() = doTest("""
+    fun `md in link titles`() = doTest(
+        """
 [`code`](example.com/1)
 
 [![an image](img.png)](example.com/2)
@@ -444,21 +500,25 @@ ${linkDest("[1]: example.png")}
 ${(linkText + hyperlink("example.com/1"))("code")}
 
 ${(linkText + hyperlink("example.com/2"))("🖼️ ${imgAlt("an image")}")}
-""".normalizeHyperlinks(), hyperlinks = true)
+""".normalizeHyperlinks(), hyperlinks = true
+    )
 
     @Test
     @JsName("default_html")
-    fun `default html`() = doTest("""
+    fun `default html`() = doTest(
+        """
 <h1 align="center">
     <img src="example.svg">
     <p>text in html</p>
 </h1>
 """, """
-""")
+"""
+    )
 
     @Test
     @JsName("show_html")
-    fun `show html`() = doTest("""
+    fun `show html`() = doTest(
+        """
 <h1 align="center">
     <img src="example.svg">
     <p>text in html</p>
@@ -468,40 +528,49 @@ ${(linkText + hyperlink("example.com/2"))("🖼️ ${imgAlt("an image")}")}
     <img src="example.svg">
     <p>text in html</p>
 </h1>
-""", showHtml = true)
+""", showHtml = true
+    )
 
     @Test
     @JsName("default_html_inline")
-    fun `default html inline`() = doTest("""
+    fun `default html inline`() = doTest(
+        """
 Hello <b>world</b>.
 """, """
 Hello world.
-""")
+"""
+    )
 
     @Test
     @JsName("show_html_inline")
-    fun `show html inline`() = doTest("""
+    fun `show html inline`() = doTest(
+        """
 Hello <b>world</b>.
 """, """
 Hello <b>world</b>.
-""", showHtml = true)
+""", showHtml = true
+    )
 
     @Test
     @JsName("horizontal_rule")
-    fun `horizontal rule`() = doTest("""
+    fun `horizontal rule`() = doTest(
+        """
 ---
 """, """
 ──────────
-""", width = 10)
+""", width = 10
+    )
 
     @Test
     @JsName("header_1_empty")
-    fun `header 1 empty`() = doTest("""
+    fun `header 1 empty`() = doTest(
+        """
 #
 """, """
 
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("header_1_custom_padding")
@@ -519,67 +588,80 @@ ${h1.colorOnly.colorOnly("═══ ${bold("Header Text")} ═══")}
 
     @Test
     @JsName("header_1")
-    fun `header 1`() = doTest("""
+    fun `header 1`() = doTest(
+        """
 # Header Text
 """, """
 
 ${h1.colorOnly("═══ ${bold("Header Text")} ═══")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("header_2")
-    fun `header 2`() = doTest("""
+    fun `header 2`() = doTest(
+        """
 ## Header Text
 """, """
 
 ${h2.colorOnly("─── ${bold("Header Text")} ───")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("header_3")
-    fun `header 3`() = doTest("""
+    fun `header 3`() = doTest(
+        """
 ### Header Text
 """, """
 
 ${h3.colorOnly("    ${(bold + underline)("Header Text")}    ")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("header_4")
-    fun `header 4`() = doTest("""
+    fun `header 4`() = doTest(
+        """
 #### Header Text
 """, """
 
 ${h4.colorOnly("    ${underline("Header Text")}    ")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("header_5")
-    fun `header 5`() = doTest("""
+    fun `header 5`() = doTest(
+        """
 ##### Header Text
 """, """
 
 ${h5.colorOnly("    ${italic("Header Text")}    ")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("header_6")
-    fun `header 6`() = doTest("""
+    fun `header 6`() = doTest(
+        """
 ###### Header Text
 """, """
 
 ${h6.colorOnly("    ${dim("Header Text")}    ")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("adjacent_headers")
-    fun `adjacent headers`() = doTest("""
+    fun `adjacent headers`() = doTest(
+        """
 # Header 1
 
 ## Header 2
@@ -591,52 +673,62 @@ ${h1.colorOnly("════ ${bold("Header 1")} ═════")}
 
 ${h2.colorOnly("──── ${bold("Header 2")} ─────")}
 
-""", width = 19)
+""", width = 19
+    )
 
 
     @Test
     @JsName("header_trailing_chars")
-    fun `header trailing chars`() = doTest("""
+    fun `header trailing chars`() = doTest(
+        """
 # Header Text ##
 """, """
 
 ${h1.colorOnly("═══ ${bold("Header Text")} ═══")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("setext_h1")
-    fun `setext h1`() = doTest("""
+    fun `setext h1`() = doTest(
+        """
 Header Text
 ===========
 """, """
 
 ${h1.colorOnly("═══ ${bold("Header Text")} ═══")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("setext_h2")
-    fun `setext h2`() = doTest("""
+    fun `setext h2`() = doTest(
+        """
   Header Text  
 ---
 """, """
 
 ${h2.colorOnly("─── ${bold("Header Text")} ───")}
 
-""", width = 19)
+""", width = 19
+    )
 
     @Test
     @JsName("empty_code_span")
-    fun `empty code span`() = doTest("""
+    fun `empty code span`() = doTest(
+        """
 An `` empty code span.
 """, """
 An `` empty code span.
-""")
+"""
+    )
 
     @Test
     @JsName("code_span")
-    fun `code span`() = doTest("""
+    fun `code span`() = doTest(
+        """
 This `is ` a `code    <br/>`   `span`.
 
 So `is  this`
@@ -652,7 +744,8 @@ This ${code("is")} a ${code("code <br/>")} ${code("span")}.
 So ${code("is this")} span.
 
 A ${code("span with a line break")}.
-""")
+"""
+    )
 
 // Not supported by the parser yet
 // https://spec.commonmark.org/0.29/#backslash-escapes
@@ -665,7 +758,8 @@ A ${code("span with a line break")}.
 
     @Test
     @JsName("hard_line_breaks")
-    fun `hard line breaks`() = doTest("""
+    fun `hard line breaks`() = doTest(
+        """
 A hard   
 line break with spaces.
 
@@ -696,24 +790,28 @@ Also break on
 NEL and on
 LS
 .
-""")
+"""
+    )
 
     // https://github.github.com/gfm/#example-205
     @Test
     @JsName("header_only_table")
-    fun `header only table`() = doTest("""
+    fun `header only table`() = doTest(
+        """
 | abc | def |
 | --- | --- |
 """, """
 ┌─────┬─────┐
 │${bold(" abc ")}│${bold(" def ")}│
 ╘═════╧═════╛
-""")
+"""
+    )
 
     // https://github.github.com/gfm/#example-198
     @Test
     @JsName("simple_table")
-    fun `simple table`() = doTest("""
+    fun `simple table`() = doTest(
+        """
  | foo | bar |
  | --- | --- |
  | baz | bim |
@@ -723,12 +821,14 @@ LS
 ╞═════╪═════╡
 │ baz │ bim │
 └─────┴─────┘
-""")
+"""
+    )
 
     // https://github.github.com/gfm/#example-204
     @Test
     @JsName("no_rectangular_table")
-    fun `non-rectangular table`() = doTest("""
+    fun `non-rectangular table`() = doTest(
+        """
 | abc | def |
 | --- | --- |
 | bar |
@@ -741,11 +841,13 @@ LS
 ├─────┼─────┐
 │ bar │ baz │
 └─────┴─────┘
-""")
+"""
+    )
 
     @Test
     @JsName("table_alignment")
-    fun `table alignment`() = doTest("""
+    fun `table alignment`() = doTest(
+        """
 | abc | def | ghi |
 | :---: | ---: | :--- |
 | bar | baz | quz |
@@ -758,11 +860,13 @@ LS
 ├────────┼────────┼────────┤
 │ ...... │ ...... │ ...... │
 └────────┴────────┴────────┘
-""")
+"""
+    )
 
     @Test
     @JsName("table_with_empty_cell")
-    fun `table with empty cell`() = doTest("""
+    fun `table with empty cell`() = doTest(
+        """
  | foo |
  | --- |
  |     |
@@ -772,11 +876,13 @@ LS
 ╞═════╡
 │     │
 └─────┘
-""")
+"""
+    )
 
     @Test
     @JsName("table_with_empty_header_cell")
-    fun `table with empty header cell`() = doTest("""
+    fun `table with empty header cell`() = doTest(
+        """
  |     |
  | --- |
  | baz |
@@ -786,23 +892,27 @@ LS
 ╞═════╡
 │ baz │
 └─────┘
-""")
+"""
+    )
 
 
     // https://github.github.com/gfm/#example-279
     @Test
     @JsName("task_list")
-    fun `task list`() = doTest("""
+    fun `task list`() = doTest(
+        """
 - [ ] foo
 - [x] bar
 """, """
  • ☐ foo
  • ☑ bar
-""")
+"""
+    )
 
     @Test
     @JsName("indented_code_block")
-    fun `indented code block`() = doTest("""
+    fun `indented code block`() = doTest(
+        """
     foo {
         bar
         
@@ -820,11 +930,13 @@ LS
 │${codeBlock("    baz")}│
 │${codeBlock("}      ")}│
 ╰───────╯
-""")
+"""
+    )
 
     @Test
     @JsName("fenced_code_block")
-    fun `fenced code block`() = doTest("""
+    fun `fenced code block`() = doTest(
+        """
 ```
 foo {
     bar
@@ -844,12 +956,14 @@ foo {
 │${codeBlock("    baz")}│
 │${codeBlock("}      ")}│
 ╰───────╯
-""")
+"""
+    )
 
     // https://github.github.com/gfm/#example-113
     @Test
     @JsName("fenced_code_block_with_info_string")
-    fun `fenced code block with info string`() = doTest("""
+    fun `fenced code block with info string`() = doTest(
+        """
 ~~~~    ruby startline=3 ${'$'}%@#${'$'}
 def foo(x)
   return 3
@@ -861,13 +975,15 @@ end
 │${codeBlock("  return 3")}│
 │${codeBlock("end       ")}│
 ╰──────────╯
-""")
+"""
+    )
 
     @Test
     @JsName("fenced_code_block_with_nbsp")
     fun `fenced code block with nbsp`() {
         val nbsp = '\u00A0'
-        doTest("""
+        doTest(
+            """
 ```
 foo${nbsp}bar baz
 ```
@@ -875,7 +991,8 @@ foo${nbsp}bar baz
 ╭───────────╮
 │${codeBlock("foo${nbsp}bar baz")}│
 ╰───────────╯
-""")
+"""
+        )
     }
 
     @Test
@@ -902,7 +1019,8 @@ ${codeBlock("foo  bar")}
 
     @Test
     @JsName("plain_theme")
-    fun `plain theme`() = doTest("""
+    fun `plain theme`() = doTest(
+        """
 # H1
 A ***~~span~~*** and `some code`.
 
@@ -915,11 +1033,13 @@ A span and some
 code.
 
 link(c.com)
-""", theme = Theme.Plain, width = 15)
+""", theme = Theme.Plain, width = 15
+    )
 
     @Test
     @JsName("ascii_theme")
-    fun `ascii theme`() = doTest("""
+    fun `ascii theme`() = doTest(
+        """
 # H1
 ## H2
 - [ ] foo
@@ -943,7 +1063,8 @@ link(c.com)
 +===+===+
 | c | d |
 +---+---+
-""", theme = Theme.PlainAscii, width = 10)
+""", theme = Theme.PlainAscii, width = 10
+    )
 
     private fun doTest(
         markdown: String,
@@ -984,12 +1105,13 @@ private fun StringBuilder.printNode(text: String, node: ASTNode, indent: Int = 0
     append(" ".repeat(indent * 2))
     append(node.type)
     if (node is LeafASTNode) {
-        append(" \'").append(text.substring(node.startOffset, node.endOffset)
-            .replace("\r", "␍")
-            .replace("\n", "␊")
-            .replace(" ", "·")
-            .replace("\t", "␉")
-            .take(30)
+        append(" \'").append(
+            text.substring(node.startOffset, node.endOffset)
+                .replace("\r", "␍")
+                .replace("\n", "␊")
+                .replace(" ", "·")
+                .replace("\t", "␉")
+                .take(30)
         )
         if (node.endOffset - node.startOffset > 30) append("…")
         append("\'")