From e2a3dc17172ca15ec6876767183c3c8b5ca60c24 Mon Sep 17 00:00:00 2001 From: Oleg Baskakov Date: Wed, 20 Mar 2024 23:12:00 -0700 Subject: [PATCH] Add strikethrough markdown support Since it's inlined, the extension is added directly to the core module. --- gradle/libs.versions.toml | 1 + markdown/README.md | 2 +- markdown/core/build.gradle.kts | 1 + .../markdown/processing/MarkdownProcessor.kt | 2 + .../DefaultInlineMarkdownRenderer.kt | 14 +++++ .../markdown/rendering/MarkdownStyling.kt | 1 + ...rkdownProcessorDocumentParsingExtraTest.kt | 52 +++++++++++++++++++ .../markdown/styling/MarkdownIntUiStyling.kt | 4 ++ settings.gradle.kts | 2 + 9 files changed, 78 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 56b05fe54..1742767c2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,7 @@ poko = "0.13.1" [libraries] commonmark-core = { module = "org.commonmark:commonmark", version.ref = "commonmark" } +commonmark-extGfmStrikethrough = { module = "org.commonmark:commonmark-ext-gfm-strikethrough", version.ref = "commonmark" } filePicker = { module = "com.darkrockstudios:mpfilepicker", version = "3.1.0" } diff --git a/markdown/README.md b/markdown/README.md index 6879c093d..d6caa3536 100644 --- a/markdown/README.md +++ b/markdown/README.md @@ -11,13 +11,13 @@ Currently supports the [CommonMark 0.31.2](https://spec.commonmark.org/0.31.2/) Additional supported Markdown, via extensions: * Alerts ([GitHub Flavored Markdown][alerts-specs]) — see [`extension-gfm-alerts`](extension/gfm-alerts) +* Strikethrough(builtin) ([GitHub Flavored Markdown](https://github.github.com/gfm/#strikethrough-extension-)) [alerts-specs]: https://github.com/orgs/community/discussions/16925 On the roadmap, but not currently supported — in no particular order: * Tables ([GitHub Flavored Markdown](https://github.github.com/gfm/#tables-extension-)) -* Strikethrough ([GitHub Flavored Markdown](https://github.github.com/gfm/#strikethrough-extension-)) * Image loading (via [Coil 3](https://coil-kt.github.io/coil/upgrading_to_coil3/)) * Auto-linking ([GitHub Flavored Markdown](https://github.github.com/gfm/#autolinks-extension-)) * Task list items ([GitHub Flavored Markdown](https://github.github.com/gfm/#task-list-items-extension-)) diff --git a/markdown/core/build.gradle.kts b/markdown/core/build.gradle.kts index d784f1314..469f1a419 100644 --- a/markdown/core/build.gradle.kts +++ b/markdown/core/build.gradle.kts @@ -8,6 +8,7 @@ plugins { dependencies { api(projects.ui) api(libs.commonmark.core) + api(libs.commonmark.extGfmStrikethrough) testImplementation(compose.desktop.uiTestJUnit4) testImplementation(projects.ui) diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/processing/MarkdownProcessor.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/processing/MarkdownProcessor.kt index 626920173..95bc7cb08 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/processing/MarkdownProcessor.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/processing/MarkdownProcessor.kt @@ -1,5 +1,7 @@ package org.jetbrains.jewel.markdown.processing +import org.commonmark.ext.gfm.strikethrough.Strikethrough +import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension import org.commonmark.node.Block import org.commonmark.node.BlockQuote import org.commonmark.node.BulletList diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer.kt index 94b11a1d6..ef2e63dca 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer.kt @@ -7,6 +7,8 @@ import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.UrlAnnotation import androidx.compose.ui.text.buildAnnotatedString +import org.commonmark.ext.gfm.strikethrough.Strikethrough +import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension import org.commonmark.renderer.text.TextContentRenderer import org.jetbrains.jewel.foundation.ExperimentalJewelApi import org.jetbrains.jewel.markdown.InlineMarkdown @@ -17,8 +19,16 @@ public open class DefaultInlineMarkdownRenderer(rendererExtensions: List { + withStyles(styling.strikethrough, child) { appendInlineMarkdownFrom(it, styling) } + } + is InlineMarkdown.StrongEmphasis -> { withStyles(styling.strongEmphasis, child) { appendInlineMarkdownFrom(it.children, styling) } } diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownStyling.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownStyling.kt index 28b23d79d..6a5bf4cb5 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownStyling.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownStyling.kt @@ -292,6 +292,7 @@ public class InlinesStyling( public val link: SpanStyle, public val emphasis: SpanStyle, public val strongEmphasis: SpanStyle, + public val strikethrough: SpanStyle, public val inlineHtml: SpanStyle, public val renderInlineHtml: Boolean, ) { diff --git a/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/MarkdownProcessorDocumentParsingExtraTest.kt b/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/MarkdownProcessorDocumentParsingExtraTest.kt index 15bf4c97e..e0dc88c97 100644 --- a/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/MarkdownProcessorDocumentParsingExtraTest.kt +++ b/markdown/core/src/test/kotlin/org/jetbrains/jewel/markdown/MarkdownProcessorDocumentParsingExtraTest.kt @@ -72,4 +72,56 @@ class MarkdownProcessorDocumentParsingExtraTest { */ parsed.assertEquals(paragraph("*_*foo *bar* a*_*")) } + + @Test + fun `should parse gfm spec example 491 correctly (Strikethrough)`() { + val parsed = processor.processMarkdownDocument("~~Hi~~ Hello, ~~there~~ world!") + + /* + * Expected HTML: + *

