diff --git a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/parser/markdown/RichTextStateMarkdownParser.kt b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/parser/markdown/RichTextStateMarkdownParser.kt index df8925a5..ec187426 100644 --- a/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/parser/markdown/RichTextStateMarkdownParser.kt +++ b/richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/parser/markdown/RichTextStateMarkdownParser.kt @@ -26,7 +26,6 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { @OptIn(ExperimentalRichTextApi::class) override fun encode(input: String): RichTextState { val openedNodes = mutableListOf() - val stringBuilder = StringBuilder() val richParagraphList = mutableListOf(RichParagraph()) var currentRichSpan: RichSpan? = null var currentRichParagraphType: ParagraphType = DefaultParagraph() @@ -36,8 +35,6 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { onText = { text -> if (text.isEmpty()) return@encodeMarkdownToRichText - stringBuilder.append(text) - if (richParagraphList.isEmpty()) richParagraphList.add(RichParagraph()) @@ -47,7 +44,10 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { if (safeCurrentRichSpan.children.isEmpty()) { safeCurrentRichSpan.text += text } else { - val newRichSpan = RichSpan(paragraph = currentRichParagraph) + val newRichSpan = RichSpan( + paragraph = currentRichParagraph, + parent = safeCurrentRichSpan, + ) newRichSpan.text = text safeCurrentRichSpan.children.add(newRichSpan) } @@ -63,8 +63,6 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { val tagSpanStyle = markdownElementsSpanStyleEncodeMap[node.type] if (node.type in markdownBlockElements) { - stringBuilder.append(' ') - val currentRichParagraph = richParagraphList.last() // Get paragraph type from markdown element @@ -127,6 +125,7 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { currentRichSpan?.spanStyle = currentRichSpan?.spanStyle?.merge(child.spanStyle) ?: child.spanStyle currentRichSpan?.style = child.style currentRichSpan?.children?.clear() + currentRichSpan?.children?.addAll(child.children) } } @@ -166,7 +165,7 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { richTextState.richParagraphList.fastForEachIndexed { index, richParagraph -> // Append paragraph start text - builder.append(richParagraph.type.startRichSpan.text) + builder.appendParagraphStartText(richParagraph) richParagraph.getFirstNonEmptyChild()?.let { firstNonEmptyChild -> if (firstNonEmptyChild.text.isNotEmpty()) { @@ -222,6 +221,19 @@ internal object RichTextStateMarkdownParser : RichTextStateParser { return stringBuilder.toString() } + private fun StringBuilder.appendParagraphStartText(paragraph: RichParagraph) { + when (val type = paragraph.type) { + is OrderedList -> + append("${type.number}. ") + + is UnorderedList -> + append("- ") + + else -> + Unit + } + } + /** * Encodes Markdown elements to [SpanStyle]. * diff --git a/richeditor-compose/src/commonTest/kotlin/com.mohamedrejeb.richeditor/parser/markdown/RichTextStateMarkdownParserTest.kt b/richeditor-compose/src/commonTest/kotlin/com.mohamedrejeb.richeditor/parser/markdown/RichTextStateMarkdownParserTest.kt new file mode 100644 index 00000000..4a222d6c --- /dev/null +++ b/richeditor-compose/src/commonTest/kotlin/com.mohamedrejeb.richeditor/parser/markdown/RichTextStateMarkdownParserTest.kt @@ -0,0 +1,83 @@ +package com.mohamedrejeb.richeditor.parser.markdown + +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import kotlin.test.Test +import kotlin.test.assertEquals + +class RichTextStateMarkdownParserTest { + + @Test + fun testBold() { + val markdown = "**Hello World!**" + val expectedText = "Hello World!" + val state = RichTextStateMarkdownParser.encode(markdown) + val actualText = state.annotatedString.text + + assertEquals( + expected = expectedText, + actual = actualText, + ) + + assertEquals( + expected = SpanStyle(fontWeight = FontWeight.Bold), + actual = state.richParagraphList.first().children.first().spanStyle + ) + } + + @Test + fun testBoldWithNestedItalic() { + val markdown = "**Hello *World!***" + val expectedText = "Hello World!" + val state = RichTextStateMarkdownParser.encode(markdown) + val actualText = state.annotatedString.text + + assertEquals( + expected = expectedText, + actual = actualText, + ) + + val firstChild = state.richParagraphList.first().children.first() + val secondChild = firstChild.children.first() + + assertEquals( + expected = SpanStyle(fontWeight = FontWeight.Bold), + actual = firstChild.spanStyle + ) + + assertEquals( + expected = SpanStyle(fontStyle = FontStyle.Italic), + actual = secondChild.spanStyle + ) + } + + + + @Test + fun testBoldWithNestedItalicAndUnderline() { + val markdown = "**Hello *World!***" + val expectedText = "Hello World!" + val state = RichTextStateMarkdownParser.encode(markdown) + val actualText = state.annotatedString.text + + assertEquals( + expected = expectedText, + actual = actualText, + ) + + val firstChild = state.richParagraphList.first().children.first() + val secondChild = firstChild.children.first() + + assertEquals( + expected = SpanStyle(fontWeight = FontWeight.Bold), + actual = firstChild.spanStyle + ) + + assertEquals( + expected = SpanStyle(fontStyle = FontStyle.Italic), + actual = secondChild.spanStyle + ) + } + +} \ No newline at end of file