Skip to content

Commit 9c02483

Browse files
authored
Merge pull request #245 from MohamedRejeb/1.x
Improve markdown encoding
2 parents 0fdb42c + 95c52fa commit 9c02483

File tree

2 files changed

+102
-7
lines changed

2 files changed

+102
-7
lines changed

richeditor-compose/src/commonMain/kotlin/com/mohamedrejeb/richeditor/parser/markdown/RichTextStateMarkdownParser.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
2626
@OptIn(ExperimentalRichTextApi::class)
2727
override fun encode(input: String): RichTextState {
2828
val openedNodes = mutableListOf<ASTNode>()
29-
val stringBuilder = StringBuilder()
3029
val richParagraphList = mutableListOf(RichParagraph())
3130
var currentRichSpan: RichSpan? = null
3231
var currentRichParagraphType: ParagraphType = DefaultParagraph()
@@ -36,8 +35,6 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
3635
onText = { text ->
3736
if (text.isEmpty()) return@encodeMarkdownToRichText
3837

39-
stringBuilder.append(text)
40-
4138
if (richParagraphList.isEmpty())
4239
richParagraphList.add(RichParagraph())
4340

@@ -47,7 +44,10 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
4744
if (safeCurrentRichSpan.children.isEmpty()) {
4845
safeCurrentRichSpan.text += text
4946
} else {
50-
val newRichSpan = RichSpan(paragraph = currentRichParagraph)
47+
val newRichSpan = RichSpan(
48+
paragraph = currentRichParagraph,
49+
parent = safeCurrentRichSpan,
50+
)
5151
newRichSpan.text = text
5252
safeCurrentRichSpan.children.add(newRichSpan)
5353
}
@@ -63,8 +63,6 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
6363
val tagSpanStyle = markdownElementsSpanStyleEncodeMap[node.type]
6464

6565
if (node.type in markdownBlockElements) {
66-
stringBuilder.append(' ')
67-
6866
val currentRichParagraph = richParagraphList.last()
6967

7068
// Get paragraph type from markdown element
@@ -127,6 +125,7 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
127125
currentRichSpan?.spanStyle = currentRichSpan?.spanStyle?.merge(child.spanStyle) ?: child.spanStyle
128126
currentRichSpan?.style = child.style
129127
currentRichSpan?.children?.clear()
128+
currentRichSpan?.children?.addAll(child.children)
130129
}
131130
}
132131

@@ -166,7 +165,7 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
166165

167166
richTextState.richParagraphList.fastForEachIndexed { index, richParagraph ->
168167
// Append paragraph start text
169-
builder.append(richParagraph.type.startRichSpan.text)
168+
builder.appendParagraphStartText(richParagraph)
170169

171170
richParagraph.getFirstNonEmptyChild()?.let { firstNonEmptyChild ->
172171
if (firstNonEmptyChild.text.isNotEmpty()) {
@@ -222,6 +221,19 @@ internal object RichTextStateMarkdownParser : RichTextStateParser<String> {
222221
return stringBuilder.toString()
223222
}
224223

224+
private fun StringBuilder.appendParagraphStartText(paragraph: RichParagraph) {
225+
when (val type = paragraph.type) {
226+
is OrderedList ->
227+
append("${type.number}. ")
228+
229+
is UnorderedList ->
230+
append("- ")
231+
232+
else ->
233+
Unit
234+
}
235+
}
236+
225237
/**
226238
* Encodes Markdown elements to [SpanStyle].
227239
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.mohamedrejeb.richeditor.parser.markdown
2+
3+
import androidx.compose.ui.text.SpanStyle
4+
import androidx.compose.ui.text.font.FontStyle
5+
import androidx.compose.ui.text.font.FontWeight
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
9+
class RichTextStateMarkdownParserTest {
10+
11+
@Test
12+
fun testBold() {
13+
val markdown = "**Hello World!**"
14+
val expectedText = "Hello World!"
15+
val state = RichTextStateMarkdownParser.encode(markdown)
16+
val actualText = state.annotatedString.text
17+
18+
assertEquals(
19+
expected = expectedText,
20+
actual = actualText,
21+
)
22+
23+
assertEquals(
24+
expected = SpanStyle(fontWeight = FontWeight.Bold),
25+
actual = state.richParagraphList.first().children.first().spanStyle
26+
)
27+
}
28+
29+
@Test
30+
fun testBoldWithNestedItalic() {
31+
val markdown = "**Hello *World!***"
32+
val expectedText = "Hello World!"
33+
val state = RichTextStateMarkdownParser.encode(markdown)
34+
val actualText = state.annotatedString.text
35+
36+
assertEquals(
37+
expected = expectedText,
38+
actual = actualText,
39+
)
40+
41+
val firstChild = state.richParagraphList.first().children.first()
42+
val secondChild = firstChild.children.first()
43+
44+
assertEquals(
45+
expected = SpanStyle(fontWeight = FontWeight.Bold),
46+
actual = firstChild.spanStyle
47+
)
48+
49+
assertEquals(
50+
expected = SpanStyle(fontStyle = FontStyle.Italic),
51+
actual = secondChild.spanStyle
52+
)
53+
}
54+
55+
56+
57+
@Test
58+
fun testBoldWithNestedItalicAndUnderline() {
59+
val markdown = "**Hello *World!***"
60+
val expectedText = "Hello World!"
61+
val state = RichTextStateMarkdownParser.encode(markdown)
62+
val actualText = state.annotatedString.text
63+
64+
assertEquals(
65+
expected = expectedText,
66+
actual = actualText,
67+
)
68+
69+
val firstChild = state.richParagraphList.first().children.first()
70+
val secondChild = firstChild.children.first()
71+
72+
assertEquals(
73+
expected = SpanStyle(fontWeight = FontWeight.Bold),
74+
actual = firstChild.spanStyle
75+
)
76+
77+
assertEquals(
78+
expected = SpanStyle(fontStyle = FontStyle.Italic),
79+
actual = secondChild.spanStyle
80+
)
81+
}
82+
83+
}

0 commit comments

Comments
 (0)