Hi Hello, there world!

+ */ + parsed.assertEquals(paragraph("~~Hi~~ Hello, ~~there~~ world\\!")) + } + + @org.junit.Ignore("Strikethrough is any text wrapped in a pair of one tildes, but our implementation assumes two") + @Test + fun `should parse gfm spec example 491b correctly (Strikethrough)`() { + val parsed = processor.processMarkdownDocument("~there~ world!") + + parsed.assertEquals(paragraph("~there~ world\\!")) + } + + /** As with regular emphasis delimiters, a new paragraph will cause strikethrough parsing to cease */ + @Test + fun `should parse gfm spec example 492 correctly (Strikethrough)`() { + val parsed = processor.processMarkdownDocument( + """ + |This ~~has a + | + |new paragraph~~. + """.trimMargin(), + ) + + /* + * Expected HTML: + *

This ~~has a

+ *

new paragraph~~.

+ */ + parsed.assertEquals( + paragraph("This \\~\\~has a"), + paragraph("new paragraph\\~\\~."), + ) + } + + @Test + fun `should parse gfm spec example 493 correctly (Strikethrough)`() { + val parsed = processor.processMarkdownDocument("This will ~~~not~~~ strike.") + + /* + * Expected HTML: + *

This will ~~~not~~~ strike.

+ */ + parsed.assertEquals(paragraph("This will \\~\\~\\~not\\~\\~\\~ strike.")) + } } diff --git a/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/styling/MarkdownIntUiStyling.kt b/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/styling/MarkdownIntUiStyling.kt index 1988411dc..d5f136561 100644 --- a/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/styling/MarkdownIntUiStyling.kt +++ b/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/styling/MarkdownIntUiStyling.kt @@ -587,6 +587,7 @@ public fun InlinesStyling.Companion.light( textStyle.copy(color = Color(0xFF0969DA), textDecoration = TextDecoration.Underline).toSpanStyle(), emphasis: SpanStyle = textStyle.copy(fontStyle = FontStyle.Italic).toSpanStyle(), strongEmphasis: SpanStyle = textStyle.copy(fontWeight = FontWeight.Bold).toSpanStyle(), + strikethrough: SpanStyle = textStyle.copy(textDecoration = TextDecoration.LineThrough).toSpanStyle().copy(), inlineHtml: SpanStyle = textStyle.toSpanStyle(), renderInlineHtml: Boolean = false, ): InlinesStyling = @@ -596,6 +597,7 @@ public fun InlinesStyling.Companion.light( link, emphasis, strongEmphasis, + strikethrough, inlineHtml, renderInlineHtml, ) @@ -616,6 +618,7 @@ public fun InlinesStyling.Companion.dark( .toSpanStyle(), emphasis: SpanStyle = textStyle.copy(fontStyle = FontStyle.Italic).toSpanStyle(), strongEmphasis: SpanStyle = textStyle.copy(fontWeight = FontWeight.Bold).toSpanStyle(), + strikethrough: SpanStyle = textStyle.copy(textDecoration = TextDecoration.LineThrough).toSpanStyle().copy(), inlineHtml: SpanStyle = textStyle.toSpanStyle(), renderInlineHtml: Boolean = false, ): InlinesStyling = @@ -625,6 +628,7 @@ public fun InlinesStyling.Companion.dark( link, emphasis, strongEmphasis, + strikethrough, inlineHtml, renderInlineHtml, ) diff --git a/settings.gradle.kts b/settings.gradle.kts index 7d7e33b08..9bc4f928d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,3 +51,5 @@ gradleEnterprise { termsOfServiceAgree = "yes" } } +include("markdown:commonmark-extensions") +findProject(":markdown:commonmark-extensions")?.name = "commonmark-extensions"