Skip to content

Commit

Permalink
[JEWEL-748] Define delimited inlines extension API, implement striket…
Browse files Browse the repository at this point in the history
…hrough

This includes a bunch of changes to the Markdown inline APIs to better align with CommonMark and our actual needs; a clean-up of the previous half-hearted attempt at supporting custom inline nodes (it did not and could never have worked in the way it was implemented), and some misc tweaks and cleanup.
  • Loading branch information
rock3r committed Feb 10, 2025
1 parent b908267 commit c38198c
Show file tree
Hide file tree
Showing 49 changed files with 1,061 additions and 337 deletions.
1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.math.max
import org.jetbrains.jewel.foundation.modifier.thenIf

/**
* A simple table that sizes columns to take as much room as they need. If the horizontal space available is less than
Expand All @@ -34,7 +35,7 @@ import kotlin.math.max
* @param rowCount The number of rows this table has.
* @param columnCount The number of columns this table has.
* @param cellBorderColor The color of the cell borders. Set to [Color.Unspecified] to avoid drawing the borders — in
* which case, the [cellBorderWidth] acts as padding.
* which case, the [cellBorderWidth] acts as a padding.
* @param modifier Modifier to apply to the table.
* @param cellBorderWidth The width of the table's borders.
* @param rows The rows that make up the table. Each row is a list of composables, one per row cell.
Expand Down Expand Up @@ -214,7 +215,3 @@ private fun Modifier.drawTableBorders(
)
}
}

// TODO remove this once thenIf is moved to foundation
private inline fun Modifier.thenIf(precondition: Boolean, action: Modifier.() -> Modifier): Modifier =
if (precondition) action() else this
3 changes: 2 additions & 1 deletion platform/jewel/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ktfmtGradlePlugin = "0.20.1"
[libraries]
commonmark-core = { module = "org.commonmark:commonmark", version.ref = "commonmark" }
commonmark-ext-autolink = { module = "org.commonmark:commonmark-ext-autolink", version.ref = "commonmark" }
commonmark-ext-gfm-strikethrough = { module = "org.commonmark:commonmark-ext-gfm-strikethrough", version.ref = "commonmark" }
commonmark-ext-gfm-tables = { module = "org.commonmark:commonmark-ext-gfm-tables", version.ref = "commonmark" }

filePicker = { module = "com.darkrockstudios:mpfilepicker", version = "3.1.0" }
Expand Down Expand Up @@ -52,4 +53,4 @@ kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlinx-binaryCompatValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlinxBinaryCompat" }
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlinter = { id = "org.jmailen.kotlinter", version.ref = "kotlinterGradlePlugin" }
ktfmt = { id = "com.ncorti.ktfmt.gradle", version.ref = "ktfmtGradlePlugin" }
ktfmt = { id = "com.ncorti.ktfmt.gradle", version.ref = "ktfmtGradlePlugin" }
4 changes: 2 additions & 2 deletions platform/jewel/markdown/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ Additional supported Markdown, via extensions:
* Autolink (standard CommonMark, but provided as extension) — see [`extension-autolink`](extension/autolink)
* Tables ([GitHub Flavored Markdown](https://github.github.com/gfm/#tables-extension-)) — see [
`extension-gfm-tables`](extension/gfm-tables)
* Strikethrough ([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:

* 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/))
* Task list items ([GitHub Flavored Markdown](https://github.github.com/gfm/#task-list-items-extension-))
* Keyboard shortcuts highlighting (specialized HTML handling)
Expand Down Expand Up @@ -159,4 +159,4 @@ You can see this in action running the Standalone sample, and selecting Markdown

The following image shows the Jewel Markdown renderer displaying the Jewel readme.

![Image showing the Markdown renderer page from the Jewel standalone sample](../art/docs/markdown-renderer.png)
![Image showing the Markdown renderer page from the Jewel standalone sample](../art/docs/markdown-renderer.png)
41 changes: 22 additions & 19 deletions platform/jewel/markdown/core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ public final class org/jetbrains/jewel/markdown/InlineMarkdown$Code : org/jetbra
public fun toString ()Ljava/lang/String;
}

public abstract interface class org/jetbrains/jewel/markdown/InlineMarkdown$CustomNode : org/jetbrains/jewel/markdown/InlineMarkdown {
public abstract fun contentOrNull ()Ljava/lang/String;
public abstract interface class org/jetbrains/jewel/markdown/InlineMarkdown$CustomDelimitedNode : org/jetbrains/jewel/markdown/InlineMarkdown, org/jetbrains/jewel/markdown/WithInlineMarkdown {
public abstract fun getClosingDelimiter ()Ljava/lang/String;
public abstract fun getOpeningDelimiter ()Ljava/lang/String;
}

public final class org/jetbrains/jewel/markdown/InlineMarkdown$CustomNode$DefaultImpls {
public static fun contentOrNull (Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomNode;)Ljava/lang/String;
public final class org/jetbrains/jewel/markdown/InlineMarkdown$CustomDelimitedNode$DefaultImpls {
public static fun getClosingDelimiter (Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomDelimitedNode;)Ljava/lang/String;
}

public final class org/jetbrains/jewel/markdown/InlineMarkdown$Emphasis : org/jetbrains/jewel/markdown/InlineMarkdown, org/jetbrains/jewel/markdown/WithInlineMarkdown {
Expand Down Expand Up @@ -264,14 +265,14 @@ public abstract interface class org/jetbrains/jewel/markdown/extensions/Markdown
public abstract fun render (Lorg/jetbrains/jewel/markdown/MarkdownBlock$CustomBlock;Lorg/jetbrains/jewel/markdown/rendering/MarkdownBlockRenderer;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;ZLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)V
}

public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownInlineProcessorExtension {
public abstract fun canProcess (Lorg/commonmark/node/CustomNode;)Z
public abstract fun processInlineMarkdown (Lorg/commonmark/node/CustomNode;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;)Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomNode;
public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownDelimitedInlineProcessorExtension {
public abstract fun canProcess (Lorg/commonmark/node/Delimited;)Z
public abstract fun processDelimitedInline (Lorg/commonmark/node/Delimited;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;)Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomDelimitedNode;
}

public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownInlineRendererExtension {
public abstract fun canRender (Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomNode;)Z
public abstract fun render (Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomNode;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Z)V
public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownDelimitedInlineRendererExtension {
public abstract fun canRender (Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomDelimitedNode;)Z
public abstract fun render (Lorg/jetbrains/jewel/markdown/InlineMarkdown$CustomDelimitedNode;Lorg/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer;Lorg/jetbrains/jewel/markdown/rendering/InlinesStyling;ZLkotlin/jvm/functions/Function1;)Landroidx/compose/ui/text/AnnotatedString;
}

public final class org/jetbrains/jewel/markdown/extensions/MarkdownKt {
Expand All @@ -287,26 +288,26 @@ public final class org/jetbrains/jewel/markdown/extensions/MarkdownKt {

public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension {
public abstract fun getBlockProcessorExtension ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownBlockProcessorExtension;
public abstract fun getInlineProcessorExtension ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownInlineProcessorExtension;
public abstract fun getDelimitedInlineProcessorExtension ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownDelimitedInlineProcessorExtension;
public abstract fun getParserExtension ()Lorg/commonmark/parser/Parser$ParserExtension;
public abstract fun getTextRendererExtension ()Lorg/commonmark/renderer/text/TextContentRenderer$TextContentRendererExtension;
}

public final class org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension$DefaultImpls {
public static fun getBlockProcessorExtension (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/jetbrains/jewel/markdown/extensions/MarkdownBlockProcessorExtension;
public static fun getInlineProcessorExtension (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/jetbrains/jewel/markdown/extensions/MarkdownInlineProcessorExtension;
public static fun getDelimitedInlineProcessorExtension (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/jetbrains/jewel/markdown/extensions/MarkdownDelimitedInlineProcessorExtension;
public static fun getParserExtension (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/commonmark/parser/Parser$ParserExtension;
public static fun getTextRendererExtension (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/commonmark/renderer/text/TextContentRenderer$TextContentRendererExtension;
}

public abstract interface class org/jetbrains/jewel/markdown/extensions/MarkdownRendererExtension {
public abstract fun getBlockRenderer ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension;
public abstract fun getInlineRenderer ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownInlineRendererExtension;
public abstract fun getDelimitedInlineRenderer ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownDelimitedInlineRendererExtension;
}

public final class org/jetbrains/jewel/markdown/extensions/MarkdownRendererExtension$DefaultImpls {
public static fun getBlockRenderer (Lorg/jetbrains/jewel/markdown/extensions/MarkdownRendererExtension;)Lorg/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension;
public static fun getInlineRenderer (Lorg/jetbrains/jewel/markdown/extensions/MarkdownRendererExtension;)Lorg/jetbrains/jewel/markdown/extensions/MarkdownInlineRendererExtension;
public static fun getDelimitedInlineRenderer (Lorg/jetbrains/jewel/markdown/extensions/MarkdownRendererExtension;)Lorg/jetbrains/jewel/markdown/extensions/MarkdownDelimitedInlineRendererExtension;
}

public final class org/jetbrains/jewel/markdown/processing/MarkdownParserFactory {
Expand All @@ -321,26 +322,27 @@ public final class org/jetbrains/jewel/markdown/processing/MarkdownProcessor {
public fun <init> ()V
public fun <init> (Ljava/util/List;Lorg/jetbrains/jewel/markdown/MarkdownMode;Lorg/commonmark/parser/Parser;)V
public synthetic fun <init> (Ljava/util/List;Lorg/jetbrains/jewel/markdown/MarkdownMode;Lorg/commonmark/parser/Parser;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getBlockExtensions ()Ljava/util/List;
public final fun getDelimitedInlineExtensions ()Ljava/util/List;
public final fun getExtensions ()Ljava/util/List;
public final fun plus (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;
public final fun processChildren (Lorg/commonmark/node/Node;)Ljava/util/List;
public final fun processMarkdownDocument (Ljava/lang/String;)Ljava/util/List;
public final fun withExtension (Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;
}

public final class org/jetbrains/jewel/markdown/processing/ProcessingUtilKt {
public static final fun readInlineContent (Lorg/commonmark/node/Node;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;)Ljava/util/List;
public static final fun readInlineMarkdown (Lorg/commonmark/node/Node;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;)Ljava/util/List;
public static final fun toInlineMarkdownOrNull (Lorg/commonmark/node/Node;Lorg/jetbrains/jewel/markdown/processing/MarkdownProcessor;)Lorg/jetbrains/jewel/markdown/InlineMarkdown;
}

public class org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer : org/jetbrains/jewel/markdown/rendering/InlineMarkdownRenderer {
public static final field $stable I
public static final field Companion Lorg/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer$Companion;
public fun <init> (Ljava/util/List;)V
protected final fun getDelimitedNodeRendererExtensions ()Ljava/util/List;
public fun renderAsAnnotatedString (Ljava/lang/Iterable;Lorg/jetbrains/jewel/markdown/rendering/InlinesStyling;ZLkotlin/jvm/functions/Function1;)Landroidx/compose/ui/text/AnnotatedString;
}

public final class org/jetbrains/jewel/markdown/rendering/DefaultInlineMarkdownRenderer$Companion {
}

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;)V
Expand Down Expand Up @@ -438,6 +440,7 @@ public final class org/jetbrains/jewel/markdown/rendering/MarkdownStyling {
public static final field Companion Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Companion;
public synthetic fun <init> (FLorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Paragraph;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Heading;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$BlockQuote;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$List;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Image;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$ThematicBreak;Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$HtmlBlock;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getBaseInlinesStyling ()Lorg/jetbrains/jewel/markdown/rendering/InlinesStyling;
public final fun getBlockQuote ()Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$BlockQuote;
public final fun getBlockVerticalSpacing-D9Ej5fM ()F
public final fun getCode ()Lorg/jetbrains/jewel/markdown/rendering/MarkdownStyling$Code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package org.jetbrains.jewel.markdown

import org.jetbrains.jewel.foundation.GenerateDataFunctions

/** A run of inline Markdown used as content for [block-level elements][MarkdownBlock]. */
/**
* An inline Markdown node, usually found as content for [block-level elements][MarkdownBlock] or other inline nodes
* annotated with the [WithInlineMarkdown] interface.
*/
public sealed interface InlineMarkdown {
@GenerateDataFunctions
public class Code(override val content: String) : InlineMarkdown, WithTextContent {
Expand All @@ -20,12 +23,26 @@ public sealed interface InlineMarkdown {
override fun toString(): String = "Code(content='$content')"
}

public interface CustomNode : InlineMarkdown {
/**
* An inline node that is delimited by a fixed delimiter, and can be rendered directly into an
* [androidx.compose.ui.text.AnnotatedString].
*
* This type of node can be parsed by a
* [org.jetbrains.jewel.markdown.extensions.MarkdownDelimitedInlineProcessorExtension] and rendered by a
* [org.jetbrains.jewel.markdown.extensions.MarkdownDelimitedInlineRendererExtension].
*/
public interface CustomDelimitedNode : InlineMarkdown, WithInlineMarkdown {
/**
* If this custom node has a text-based representation, this function should return it. Otherwise, it should
* return null.
* The string used to indicate the beginning of this type of inline node. Can be identical to the
* [closingDelimiter].
*/
public fun contentOrNull(): String? = null
public val openingDelimiter: String

/**
* The string used to indicate the end of this type of inline node. Can be identical to the [openingDelimiter].
*/
public val closingDelimiter: String
get() = openingDelimiter
}

@GenerateDataFunctions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.jetbrains.jewel.markdown

/** An inline Markdown node that contains other [InlineMarkdown] nodes. */
public interface WithInlineMarkdown {
/** Child inline Markdown nodes. */
public val inlineContent: List<InlineMarkdown>
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.jetbrains.jewel.markdown

/** An inline Markdown node that has a plain text content, or can be rendered as plain text. */
public interface WithTextContent {
/** The plain text content or representation of this node. */
public val content: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ import org.commonmark.node.CustomBlock
import org.jetbrains.jewel.markdown.MarkdownBlock
import org.jetbrains.jewel.markdown.processing.MarkdownProcessor

/** Extension that can process a custom block-level [CustomBlock]. */
public interface MarkdownBlockProcessorExtension {
/**
* Returns true if the [block] can be processed by this extension instance.
*
* @param block The [CustomBlock] to parse
* @return True if this extension can process the provided [CustomBlock], false otherwise.
*/
public fun canProcess(block: CustomBlock): Boolean

/**
* Processes the [block] as a [MarkdownBlock.CustomBlock], if possible. Note that you should always check that
* [canProcess] returns true for the same [block], as implementations might throw an exception for unsupported block
* types.
*
* @param block The [CustomBlock] to process, for which this extension's [canProcess] returned true.
* @param processor The [MarkdownProcessor] to use for processing.
* @return null if the processing fails, otherwise the processed [MarkdownBlock.CustomBlock].
*/
public fun processMarkdownBlock(block: CustomBlock, processor: MarkdownProcessor): MarkdownBlock.CustomBlock?
}
Loading

0 comments on commit c38198c

Please sign in to comment.