Skip to content

Commit

Permalink
Allow customizing the CommonMark Parser (#444)
Browse files Browse the repository at this point in the history
This is required by one of our customers, and it allows them to
customize the handling of standard tags (e.g., remove images).

It is an opt-in behaviour and it does not impact existing Markdown
handling code or behaviour.
  • Loading branch information
rock3r authored Jul 10, 2024
1 parent 26f0061 commit ce3aa2f
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
14 changes: 11 additions & 3 deletions markdown/core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -442,12 +442,20 @@ public abstract interface class org/jetbrains/jewel/markdown/extensions/Markdown
public abstract fun getBlockRenderer ()Lorg/jetbrains/jewel/markdown/extensions/MarkdownBlockRendererExtension;
}

public final class org/jetbrains/jewel/markdown/processing/MarkdownParserFactory {
public static final field $stable I
public static final field INSTANCE Lorg/jetbrains/jewel/markdown/processing/MarkdownParserFactory;
public final fun create (ZLjava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/commonmark/parser/Parser;
public static synthetic fun create$default (Lorg/jetbrains/jewel/markdown/processing/MarkdownParserFactory;ZLjava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/commonmark/parser/Parser;
}

public final class org/jetbrains/jewel/markdown/processing/MarkdownProcessor {
public static final field $stable I
public fun <init> ()V
public fun <init> (Ljava/util/List;Z)V
public synthetic fun <init> (Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> ([Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)V
public fun <init> (Ljava/util/List;ZLorg/commonmark/parser/Parser;)V
public synthetic fun <init> (Ljava/util/List;ZLorg/commonmark/parser/Parser;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Z[Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;)V
public synthetic fun <init> (Z[Lorg/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun processChildren (Lorg/commonmark/node/Node;)Ljava/util/List;
public final fun processMarkdownDocument (Ljava/lang/String;)Ljava/util/List;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.jetbrains.jewel.markdown.processing

import org.commonmark.parser.IncludeSourceSpans
import org.commonmark.parser.Parser
import org.jetbrains.jewel.markdown.extensions.MarkdownProcessorExtension

/**
* Simplifies creating a [CommonMark `Parser`][Parser] while also
* supporting Jewel's [MarkdownProcessorExtension]s and the `optimizeEdits`
* flag.
*/
public object MarkdownParserFactory {
/**
* Create a [CommonMark `Parser`][Parser] with the provided [extensions].
* The parser's [Builder][Parser.Builder] can be customized by providing a
* non-null [customizeBuilder] lambda.
*
* Make sure to provide the right value for [optimizeEdits], matching the
* one provided to the [MarkdownProcessor], or this parser will not be set
* up correctly.
*
* @param optimizeEdits If true, sets up the [Parser] to allow for edits
* optimization in the [MarkdownProcessor].
* @param extensions A list of [MarkdownProcessorExtension] to attach.
* @param customizeBuilder Allows customizing the [Parser.Builder] before
* its [build()][Parser.Builder.build] method is called.
*/
public fun create(
optimizeEdits: Boolean,
extensions: List<MarkdownProcessorExtension> = emptyList(),
customizeBuilder: (Parser.Builder.() -> Parser.Builder)? = null,
): Parser =
Parser.builder()
.extensions(extensions.map(MarkdownProcessorExtension::parserExtension))
.run {
val builder =
if (optimizeEdits) {
includeSourceSpans(IncludeSourceSpans.BLOCKS)
} else {
this
}

if (customizeBuilder != null) {
builder.customizeBuilder()
} else {
builder
}
}
.build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import org.commonmark.node.Node
import org.commonmark.node.OrderedList
import org.commonmark.node.Paragraph
import org.commonmark.node.ThematicBreak
import org.commonmark.parser.IncludeSourceSpans
import org.commonmark.parser.Parser
import org.intellij.lang.annotations.Language
import org.jetbrains.annotations.TestOnly
Expand All @@ -30,25 +29,20 @@ import org.jetbrains.jewel.markdown.rendering.DefaultInlineMarkdownRenderer
import org.commonmark.node.ListBlock as CMListBlock

/**
* @param optimizeEdits Optional. Indicates whether the processing should only update the changed blocks
* by keeping a previous state in memory. Default is `true`, use `false` for immutable data.
* @param optimizeEdits Optional. Indicates whether the processing should
* only update the changed blocks by keeping a previous state in memory.
* Default is `true`, use `false` for immutable data.
*/
@ExperimentalJewelApi
public class MarkdownProcessor(
private val extensions: List<MarkdownProcessorExtension> = emptyList(),
private val optimizeEdits: Boolean = true,
private val commonMarkParser: Parser = MarkdownParserFactory.create(optimizeEdits, extensions),
) {
public constructor(vararg extensions: MarkdownProcessorExtension) : this(extensions.toList())

private val commonMarkParser =
Parser.builder()
.let { builder ->
builder.extensions(extensions.map(MarkdownProcessorExtension::parserExtension))
if (optimizeEdits) {
builder.includeSourceSpans(IncludeSourceSpans.BLOCKS)
}
builder.build()
}
public constructor(
optimizeEdits: Boolean = true,
vararg extensions: MarkdownProcessorExtension,
) : this(extensions.toList(), optimizeEdits)

private data class State(val lines: List<String>, val blocks: List<Block>, val indexes: List<Pair<Int, Int>>)

Expand All @@ -58,9 +52,9 @@ public class MarkdownProcessor(
internal fun getCurrentIndexesInTest() = currentState.indexes

/**
* Parses a Markdown document, translating from CommonMark 0.31.2
* to a list of [MarkdownBlock]. Inline Markdown in leaf nodes
* is contained in [InlineMarkdown], which can be rendered
* Parses a Markdown document, translating from CommonMark
* 0.31.2 to a list of [MarkdownBlock]. Inline Markdown in leaf
* nodes is contained in [InlineMarkdown], which can be rendered
* to an [androidx.compose.ui.text.AnnotatedString] by using
* [DefaultInlineMarkdownRenderer.renderAsAnnotatedString].
*
Expand Down

0 comments on commit ce3aa2f

Please sign in to comment.