-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add equals/hashcode to inline Markdown entities, too
Tests have been updated to match the new behaviour, that's closer to the CommonMark one. Now we don't carry unparsed inline Markdown anymore, but rather we fully parse it in advance, so rendering it later is easier and doesn't require running parsing in the UI anymore. This is a tradeoff in memory usage for speed of rendering, and it makes sense in the context of most Markdown text being static and only parsed once, then kept on screen. Inline extensions are now possible, but not tested. This makes #325 possible, since the extension now can be moved to its own module. Note: please don't look too much at the test changes. They're _verbose_, and are only there to ensure CommonMark specs compliance.
- Loading branch information
Showing
21 changed files
with
4,044 additions
and
2,713 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
148 changes: 66 additions & 82 deletions
148
markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/InlineMarkdown.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,96 +1,80 @@ | ||
package org.jetbrains.jewel.markdown | ||
|
||
import org.commonmark.node.Node | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.Code | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.CustomNode | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.Emphasis | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.HardLineBreak | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.HtmlInline | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.Image | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.Link | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.SoftLineBreak | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.StrongEmphasis | ||
import org.jetbrains.jewel.markdown.InlineMarkdown.Text | ||
import org.commonmark.node.Code as CMCode | ||
import org.commonmark.node.CustomNode as CMCustomNode | ||
import org.commonmark.node.Emphasis as CMEmphasis | ||
import org.commonmark.node.HardLineBreak as CMHardLineBreak | ||
import org.commonmark.node.HtmlInline as CMHtmlInline | ||
import org.commonmark.node.Image as CMImage | ||
import org.commonmark.node.Link as CMLink | ||
import org.commonmark.node.SoftLineBreak as CMSoftLineBreak | ||
import org.commonmark.node.StrongEmphasis as CMStrongEmphasis | ||
import org.commonmark.node.Text as CMText | ||
import org.jetbrains.jewel.foundation.GenerateDataFunctions | ||
|
||
/** | ||
* A run of inline Markdown used as content for | ||
* [block-level elements][MarkdownBlock]. | ||
*/ | ||
public sealed interface InlineMarkdown { | ||
public val nativeNode: Node | ||
|
||
@JvmInline | ||
public value class Code(override val nativeNode: CMCode) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class CustomNode(override val nativeNode: CMCustomNode) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class Emphasis(override val nativeNode: CMEmphasis) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class HardLineBreak(override val nativeNode: CMHardLineBreak) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class HtmlInline(override val nativeNode: CMHtmlInline) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class Image(override val nativeNode: CMImage) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class Link(override val nativeNode: CMLink) : InlineMarkdown | ||
|
||
@JvmInline | ||
public value class SoftLineBreak(override val nativeNode: CMSoftLineBreak) : InlineMarkdown | ||
@GenerateDataFunctions | ||
public class Code(override val content: String) : InlineMarkdown, WithTextContent | ||
|
||
public interface CustomNode : InlineMarkdown { | ||
/** | ||
* If this custom node has a text-based representation, this function | ||
* should return it. Otherwise, it should return null. | ||
*/ | ||
public fun contentOrNull(): String? = null | ||
} | ||
|
||
@JvmInline | ||
public value class StrongEmphasis(override val nativeNode: CMStrongEmphasis) : InlineMarkdown | ||
@GenerateDataFunctions | ||
public class Emphasis( | ||
public val delimiter: String, | ||
override val inlineContent: List<InlineMarkdown>, | ||
) : InlineMarkdown, WithInlineMarkdown { | ||
public constructor( | ||
delimiter: String, | ||
vararg inlineContent: InlineMarkdown, | ||
) : this(delimiter, inlineContent.toList()) | ||
} | ||
|
||
@JvmInline | ||
public value class Text(override val nativeNode: CMText) : InlineMarkdown | ||
public data object HardLineBreak : InlineMarkdown | ||
|
||
@GenerateDataFunctions | ||
public class HtmlInline(override val content: String) : InlineMarkdown, WithTextContent | ||
|
||
@GenerateDataFunctions | ||
public class Image( | ||
public val source: String, | ||
public val alt: String, | ||
public val title: String?, | ||
override val inlineContent: List<InlineMarkdown>, | ||
) : InlineMarkdown, WithInlineMarkdown { | ||
public constructor( | ||
source: String, | ||
alt: String, | ||
title: String?, | ||
vararg inlineContent: InlineMarkdown, | ||
) : this(source, alt, title, inlineContent.toList()) | ||
} | ||
|
||
public val children: Iterable<InlineMarkdown> | ||
get() = | ||
object : Iterable<InlineMarkdown> { | ||
override fun iterator(): Iterator<InlineMarkdown> = | ||
object : Iterator<InlineMarkdown> { | ||
var current = this@InlineMarkdown.nativeNode.firstChild | ||
@GenerateDataFunctions | ||
public class Link( | ||
public val destination: String, | ||
public val title: String?, | ||
override val inlineContent: List<InlineMarkdown>, | ||
) : InlineMarkdown, WithInlineMarkdown { | ||
public constructor( | ||
destination: String, | ||
title: String?, | ||
vararg inlineContent: InlineMarkdown, | ||
) : this(destination, title, inlineContent.toList()) | ||
} | ||
|
||
override fun hasNext(): Boolean = current != null | ||
public data object SoftLineBreak : InlineMarkdown | ||
|
||
@GenerateDataFunctions | ||
public class StrongEmphasis( | ||
public val delimiter: String, | ||
override val inlineContent: List<InlineMarkdown>, | ||
) : InlineMarkdown, WithInlineMarkdown { | ||
public constructor( | ||
delimiter: String, | ||
vararg inlineContent: InlineMarkdown, | ||
) : this(delimiter, inlineContent.toList()) | ||
} | ||
|
||
override fun next(): InlineMarkdown = | ||
if (hasNext()) { | ||
current.toInlineNode().also { | ||
current = current.next | ||
} | ||
} else { | ||
throw NoSuchElementException() | ||
} | ||
} | ||
} | ||
@GenerateDataFunctions | ||
public class Text(override val content: String) : InlineMarkdown, WithTextContent | ||
} | ||
|
||
public fun Node.toInlineNode(): InlineMarkdown = | ||
when (this) { | ||
is CMText -> Text(this) | ||
is CMLink -> Link(this) | ||
is CMEmphasis -> Emphasis(this) | ||
is CMStrongEmphasis -> StrongEmphasis(this) | ||
is CMCode -> Code(this) | ||
is CMHtmlInline -> HtmlInline(this) | ||
is CMImage -> Image(this) | ||
is CMHardLineBreak -> HardLineBreak(this) | ||
is CMSoftLineBreak -> SoftLineBreak(this) | ||
is CMCustomNode -> CustomNode(this) | ||
else -> error("Unexpected block $this") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/WithInlineMarkdown.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.jetbrains.jewel.markdown | ||
|
||
public interface WithInlineMarkdown { | ||
public val inlineContent: List<InlineMarkdown> | ||
} |
5 changes: 5 additions & 0 deletions
5
markdown/core/src/main/kotlin/org/jetbrains/jewel/markdown/WithTextContent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.jetbrains.jewel.markdown | ||
|
||
public interface WithTextContent { | ||
public val content: String | ||
} |
25 changes: 25 additions & 0 deletions
25
...c/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownInlineProcessorExtension.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package org.jetbrains.jewel.markdown.extensions | ||
|
||
import org.commonmark.node.CustomNode | ||
import org.jetbrains.jewel.markdown.InlineMarkdown | ||
import org.jetbrains.jewel.markdown.processing.MarkdownProcessor | ||
|
||
public interface MarkdownInlineProcessorExtension { | ||
/** | ||
* Returns true if the [node] can be processed by this extension instance. | ||
* | ||
* @param node The [CustomNode] to parse | ||
*/ | ||
public fun canProcess(node: CustomNode): Boolean | ||
|
||
/** | ||
* Processes the [node] as a [InlineMarkdown.CustomNode], if possible. Note | ||
* that you should always check that [canProcess] returns true for the same | ||
* [node], as implementations might throw an exception for unsupported node | ||
* types. | ||
*/ | ||
public fun processInlineMarkdown( | ||
node: CustomNode, | ||
processor: MarkdownProcessor, | ||
): InlineMarkdown.CustomNode? | ||
} |
48 changes: 35 additions & 13 deletions
48
...ore/src/main/kotlin/org/jetbrains/jewel/markdown/extensions/MarkdownProcessorExtension.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,58 @@ | ||
package org.jetbrains.jewel.markdown.extensions | ||
|
||
import org.commonmark.node.CustomBlock | ||
import org.commonmark.parser.Parser.ParserExtension | ||
import org.commonmark.renderer.text.TextContentRenderer.TextContentRendererExtension | ||
import org.jetbrains.jewel.foundation.ExperimentalJewelApi | ||
import org.jetbrains.jewel.markdown.MarkdownBlock | ||
|
||
/** An extension for the Jewel Markdown processing engine. */ | ||
@ExperimentalJewelApi | ||
public interface MarkdownProcessorExtension { | ||
/** | ||
* A CommonMark [ParserExtension] that will be used to parse the extended | ||
* syntax represented by this extension instance. Null in the case where | ||
* parsing is already handled by an existing [org.commonmark.parser.Parser]. | ||
* syntax represented by this extension instance. | ||
* | ||
* Can be null if all required processing is already handled by an existing | ||
* [org.commonmark.parser.Parser]. | ||
*/ | ||
public val parserExtension: ParserExtension? | ||
get() = null | ||
|
||
/** | ||
* A CommonMark [TextContentRendererExtension] that will be used to render | ||
* the text content of the CommonMark [CustomBlock] produced by the | ||
* [parserExtension]. Null in the case where rendering is already | ||
* handled by an existing [org.commonmark.renderer.Renderer]. | ||
* the text content of the CommonMark [org.commonmark.node.CustomBlock] | ||
* produced by the [parserExtension]. | ||
* | ||
* Can be null if all required processing is already handled by an existing | ||
* [org.commonmark.renderer.Renderer]. | ||
*/ | ||
public val textRendererExtension: TextContentRendererExtension? | ||
get() = null | ||
|
||
/** | ||
* An extension for | ||
* [`MarkdownParser`][org.jetbrains.jewel.markdown.parsing.MarkdownParser] | ||
* that will transform a supported [CustomBlock] into the corresponding | ||
* [MarkdownBlock.CustomBlock]. Null in the case where processing | ||
* is already be handled by [org.jetbrains.jewel.markdown.processing.MarkdownProcessor] | ||
* or another [org.jetbrains.jewel.markdown.extensions.MarkdownProcessorExtension]. | ||
* [`MarkdownProcessor`][org.jetbrains.jewel.markdown.processing.MarkdownProcessor] | ||
* that will transform a supported [org.commonmark.node.CustomBlock] into | ||
* the corresponding | ||
* [org.jetbrains.jewel.markdown.MarkdownBlock.CustomBlock]. | ||
* | ||
* Can be null if all required processing is already handled by | ||
* [org.jetbrains.jewel.markdown.processing.MarkdownProcessor] or another | ||
* [org.jetbrains.jewel.markdown.extensions.MarkdownProcessorExtension]. | ||
*/ | ||
public val processorExtension: MarkdownBlockProcessorExtension? | ||
public val blockProcessorExtension: MarkdownBlockProcessorExtension? | ||
get() = null | ||
|
||
/** | ||
* An extension for | ||
* [`MarkdownProcessor`][org.jetbrains.jewel.markdown.processing.MarkdownProcessor] | ||
* that will transform a supported [org.commonmark.node.CustomNode] into | ||
* the corresponding | ||
* [org.jetbrains.jewel.markdown.InlineMarkdown.CustomNode]. | ||
* | ||
* Can be null if all required processing is already handled by | ||
* [org.jetbrains.jewel.markdown.processing.MarkdownProcessor] or another | ||
* [org.jetbrains.jewel.markdown.extensions.MarkdownProcessorExtension]. | ||
*/ | ||
public val inlineProcessorExtension: MarkdownInlineProcessorExtension? | ||
get() = null | ||
} |
Oops, something went wrong.