diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 56b05fe540..5b013282bf 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-ext-autolink = { module = "org.commonmark:commonmark-ext-autolink", version.ref = "commonmark" } filePicker = { module = "com.darkrockstudios:mpfilepicker", version = "3.1.0" } diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension.kt index f484bf8832..039f5c8952 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension.kt @@ -9,25 +9,28 @@ import org.jetbrains.jewel.markdown.MarkdownBlock /** An extension for the Jewel Markdown processing engine. */ @ExperimentalJewelApi public interface MarkdownProcessorExtension { - /** * A CommonMark [ParserExtension] that will be used to parse the extended - * syntax represented by this extension instance. + * syntax represented by this extension instance. Null in the case where + * parsing is already handled by an existing [org.commonmark.parser.Parser]. */ - public val parserExtension: ParserExtension + public val parserExtension: ParserExtension? /** * A CommonMark [TextContentRendererExtension] that will be used to render * the text content of the CommonMark [CustomBlock] produced by the - * [parserExtension]. + * [parserExtension]. Null in the case where rendering is already + * handled by an existing [org.commonmark.renderer.Renderer]. */ - public val textRendererExtension: TextContentRendererExtension + public val textRendererExtension: TextContentRendererExtension? /** * An extension for * [`MarkdownParser`][org.jetbrains.jewel.markdown.parsing.MarkdownParser] * that will transform a supported [CustomBlock] into the corresponding - * [MarkdownBlock.CustomBlock]. + * [MarkdownBlock.CustomBlock]. Null in the case where processing + * is already be handled by [org.jetbrains.jewel.markdown.processing.MarkdownProcessor] + * or another [org.jetbrains.jewel.markdown.extensions.MarkdownProcessorExtension]. */ - public val processorExtension: MarkdownBlockProcessorExtension + public val processorExtension: MarkdownBlockProcessorExtension? } 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 626920173f..a30750bc43 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 @@ -189,7 +189,7 @@ public class MarkdownProcessor( is ThematicBreak -> MarkdownBlock.ThematicBreak is HtmlBlock -> toMarkdownHtmlBlockOrNull() is CustomBlock -> { - extensions.find { it.processorExtension.canProcess(this) } + extensions.find { it.processorExtension?.canProcess(this) == true } ?.processorExtension?.processMarkdownBlock(this, this@MarkdownProcessor) } else -> null diff --git a/markdown/extension/autolink/api/autolink.api b/markdown/extension/autolink/api/autolink.api new file mode 100644 index 0000000000..76809e0ee5 --- /dev/null +++ b/markdown/extension/autolink/api/autolink.api @@ -0,0 +1,8 @@ +public final class org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtension : org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension { + public static final field $stable I + public fun ()V + public fun getParserExtension ()Lorg/commonmark/parser/Parser$ParserExtension; + public fun getProcessorExtension ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownBlockProcessorExtension; + public fun getTextRendererExtension ()Lorg/commonmark/renderer/text/TextContentRenderer$TextContentRendererExtension; +} + diff --git a/markdown/extension/autolink/build.gradle.kts b/markdown/extension/autolink/build.gradle.kts new file mode 100644 index 0000000000..052dbcea83 --- /dev/null +++ b/markdown/extension/autolink/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + jewel + `jewel-publish` + `jewel-check-public-api` + alias(libs.plugins.composeDesktop) +} + +dependencies { + implementation(projects.markdown.core) + implementation(libs.commonmark.ext.autolink) + testImplementation(compose.desktop.uiTestJUnit4) +} + +publishing.publications.named("main") { + val ijpTarget = project.property("ijp.target") as String + artifactId = "jewel-markdown-extension-${project.name}-$ijpTarget" +} diff --git a/markdown/extension/autolink/src/main/kotlin/org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtension.kt b/markdown/extension/autolink/src/main/kotlin/org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtension.kt new file mode 100644 index 0000000000..ad8161deaf --- /dev/null +++ b/markdown/extension/autolink/src/main/kotlin/org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtension.kt @@ -0,0 +1,20 @@ +package org.jetbrains.jewel.markdown.extension.autolink + +import org.commonmark.ext.autolink.AutolinkExtension +import org.commonmark.parser.Parser.ParserExtension +import org.commonmark.renderer.text.TextContentRenderer +import org.jetbrains.jewel.markdown.extensions.MarkdownBlockProcessorExtension +import org.jetbrains.jewel.markdown.extensions.MarkdownProcessorExtension + +public object AutolinkProcessorExtension : MarkdownProcessorExtension { + override val parserExtension: ParserExtension + get() = AutolinkExtension.create() as ParserExtension + + /** + * Rendering and processing is already handled by [org.jetbrains.jewel.markdown.rendering.DefaultInlineMarkdownRenderer] + */ + override val textRendererExtension: TextContentRenderer.TextContentRendererExtension? + get() = null + override val processorExtension: MarkdownBlockProcessorExtension? + get() = null +} diff --git a/markdown/extension/autolink/src/test/kotlin/org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtensionTest.kt b/markdown/extension/autolink/src/test/kotlin/org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtensionTest.kt new file mode 100644 index 0000000000..8e14e7aa5f --- /dev/null +++ b/markdown/extension/autolink/src/test/kotlin/org/jetbrains/jewel/markdown/extension/autolink/AutolinkProcessorExtensionTest.kt @@ -0,0 +1,20 @@ +package org.jetbrains.jewel.markdown.extension.autolink + +import org.jetbrains.jewel.markdown.InlineMarkdown +import org.jetbrains.jewel.markdown.MarkdownBlock +import org.jetbrains.jewel.markdown.processing.MarkdownProcessor +import org.junit.Assert.assertTrue +import org.junit.Test + +class AutolinkProcessorExtensionTest { + // testing a simple case to assure wiring up our AutolinkProcessorExtension works correctly + @Test + fun `https text gets processed into a link`() { + val processor = MarkdownProcessor(listOf(AutolinkProcessorExtension)) + val rawMarkDown = "https://commonmark.org" + val processed = processor.processMarkdownDocument(rawMarkDown) + val paragraph = processed[0] as MarkdownBlock.Paragraph + + assertTrue(paragraph.inlineContent[0] is InlineMarkdown.Link) + } +} diff --git a/samples/standalone/build.gradle.kts b/samples/standalone/build.gradle.kts index 6cff7ddff0..2d8714780d 100644 --- a/samples/standalone/build.gradle.kts +++ b/samples/standalone/build.gradle.kts @@ -14,7 +14,7 @@ dependencies { implementation(projects.intUi.intUiDecoratedWindow) implementation(projects.markdown.intUiStandaloneStyling) implementation(projects.markdown.extension.gfmAlerts) - + implementation(projects.markdown.extension.autolink) implementation(compose.desktop.currentOs) { exclude(group = "org.jetbrains.compose.material") } diff --git a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt index 8baa7ed879..f3f6aba6e2 100644 --- a/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt +++ b/samples/standalone/src/main/kotlin/org/jetbrains/jewel/samples/standalone/view/markdown/MarkdownPreview.kt @@ -31,6 +31,7 @@ import org.jetbrains.jewel.intui.markdown.styling.extension.github.alerts.dark import org.jetbrains.jewel.intui.markdown.styling.extension.github.alerts.light import org.jetbrains.jewel.intui.markdown.styling.light import org.jetbrains.jewel.markdown.MarkdownBlock +import org.jetbrains.jewel.markdown.extension.autolink.AutolinkProcessorExtension import org.jetbrains.jewel.markdown.extensions.github.alerts.AlertStyling import org.jetbrains.jewel.markdown.extensions.github.alerts.GitHubAlertProcessorExtension import org.jetbrains.jewel.markdown.extensions.github.alerts.GitHubAlertRendererExtension @@ -43,14 +44,17 @@ import java.awt.Desktop import java.net.URI @Composable -internal fun MarkdownPreview(rawMarkdown: String, modifier: Modifier = Modifier) { +internal fun MarkdownPreview( + rawMarkdown: String, + modifier: Modifier = Modifier, +) { val isDark = JewelTheme.isDark val markdownStyling = remember(isDark) { if (isDark) MarkdownStyling.dark() else MarkdownStyling.light() } var markdownBlocks by remember { mutableStateOf(emptyList()) } - val extensions = listOf(GitHubAlertProcessorExtension) + val extensions = listOf(GitHubAlertProcessorExtension, AutolinkProcessorExtension) val processor = remember { MarkdownProcessor(extensions) } LaunchedEffect(rawMarkdown) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 7d7e33b089..69a2b6a3e4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,6 +36,7 @@ include( ":int-ui:int-ui-decorated-window", ":int-ui:int-ui-standalone", ":markdown:core", + ":markdown:extension:autolink", ":markdown:extension:gfm-alerts", ":markdown:int-ui-standalone-styling", ":markdown:ide-laf-bridge-styling",