Skip to content

Commit

Permalink
Add Markdown styling to theme (#367)
Browse files Browse the repository at this point in the history
Add Markdown styling to theme, add simple components

We were lacking a good, easy way to render Markdown, requiring a lot of
ceremony. Now it's much easier, you just need to provide the styling,
renderer, and processor as composition locals (with the defaults
coming from ProvideMarkdownStyling). Then use either Markdown(), or
LazyMarkdown(), and your content is displayed. Better!

I have updated the samples to show how it works.
  • Loading branch information
rock3r authored Apr 26, 2024
1 parent 8591713 commit a428d23
Show file tree
Hide file tree
Showing 16 changed files with 512 additions and 208 deletions.
56 changes: 34 additions & 22 deletions markdown/core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 <init> (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 <init> (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
}

Expand Down Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<MarkdownStyling> = 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<MarkdownProcessor> = 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<MarkdownBlockRenderer> = 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<MarkdownBlock>()) }
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<MarkdownBlock>,
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<MarkdownBlock>,
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)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ public interface MarkdownBlockRendererExtension {
block: CustomBlock,
blockRenderer: MarkdownBlockRenderer,
inlineRenderer: InlineMarkdownRenderer,
onUrlClick: (String) -> Unit,
onTextClick: () -> Unit,
)
}
Loading

0 comments on commit a428d23

Please sign in to comment.