diff --git a/markdown/core/api/core.api b/markdown/core/api/core.api index 82e6953f1..2bb6fb2a7 100644 --- a/markdown/core/api/core.api +++ b/markdown/core/api/core.api @@ -408,7 +408,19 @@ public abstract interface class org/jetbrains/jewel/markdown/extensions/Markdown public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension { public abstract fun canRender (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;)Z - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V +} + +public final class org/jetbrains/jewel/markdown/extensions/MarkdownKt { + public static final fun LazyMarkdown (Ljava/util/List;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Landroidx/compose/foundation/lazy/LazyListState;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Landroidx/compose/runtime/Composer;II)V + public static final fun Markdown (Ljava/lang/String;Landroidx/compose/ui/Modifier;ZLkotlinx/coroutines/CoroutineDispatcher;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Landroidx/compose/runtime/Composer;II)V + public static final fun Markdown (Ljava/util/List;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Landroidx/compose/runtime/Composer;II)V + public static final fun getLocalMarkdownBlockRenderer ()Landroidx/compose/runtime/ProvidableCompositionLocal; + public static final fun getLocalMarkdownProcessor ()Landroidx/compose/runtime/ProvidableCompositionLocal; + public static final fun getLocalMarkdownStyling ()Landroidx/compose/runtime/ProvidableCompositionLocal; + public static final fun getMarkdownBlockRenderer (Lorg/jetbrains/jewel/foundation/theme/JewelTheme$Companion;Landroidx/compose/runtime/Composer;I)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; + public static final fun getMarkdownProcessor (Lorg/jetbrains/jewel/foundation/theme/JewelTheme$Companion;Landroidx/compose/runtime/Composer;I)Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor; + public static final fun getMarkdownStyling (Lorg/jetbrains/jewel/foundation/theme/JewelTheme$Companion;Landroidx/compose/runtime/Composer;I)Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling; } public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension { @@ -444,21 +456,21 @@ public final class org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownR public class org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer : org/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer { public static final field $stable I - public fun (Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)V - public fun render (Ljava/util/List;Landroidx/compose/runtime/Composer;I)V - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$BlockQuote;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$BlockQuote;Landroidx/compose/runtime/Composer;I)V + public fun (Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;)V + public fun render (Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$BlockQuote;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$BlockQuote;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CodeBlock$FencedCodeBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code$Fenced;Landroidx/compose/runtime/Composer;I)V public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CodeBlock$IndentedCodeBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code$Indented;Landroidx/compose/runtime/Composer;I)V public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CodeBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code;Landroidx/compose/runtime/Composer;I)V public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$HtmlBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$HtmlBlock;Landroidx/compose/runtime/Composer;I)V - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$OrderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Ordered;Landroidx/compose/runtime/Composer;I)V - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$UnorderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Unordered;Landroidx/compose/runtime/Composer;I)V - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List;Landroidx/compose/runtime/Composer;I)V - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListItem;Landroidx/compose/runtime/Composer;I)V - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock;Landroidx/compose/runtime/Composer;I)V - public fun render-mpq8F88 (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading$HN;Landroidx/compose/runtime/Composer;I)V - public fun render-mpq8F88 (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading;Landroidx/compose/runtime/Composer;I)V - public fun render-wlAWB_Q (Lorg/commonmark/node/Paragraph;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Paragraph;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$OrderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Ordered;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$UnorderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Unordered;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListItem;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render-BLSRvKs (Lorg/commonmark/node/Paragraph;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Paragraph;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render-kPCWZlk (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading$HN;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public fun render-kPCWZlk (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V public fun renderThematicBreak (Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$ThematicBreak;Landroidx/compose/runtime/Composer;I)V } @@ -493,20 +505,20 @@ public final class org/jetbrains/jewel/markdown/rendering/InlinesStyling$Compani public abstract interface class org/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer { public static final field Companion Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion; - public abstract fun render (Ljava/util/List;Landroidx/compose/runtime/Composer;I)V - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$BlockQuote;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$BlockQuote;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$BlockQuote;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$BlockQuote;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CodeBlock$FencedCodeBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code$Fenced;Landroidx/compose/runtime/Composer;I)V public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CodeBlock$IndentedCodeBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code$Indented;Landroidx/compose/runtime/Composer;I)V public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CodeBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code;Landroidx/compose/runtime/Composer;I)V public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$HtmlBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$HtmlBlock;Landroidx/compose/runtime/Composer;I)V - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$OrderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Ordered;Landroidx/compose/runtime/Composer;I)V - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$UnorderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Unordered;Landroidx/compose/runtime/Composer;I)V - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List;Landroidx/compose/runtime/Composer;I)V - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListItem;Landroidx/compose/runtime/Composer;I)V - public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock;Landroidx/compose/runtime/Composer;I)V - public abstract fun render-mpq8F88 (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading$HN;Landroidx/compose/runtime/Composer;I)V - public abstract fun render-mpq8F88 (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading;Landroidx/compose/runtime/Composer;I)V - public abstract fun render-wlAWB_Q (Lorg/commonmark/node/Paragraph;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Paragraph;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$OrderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Ordered;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock$UnorderedList;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List$Unordered;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$ListItem;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render-BLSRvKs (Lorg/commonmark/node/Paragraph;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Paragraph;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render-kPCWZlk (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading$HN;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V + public abstract fun render-kPCWZlk (Lorg/commonmark/node/Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V public abstract fun renderThematicBreak (Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$ThematicBreak;Landroidx/compose/runtime/Composer;I)V } diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/Markdown.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/Markdown.kt new file mode 100644 index 000000000..47ab5b8c6 --- /dev/null +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/Markdown.kt @@ -0,0 +1,151 @@ +package org.jetbrains.jewel.markdown.extensions + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.intellij.lang.annotations.Language +import org.jetbrains.jewel.foundation.ExperimentalJewelApi +import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.markdown.MarkdownBlock +import org.jetbrains.jewel.markdown.processing.MarkdownProcessor +import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer +import org.jetbrains.jewel.markdown.rendering.MarkdownStyling + +public val LocalMarkdownStyling: ProvidableCompositionLocal = staticCompositionLocalOf { + error("No MarkdownStyling defined, have you forgotten to provide it?") +} + +public val JewelTheme.Companion.markdownStyling: MarkdownStyling + @Composable get() = LocalMarkdownStyling.current + +public val LocalMarkdownProcessor: ProvidableCompositionLocal = staticCompositionLocalOf { + error("No MarkdownProcessor defined, have you forgotten to provide it?") +} + +public val JewelTheme.Companion.markdownProcessor: MarkdownProcessor + @Composable get() = LocalMarkdownProcessor.current + +public val LocalMarkdownBlockRenderer: ProvidableCompositionLocal = staticCompositionLocalOf { + error("No MarkdownBlockRenderer defined, have you forgotten to provide it?") +} + +public val JewelTheme.Companion.markdownBlockRenderer: MarkdownBlockRenderer + @Composable get() = LocalMarkdownBlockRenderer.current + +@ExperimentalJewelApi +@Composable +public fun Markdown( + @Language("Markdown") markdown: String, + modifier: Modifier = Modifier, + isSelectable: Boolean = false, + renderingDispatcher: CoroutineDispatcher = Dispatchers.Default, + onUrlClick: (String) -> Unit = {}, + onTextClick: () -> Unit = {}, + markdownStyling: MarkdownStyling = JewelTheme.markdownStyling, + processor: MarkdownProcessor = JewelTheme.markdownProcessor, + blockRenderer: MarkdownBlockRenderer = JewelTheme.markdownBlockRenderer, +) { + var markdownBlocks by remember { mutableStateOf(emptyList()) } + LaunchedEffect(markdown) { + markdownBlocks = + withContext(renderingDispatcher) { processor.processMarkdownDocument(markdown) } + } + + Markdown( + markdownBlocks = markdownBlocks, + modifier = modifier, + isSelectable = isSelectable, + onUrlClick = onUrlClick, + onTextClick = onTextClick, + markdownStyling = markdownStyling, + blockRenderer = blockRenderer, + ) +} + +@ExperimentalJewelApi +@Composable +public fun Markdown( + markdownBlocks: List, + modifier: Modifier = Modifier, + isSelectable: Boolean = false, + onUrlClick: (String) -> Unit = {}, + onTextClick: () -> Unit = {}, + markdownStyling: MarkdownStyling = JewelTheme.markdownStyling, + blockRenderer: MarkdownBlockRenderer = JewelTheme.markdownBlockRenderer, +) { + if (isSelectable) { + SelectionContainer(modifier) { + Column(verticalArrangement = Arrangement.spacedBy(markdownStyling.blockVerticalSpacing)) { + for (block in markdownBlocks) { + blockRenderer.render(block, onUrlClick, onTextClick) + } + } + } + } else { + Column( + modifier, + verticalArrangement = Arrangement.spacedBy(markdownStyling.blockVerticalSpacing), + ) { + for (block in markdownBlocks) { + blockRenderer.render(block, onUrlClick, onTextClick) + } + } + } +} + +@ExperimentalJewelApi +@Composable +public fun LazyMarkdown( + markdownBlocks: List, + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(0.dp), + state: LazyListState = rememberLazyListState(), + isSelectable: Boolean = false, + onUrlClick: (String) -> Unit = {}, + onTextClick: () -> Unit = {}, + markdownStyling: MarkdownStyling = JewelTheme.markdownStyling, + blockRenderer: MarkdownBlockRenderer = JewelTheme.markdownBlockRenderer, +) { + if (isSelectable) { + SelectionContainer(modifier) { + LazyColumn( + state = state, + contentPadding = contentPadding, + verticalArrangement = Arrangement.spacedBy(markdownStyling.blockVerticalSpacing), + ) { + items(markdownBlocks) { block -> + blockRenderer.render(block, onUrlClick, onTextClick) + } + } + } + } else { + LazyColumn( + modifier = modifier, + state = state, + contentPadding = contentPadding, + verticalArrangement = Arrangement.spacedBy(markdownStyling.blockVerticalSpacing), + ) { + items(markdownBlocks) { block -> + blockRenderer.render(block, onUrlClick, onTextClick) + } + } + } +} diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension.kt index ea5cb42a2..5c29cb41b 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension.kt @@ -24,5 +24,7 @@ public interface MarkdownBlockRendererExtension { block: CustomBlock, blockRenderer: MarkdownBlockRenderer, inlineRenderer: InlineMarkdownRenderer, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, ) } diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt index 96b3853ef..e74168252 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/DefaultMarkdownBlockRenderer.kt @@ -70,71 +70,92 @@ import org.jetbrains.jewel.ui.Orientation.Horizontal import org.jetbrains.jewel.ui.component.Divider import org.jetbrains.jewel.ui.component.Text -@Suppress("FunctionName") @ExperimentalJewelApi public open class DefaultMarkdownBlockRenderer( private val rootStyling: MarkdownStyling, private val rendererExtensions: List, private val inlineRenderer: InlineMarkdownRenderer, - private val onUrlClick: (String) -> Unit, - private val onTextClick: () -> Unit, ) : MarkdownBlockRenderer { @Composable - override fun render(blocks: List) { + override fun render( + blocks: List, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { Column(verticalArrangement = Arrangement.spacedBy(rootStyling.blockVerticalSpacing)) { for (block in blocks) { - render(block) + render(block, onUrlClick, onTextClick) } } } @Composable - override fun render(block: MarkdownBlock) { + override fun render( + block: MarkdownBlock, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { when (block) { - is BlockQuote -> render(block, rootStyling.blockQuote) + is BlockQuote -> render(block, rootStyling.blockQuote, onUrlClick, onTextClick) is FencedCodeBlock -> render(block, rootStyling.code.fenced) is IndentedCodeBlock -> render(block, rootStyling.code.indented) - is Heading -> render(block, rootStyling.heading) + is Heading -> render(block, rootStyling.heading, onUrlClick, onTextClick) is HtmlBlock -> render(block, rootStyling.htmlBlock) - is OrderedList -> render(block, rootStyling.list.ordered) - is UnorderedList -> render(block, rootStyling.list.unordered) - is ListItem -> render(block) - is Paragraph -> render(block, rootStyling.paragraph) + is OrderedList -> render(block, rootStyling.list.ordered, onUrlClick, onTextClick) + is UnorderedList -> render(block, rootStyling.list.unordered, onUrlClick, onTextClick) + is ListItem -> render(block, onUrlClick, onTextClick) + is Paragraph -> render(block, rootStyling.paragraph, onUrlClick, onTextClick) ThematicBreak -> renderThematicBreak(rootStyling.thematicBreak) is CustomBlock -> { rendererExtensions.find { it.blockRenderer.canRender(block) } - ?.blockRenderer?.render(block, this, inlineRenderer) + ?.blockRenderer?.render(block, this, inlineRenderer, onUrlClick, onTextClick) } } } @Composable - override fun render(block: Paragraph, styling: MarkdownStyling.Paragraph) { + override fun render( + block: Paragraph, + styling: MarkdownStyling.Paragraph, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { val renderedContent = rememberRenderedContent(block, styling.inlinesStyling) SimpleClickableText( text = renderedContent, textStyle = styling.inlinesStyling.textStyle, color = styling.inlinesStyling.textStyle.color.takeOrElse { LocalContentColor.current }, onUnhandledClick = onTextClick, + onUrlClick = onUrlClick, ) } @Composable - override fun render(block: Heading, styling: MarkdownStyling.Heading) { + override fun render( + block: Heading, + styling: MarkdownStyling.Heading, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { when (block.level) { - 1 -> render(block, styling.h1) - 2 -> render(block, styling.h2) - 3 -> render(block, styling.h3) - 4 -> render(block, styling.h4) - 5 -> render(block, styling.h5) - 6 -> render(block, styling.h6) + 1 -> render(block, styling.h1, onUrlClick, onTextClick) + 2 -> render(block, styling.h2, onUrlClick, onTextClick) + 3 -> render(block, styling.h3, onUrlClick, onTextClick) + 4 -> render(block, styling.h4, onUrlClick, onTextClick) + 5 -> render(block, styling.h5, onUrlClick, onTextClick) + 6 -> render(block, styling.h6, onUrlClick, onTextClick) else -> error("Heading level ${block.level} not supported:\n$block") } } @Composable - override fun render(block: Heading, styling: MarkdownStyling.Heading.HN) { + override fun render( + block: Heading, + styling: MarkdownStyling.Heading.HN, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { val renderedContent = rememberRenderedContent(block, styling.inlinesStyling) Heading( renderedContent, @@ -143,6 +164,8 @@ public open class DefaultMarkdownBlockRenderer( styling.underlineWidth, styling.underlineColor, styling.underlineGap, + onUrlClick, + onTextClick, ) } @@ -154,6 +177,8 @@ public open class DefaultMarkdownBlockRenderer( underlineWidth: Dp, underlineColor: Color, underlineGap: Dp, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, ) { Column(modifier = Modifier.padding(paddingValues)) { SimpleClickableText( @@ -161,6 +186,7 @@ public open class DefaultMarkdownBlockRenderer( textStyle = textStyle, color = textStyle.color.takeOrElse { LocalContentColor.current }, onUnhandledClick = onTextClick, + onUrlClick = onUrlClick, ) if (underlineWidth > 0.dp && underlineColor.isSpecified) { @@ -174,6 +200,8 @@ public open class DefaultMarkdownBlockRenderer( override fun render( block: BlockQuote, styling: MarkdownStyling.BlockQuote, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, ) { Column( Modifier.drawBehind { @@ -194,21 +222,31 @@ public open class DefaultMarkdownBlockRenderer( verticalArrangement = Arrangement.spacedBy(rootStyling.blockVerticalSpacing), ) { CompositionLocalProvider(LocalContentColor provides styling.textColor) { - render(block.children) + render(block.children, onUrlClick, onTextClick) } } } @Composable - override fun render(block: ListBlock, styling: MarkdownStyling.List) { + override fun render( + block: ListBlock, + styling: MarkdownStyling.List, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { when (block) { - is OrderedList -> render(block, styling.ordered) - is UnorderedList -> render(block, styling.unordered) + is OrderedList -> render(block, styling.ordered, onUrlClick, onTextClick) + is UnorderedList -> render(block, styling.unordered, onUrlClick, onTextClick) } } @Composable - override fun render(block: OrderedList, styling: MarkdownStyling.List.Ordered) { + override fun render( + block: OrderedList, + styling: MarkdownStyling.List.Ordered, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { val itemSpacing = if (block.isTight) { styling.itemVerticalSpacingTight @@ -234,14 +272,19 @@ public open class DefaultMarkdownBlockRenderer( Spacer(Modifier.width(styling.numberContentGap)) - render(item) + render(item, onUrlClick, onTextClick) } } } } @Composable - override fun render(block: UnorderedList, styling: MarkdownStyling.List.Unordered) { + override fun render( + block: UnorderedList, + styling: MarkdownStyling.List.Unordered, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { val itemSpacing = if (block.isTight) { styling.itemVerticalSpacingTight @@ -264,16 +307,20 @@ public open class DefaultMarkdownBlockRenderer( Spacer(Modifier.width(styling.bulletContentGap)) - render(item) + render(item, onUrlClick, onTextClick) } } } } @Composable - override fun render(block: ListItem) { + override fun render( + block: ListItem, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { Column(verticalArrangement = Arrangement.spacedBy(rootStyling.blockVerticalSpacing)) { - render(block.children) + render(block.children, onUrlClick, onTextClick) } } @@ -383,6 +430,7 @@ public open class DefaultMarkdownBlockRenderer( textStyle: TextStyle, modifier: Modifier = Modifier, color: Color = Color.Unspecified, + onUrlClick: (String) -> Unit, onUnhandledClick: () -> Unit, ) { var pointerIcon by remember { mutableStateOf(PointerIcon.Default) } diff --git a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer.kt b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer.kt index eeced2209..0d0cc22a7 100644 --- a/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer.kt +++ b/markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer.kt @@ -12,39 +12,87 @@ import org.jetbrains.jewel.markdown.MarkdownBlock.ListBlock import org.jetbrains.jewel.markdown.MarkdownBlock.ListBlock.OrderedList import org.jetbrains.jewel.markdown.MarkdownBlock.ListBlock.UnorderedList import org.jetbrains.jewel.markdown.MarkdownBlock.ListItem +import org.jetbrains.jewel.markdown.MarkdownBlock.Paragraph @ExperimentalJewelApi public interface MarkdownBlockRenderer { @Composable - public fun render(blocks: List) + public fun render( + blocks: List, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: MarkdownBlock) + public fun render( + block: MarkdownBlock, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: MarkdownBlock.Paragraph, styling: MarkdownStyling.Paragraph) + public fun render( + block: Paragraph, + styling: MarkdownStyling.Paragraph, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: MarkdownBlock.Heading, styling: MarkdownStyling.Heading) + public fun render( + block: MarkdownBlock.Heading, + styling: MarkdownStyling.Heading, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: MarkdownBlock.Heading, styling: MarkdownStyling.Heading.HN) + public fun render( + block: MarkdownBlock.Heading, + styling: MarkdownStyling.Heading.HN, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: BlockQuote, styling: MarkdownStyling.BlockQuote) + public fun render( + block: BlockQuote, + styling: MarkdownStyling.BlockQuote, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: ListBlock, styling: MarkdownStyling.List) + public fun render( + block: ListBlock, + styling: MarkdownStyling.List, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: OrderedList, styling: MarkdownStyling.List.Ordered) + public fun render( + block: OrderedList, + styling: MarkdownStyling.List.Ordered, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: UnorderedList, styling: MarkdownStyling.List.Unordered) + public fun render( + block: UnorderedList, + styling: MarkdownStyling.List.Unordered, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable - public fun render(block: ListItem) + public fun render( + block: ListItem, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) @Composable public fun render(block: CodeBlock, styling: MarkdownStyling.Code) diff --git a/markdown/extension/gfm-alerts/api/gfm-alerts.api b/markdown/extension/gfm-alerts/api/gfm-alerts.api index c292a1850..f193ce1ee 100644 --- a/markdown/extension/gfm-alerts/api/gfm-alerts.api +++ b/markdown/extension/gfm-alerts/api/gfm-alerts.api @@ -116,7 +116,7 @@ public final class org/jetbrains/jewel/markdown/extensions/github/alerts/GitHubA public static final field $stable I public fun (Lorg/jetbrains/jewel/markdown/extensions/github/alerts/AlertStyling;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;)V public fun canRender (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;)Z - public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Landroidx/compose/runtime/Composer;I)V + public fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V } public final class org/jetbrains/jewel/markdown/extensions/github/alerts/GitHubAlertProcessorExtension : org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension { diff --git a/markdown/extension/gfm-alerts/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/github/alerts/GitHubAlertBlockRenderer.kt b/markdown/extension/gfm-alerts/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/github/alerts/GitHubAlertBlockRenderer.kt index 5ffdddb5b..46b93771b 100644 --- a/markdown/extension/gfm-alerts/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/github/alerts/GitHubAlertBlockRenderer.kt +++ b/markdown/extension/gfm-alerts/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/github/alerts/GitHubAlertBlockRenderer.kt @@ -45,6 +45,8 @@ public class GitHubAlertBlockRenderer( block: CustomBlock, blockRenderer: MarkdownBlockRenderer, inlineRenderer: InlineMarkdownRenderer, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, ) { // Smart cast doesn't work in this case, and then the detection for redundant suppression is // also borked @@ -52,17 +54,23 @@ public class GitHubAlertBlockRenderer( val alert = block as? Alert when (alert) { - is Caution -> Alert(alert, styling.caution, blockRenderer) - is Important -> Alert(alert, styling.important, blockRenderer) - is Note -> Alert(alert, styling.note, blockRenderer) - is Tip -> Alert(alert, styling.tip, blockRenderer) - is Warning -> Alert(alert, styling.warning, blockRenderer) + is Caution -> Alert(alert, styling.caution, blockRenderer, onUrlClick, onTextClick) + is Important -> Alert(alert, styling.important, blockRenderer, onUrlClick, onTextClick) + is Note -> Alert(alert, styling.note, blockRenderer, onUrlClick, onTextClick) + is Tip -> Alert(alert, styling.tip, blockRenderer, onUrlClick, onTextClick) + is Warning -> Alert(alert, styling.warning, blockRenderer, onUrlClick, onTextClick) else -> error("Unsupported block of type ${block.javaClass.name} cannot be rendered") } } @Composable - private fun Alert(block: Alert, styling: BaseAlertStyling, blockRenderer: MarkdownBlockRenderer) { + private fun Alert( + block: Alert, + styling: BaseAlertStyling, + blockRenderer: MarkdownBlockRenderer, + onUrlClick: (String) -> Unit, + onTextClick: () -> Unit, + ) { Column( Modifier.drawBehind { val isLtr = layoutDirection == Ltr @@ -118,7 +126,7 @@ public class GitHubAlertBlockRenderer( CompositionLocalProvider( LocalContentColor provides styling.textColor.takeOrElse { LocalContentColor.current }, ) { - blockRenderer.render(block.content) + blockRenderer.render(block.content, onUrlClick, onTextClick) } } } diff --git a/markdown/ide-laf-bridge-styling/api/ide-laf-bridge-styling.api b/markdown/ide-laf-bridge-styling/api/ide-laf-bridge-styling.api index 4deef211c..e1e35d830 100644 --- a/markdown/ide-laf-bridge-styling/api/ide-laf-bridge-styling.api +++ b/markdown/ide-laf-bridge-styling/api/ide-laf-bridge-styling.api @@ -1,6 +1,7 @@ -public final class org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensionsKt { - public static final fun create (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; - public static synthetic fun create$default (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; +public final class org/jetbrains/jewel/intui/markdown/BridgeMarkdownBlockRendererExtensionsKt { + public static final fun ProvideMarkdownStyling (Ljava/lang/String;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun create (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; + public static synthetic fun create$default (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;ILjava/lang/Object;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; } public final class org/jetbrains/jewel/intui/markdown/styling/MarkdownBridgeStylingKt { diff --git a/markdown/ide-laf-bridge-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/BridgeMarkdownBlockRendererExtensions.kt b/markdown/ide-laf-bridge-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/BridgeMarkdownBlockRendererExtensions.kt new file mode 100644 index 000000000..136462444 --- /dev/null +++ b/markdown/ide-laf-bridge-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/BridgeMarkdownBlockRendererExtensions.kt @@ -0,0 +1,45 @@ +package org.jetbrains.jewel.intui.markdown + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import org.jetbrains.jewel.foundation.ExperimentalJewelApi +import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.intui.markdown.styling.create +import org.jetbrains.jewel.markdown.extensions.LocalMarkdownBlockRenderer +import org.jetbrains.jewel.markdown.extensions.LocalMarkdownProcessor +import org.jetbrains.jewel.markdown.extensions.LocalMarkdownStyling +import org.jetbrains.jewel.markdown.extensions.MarkdownRendererExtension +import org.jetbrains.jewel.markdown.processing.MarkdownProcessor +import org.jetbrains.jewel.markdown.rendering.DefaultMarkdownBlockRenderer +import org.jetbrains.jewel.markdown.rendering.InlineMarkdownRenderer +import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer +import org.jetbrains.jewel.markdown.rendering.MarkdownStyling + +public fun MarkdownBlockRenderer.Companion.create( + styling: MarkdownStyling = MarkdownStyling.create(), + rendererExtensions: List = emptyList(), + inlineRenderer: InlineMarkdownRenderer = InlineMarkdownRenderer.default(), +): MarkdownBlockRenderer = DefaultMarkdownBlockRenderer(styling, rendererExtensions, inlineRenderer) + +@ExperimentalJewelApi +@Composable +public fun ProvideMarkdownStyling( + themeName: String = JewelTheme.name, + markdownStyling: MarkdownStyling = remember(themeName) { MarkdownStyling.create() }, + markdownProcessor: MarkdownProcessor = remember { MarkdownProcessor() }, + markdownBlockRenderer: MarkdownBlockRenderer = remember(markdownStyling) { + MarkdownBlockRenderer.create( + markdownStyling, + ) + }, + content: @Composable () -> Unit, +) { + CompositionLocalProvider( + LocalMarkdownStyling provides markdownStyling, + LocalMarkdownProcessor provides markdownProcessor, + LocalMarkdownBlockRenderer provides markdownBlockRenderer, + ) { + content() + } +} diff --git a/markdown/ide-laf-bridge-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensions.kt b/markdown/ide-laf-bridge-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensions.kt deleted file mode 100644 index d3e7670f2..000000000 --- a/markdown/ide-laf-bridge-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensions.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.jetbrains.jewel.intui.markdown - -import org.jetbrains.jewel.intui.markdown.styling.create -import org.jetbrains.jewel.markdown.extensions.MarkdownRendererExtension -import org.jetbrains.jewel.markdown.rendering.DefaultMarkdownBlockRenderer -import org.jetbrains.jewel.markdown.rendering.InlineMarkdownRenderer -import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer -import org.jetbrains.jewel.markdown.rendering.MarkdownStyling - -public fun MarkdownBlockRenderer.Companion.create( - styling: MarkdownStyling = MarkdownStyling.create(), - rendererExtensions: List = emptyList(), - inlineRenderer: InlineMarkdownRenderer = InlineMarkdownRenderer.default(), - onUrlClick: (String) -> Unit = {}, - onTextClick: () -> Unit = {}, -): MarkdownBlockRenderer = - DefaultMarkdownBlockRenderer(styling, rendererExtensions, inlineRenderer, onUrlClick, onTextClick) diff --git a/markdown/int-ui-standalone-styling/api/int-ui-standalone-styling.api b/markdown/int-ui-standalone-styling/api/int-ui-standalone-styling.api index 6af0bf3c1..67ff71480 100644 --- a/markdown/int-ui-standalone-styling/api/int-ui-standalone-styling.api +++ b/markdown/int-ui-standalone-styling/api/int-ui-standalone-styling.api @@ -1,8 +1,10 @@ -public final class org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensionsKt { - public static final fun dark (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; - public static synthetic fun dark$default (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; - public static final fun light (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; - public static synthetic fun light$default (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; +public final class org/jetbrains/jewel/intui/markdown/IntUiMarkdownBlockRendererExtensionsKt { + public static final fun ProvideMarkdownStyling (Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun ProvideMarkdownStyling (ZLorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V + public static final fun dark (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; + public static synthetic fun dark$default (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;ILjava/lang/Object;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; + public static final fun light (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; + public static synthetic fun light$default (Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer$Companion;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling;Ljava/util/List;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;ILjava/lang/Object;)Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer; } public final class org/jetbrains/jewel/intui/markdown/styling/MarkdownIntUiStylingKt { diff --git a/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/IntUiMarkdownBlockRendererExtensions.kt b/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/IntUiMarkdownBlockRendererExtensions.kt new file mode 100644 index 000000000..a6590aeaf --- /dev/null +++ b/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/IntUiMarkdownBlockRendererExtensions.kt @@ -0,0 +1,79 @@ +package org.jetbrains.jewel.intui.markdown + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import org.jetbrains.jewel.foundation.ExperimentalJewelApi +import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.intui.markdown.styling.dark +import org.jetbrains.jewel.intui.markdown.styling.light +import org.jetbrains.jewel.markdown.extensions.LocalMarkdownBlockRenderer +import org.jetbrains.jewel.markdown.extensions.LocalMarkdownProcessor +import org.jetbrains.jewel.markdown.extensions.LocalMarkdownStyling +import org.jetbrains.jewel.markdown.extensions.MarkdownRendererExtension +import org.jetbrains.jewel.markdown.processing.MarkdownProcessor +import org.jetbrains.jewel.markdown.rendering.DefaultMarkdownBlockRenderer +import org.jetbrains.jewel.markdown.rendering.InlineMarkdownRenderer +import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer +import org.jetbrains.jewel.markdown.rendering.MarkdownStyling + +public fun MarkdownBlockRenderer.Companion.light( + styling: MarkdownStyling = MarkdownStyling.light(), + rendererExtensions: List = emptyList(), + inlineRenderer: InlineMarkdownRenderer = InlineMarkdownRenderer.default(), +): MarkdownBlockRenderer = + DefaultMarkdownBlockRenderer(styling, rendererExtensions, inlineRenderer) + +public fun MarkdownBlockRenderer.Companion.dark( + styling: MarkdownStyling = MarkdownStyling.dark(), + rendererExtensions: List = emptyList(), + inlineRenderer: InlineMarkdownRenderer = InlineMarkdownRenderer.default(), +): MarkdownBlockRenderer = + DefaultMarkdownBlockRenderer(styling, rendererExtensions, inlineRenderer) + +@ExperimentalJewelApi +@Composable +public fun ProvideMarkdownStyling( + isDark: Boolean = JewelTheme.isDark, + markdownStyling: MarkdownStyling = remember(isDark) { + if (isDark) { + MarkdownStyling.dark() + } else { + MarkdownStyling.light() + } + }, + markdownProcessor: MarkdownProcessor = remember { MarkdownProcessor() }, + markdownBlockRenderer: MarkdownBlockRenderer = remember(markdownStyling) { + if (isDark) { + MarkdownBlockRenderer.dark(markdownStyling) + } else { + MarkdownBlockRenderer.light(markdownStyling) + } + }, + content: @Composable () -> Unit, +) { + CompositionLocalProvider( + LocalMarkdownStyling provides markdownStyling, + LocalMarkdownProcessor provides markdownProcessor, + LocalMarkdownBlockRenderer provides markdownBlockRenderer, + ) { + content() + } +} + +@ExperimentalJewelApi +@Composable +public fun ProvideMarkdownStyling( + markdownStyling: MarkdownStyling, + markdownBlockRenderer: MarkdownBlockRenderer, + markdownProcessor: MarkdownProcessor = remember { MarkdownProcessor() }, + content: @Composable () -> Unit, +) { + CompositionLocalProvider( + LocalMarkdownStyling provides markdownStyling, + LocalMarkdownProcessor provides markdownProcessor, + LocalMarkdownBlockRenderer provides markdownBlockRenderer, + ) { + content() + } +} diff --git a/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensions.kt b/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensions.kt deleted file mode 100644 index 89a1326f2..000000000 --- a/markdown/int-ui-standalone-styling/src/main/kotlin/org/jetbrains/jewel/intui/markdown/MarkdownBlockRendererExtensions.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.jetbrains.jewel.intui.markdown - -import org.jetbrains.jewel.intui.markdown.styling.dark -import org.jetbrains.jewel.intui.markdown.styling.light -import org.jetbrains.jewel.markdown.extensions.MarkdownRendererExtension -import org.jetbrains.jewel.markdown.rendering.DefaultMarkdownBlockRenderer -import org.jetbrains.jewel.markdown.rendering.InlineMarkdownRenderer -import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer -import org.jetbrains.jewel.markdown.rendering.MarkdownStyling - -public fun MarkdownBlockRenderer.Companion.light( - styling: MarkdownStyling = MarkdownStyling.light(), - rendererExtensions: List = emptyList(), - inlineRenderer: InlineMarkdownRenderer = InlineMarkdownRenderer.default(), - onUrlClick: (String) -> Unit = {}, - onTextClick: () -> Unit = {}, -): MarkdownBlockRenderer = - DefaultMarkdownBlockRenderer(styling, rendererExtensions, inlineRenderer, onUrlClick, onTextClick) - -public fun MarkdownBlockRenderer.Companion.dark( - styling: MarkdownStyling = MarkdownStyling.dark(), - rendererExtensions: List = emptyList(), - inlineRenderer: InlineMarkdownRenderer = InlineMarkdownRenderer.default(), - onUrlClick: (String) -> Unit = {}, - onTextClick: () -> Unit = {}, -): MarkdownBlockRenderer = - DefaultMarkdownBlockRenderer(styling, rendererExtensions, inlineRenderer, onUrlClick, onTextClick) diff --git a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt index 1641c75e0..0a311572f 100644 --- a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt +++ b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/ComponentShowcaseTab.kt @@ -36,6 +36,8 @@ import org.jetbrains.jewel.foundation.modifier.onActivated import org.jetbrains.jewel.foundation.modifier.trackActivation import org.jetbrains.jewel.foundation.modifier.trackComponentActivation import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.intui.markdown.ProvideMarkdownStyling +import org.jetbrains.jewel.markdown.extensions.Markdown import org.jetbrains.jewel.ui.Orientation import org.jetbrains.jewel.ui.Outline import org.jetbrains.jewel.ui.component.CheckboxRow @@ -235,21 +237,23 @@ private fun RowScope.ColumnTwo() { Modifier.trackActivation().weight(1f), verticalArrangement = Arrangement.spacedBy(16.dp), ) { - MarkdownPreview( - """ - |Hi! This is an example of **Markdown** rendering. We support the [CommonMark specs](https://commonmark.org/) - |out of the box, but you can also have _extensions_. - | - |For example: - | * Images - | * Tables - | * And more — I am running out of random things to say 😆 - """.trimMargin(), - Modifier.fillMaxWidth() - .background(JBUI.CurrentTheme.Banner.INFO_BACKGROUND.toComposeColor()) - .border(1.dp, JBUI.CurrentTheme.Banner.INFO_BORDER_COLOR.toComposeColor(), RoundedCornerShape(8.dp)) - .padding(8.dp), - ) + ProvideMarkdownStyling { + Markdown( + """ + |Hi! This is an example of **Markdown** rendering. We support the [CommonMark specs](https://commonmark.org/) + |out of the box, but you can also have _extensions_. + | + |For example: + | * Images + | * Tables + | * And more — I am running out of random things to say 😆 + """.trimMargin(), + Modifier.fillMaxWidth() + .background(JBUI.CurrentTheme.Banner.INFO_BACKGROUND.toComposeColor(), RoundedCornerShape(8.dp)) + .border(1.dp, JBUI.CurrentTheme.Banner.INFO_BORDER_COLOR.toComposeColor(), RoundedCornerShape(8.dp)) + .padding(8.dp), + ) + } Divider(Orientation.Horizontal) diff --git a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/MarkdownPreview.kt b/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/MarkdownPreview.kt deleted file mode 100644 index 2a1cd8027..000000000 --- a/samples/ide-plugin/src/main/kotlin/org/jetbrains/jewel/samples/ideplugin/MarkdownPreview.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.jetbrains.jewel.samples.ideplugin - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import org.intellij.lang.annotations.Language -import org.jetbrains.jewel.foundation.theme.JewelTheme -import org.jetbrains.jewel.intui.markdown.create -import org.jetbrains.jewel.intui.markdown.styling.create -import org.jetbrains.jewel.markdown.processing.MarkdownProcessor -import org.jetbrains.jewel.markdown.rendering.InlineMarkdownRenderer -import org.jetbrains.jewel.markdown.rendering.MarkdownBlockRenderer -import org.jetbrains.jewel.markdown.rendering.MarkdownStyling -import java.awt.Desktop -import java.net.URI - -@Composable -internal fun MarkdownPreview(@Language("Markdown") rawMarkdown: String, modifier: Modifier = Modifier) { - val themeKey = JewelTheme.name - val markdownStyling = remember(themeKey) { MarkdownStyling.create() } - - val processor = remember { MarkdownProcessor() } - // TODO move this away from the composition! - val markdownBlocks by remember { derivedStateOf { processor.processMarkdownDocument(rawMarkdown) } } - - val blockRenderer = - remember(markdownStyling) { - MarkdownBlockRenderer.create( - styling = markdownStyling, - inlineRenderer = InlineMarkdownRenderer.default(), - onUrlClick = { url -> Desktop.getDesktop().browse(URI.create(url)) }, - ) - } - - SelectionContainer(modifier) { - Column( - verticalArrangement = Arrangement.spacedBy(markdownStyling.blockVerticalSpacing), - ) { - for (block in markdownBlocks) { - blockRenderer.render(block) - } - } - } -} 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 7670301e4..3f2c4bab9 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 @@ -1,16 +1,12 @@ package org.jetbrains.jewel.samples.standalone.view.markdown import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollbarAdapter -import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -24,6 +20,7 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jetbrains.jewel.foundation.theme.JewelTheme +import org.jetbrains.jewel.intui.markdown.ProvideMarkdownStyling import org.jetbrains.jewel.intui.markdown.dark import org.jetbrains.jewel.intui.markdown.light import org.jetbrains.jewel.intui.markdown.styling.dark @@ -32,6 +29,7 @@ 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.LazyMarkdown 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 @@ -71,14 +69,12 @@ internal fun MarkdownPreview( styling = markdownStyling, rendererExtensions = listOf(GitHubAlertRendererExtension(AlertStyling.dark(), markdownStyling)), inlineRenderer = InlineMarkdownRenderer.default(extensions), - onUrlClick = { url -> Desktop.getDesktop().browse(URI.create(url)) }, ) } else { MarkdownBlockRenderer.light( styling = markdownStyling, rendererExtensions = listOf(GitHubAlertRendererExtension(AlertStyling.light(), markdownStyling)), inlineRenderer = InlineMarkdownRenderer.default(extensions), - onUrlClick = { url -> Desktop.getDesktop().browse(URI.create(url)) }, ) } } @@ -86,21 +82,22 @@ internal fun MarkdownPreview( // Using the values from the GitHub rendering to ensure contrast val background = remember(isDark) { if (isDark) Color(0xff0d1117) else Color.White } - Box(modifier.background(background)) { - val scrollState = rememberLazyListState() - SelectionContainer { - LazyColumn( + ProvideMarkdownStyling(markdownStyling, blockRenderer) { + Box(modifier.background(background)) { + val lazyListState = rememberLazyListState() + LazyMarkdown( + markdownBlocks = markdownBlocks, + modifier = modifier.background(background), contentPadding = PaddingValues(16.dp), - state = scrollState, - verticalArrangement = Arrangement.spacedBy(markdownStyling.blockVerticalSpacing), - ) { - items(markdownBlocks) { blockRenderer.render(it) } - } - } + state = lazyListState, + isSelectable = true, + onUrlClick = { url -> Desktop.getDesktop().browse(URI.create(url)) }, + ) - VerticalScrollbar( - rememberScrollbarAdapter(scrollState), - Modifier.align(Alignment.TopEnd).fillMaxHeight().padding(2.dp), - ) + VerticalScrollbar( + rememberScrollbarAdapter(lazyListState), + Modifier.align(Alignment.TopEnd).fillMaxHeight().padding(2.dp), + ) + } } }