Add tests for nested unordered and checkbox lists, extend parser for indentation handling, and bump version to 2.0.0

This commit is contained in:
2026-01-18 10:50:53 +01:00
parent 382ee2f5aa
commit 68f0dfbc67
5 changed files with 110 additions and 24 deletions

1
.gitignore vendored
View File

@@ -45,3 +45,4 @@ bin/
.kotlin
kotlin-js-store
gradle.properties
.idea

View File

@@ -8,7 +8,7 @@ plugins {
}
group = "nl.astraeus"
version = "1.1.0"
version = "2.0.0"
repositories {
mavenCentral()

View File

@@ -1,9 +1,16 @@
package nl.astraeus.markdown.parser
data class ListItem(
val text: String,
val order: Int,
val indentation: Int = 0
)
data class CheckboxItem(
val line: Int,
val checked: Boolean,
val text: String,
val indentation: Int = 0
)
sealed class MarkdownPart {
@@ -66,7 +73,7 @@ sealed class MarkdownPart {
) : MarkdownPart()
data class UnorderedList(
val lines: List<Pair<Int, String>>,
val lines: List<ListItem>,
) : MarkdownPart()
data class CheckboxList(
@@ -74,7 +81,7 @@ sealed class MarkdownPart {
) : MarkdownPart()
data class OrderedList(
val lines: List<String>,
val lines: List<ListItem>,
) : MarkdownPart()
data class CodeBlock(

View File

@@ -11,17 +11,22 @@ enum class MarkdownType {
}
fun markdown(text: String): List<MarkdownPart> {
val lines = text.lines()
val lines: MutableList<String> = text.lines().toMutableList()
val parts = mutableListOf<MarkdownPart>()
var language = ""
var type = MarkdownType.PARAGRAPH
var listIndex = 1
var checkboxLine = 0
var indentation = 0
var index = 0
val buffer = StringBuilder()
var checkboxList = mutableListOf<CheckboxItem>()
if (!lines.isEmpty() && lines.last().isNotEmpty()) {
lines.add("")
}
fun parseBuffer() {
if (buffer.isNotBlank()) {
parts.addAll(handleBuffer(type, buffer.toString(), language))
@@ -46,7 +51,7 @@ fun markdown(text: String): List<MarkdownPart> {
|| line.startsWith("#.")
) {
buffer.append("\n")
buffer.append(line.substring(2))
buffer.append(rawLine)
} else {
buffer.append(" ")
buffer.append(line)
@@ -56,7 +61,7 @@ fun markdown(text: String): List<MarkdownPart> {
type == MarkdownType.CHECKBOX_LIST -> {
if (line.isBlank()) {
if (buffer.isNotBlank()) {
addCheckbox(checkboxList, checkboxLine, buffer)
addCheckbox(checkboxList, checkboxLine, buffer, indentation)
}
parts.add(MarkdownPart.CheckboxList(checkboxList))
checkboxList = mutableListOf()
@@ -64,10 +69,11 @@ fun markdown(text: String): List<MarkdownPart> {
continue
} else if (line.startsWith("- [ ]") || line.startsWith("- [x]")) {
if (buffer.isNotBlank()) {
addCheckbox(checkboxList, checkboxLine, buffer)
addCheckbox(checkboxList, checkboxLine, buffer, indentation)
}
checkboxLine = index
buffer.append(line)
indentation = rawLine.countSpaces()
} else {
buffer.append(" ")
buffer.append(line)
@@ -133,7 +139,7 @@ fun markdown(text: String): List<MarkdownPart> {
parseBuffer()
type = MarkdownType.ORDERED_LIST
listIndex = 2
buffer.append(line.substring(2))
buffer.append(rawLine)
}
line.startsWith("- [ ]") || line.startsWith("- [x]") -> {
@@ -141,6 +147,7 @@ fun markdown(text: String): List<MarkdownPart> {
type = MarkdownType.CHECKBOX_LIST
checkboxLine = index
buffer.append(line)
indentation = rawLine.countSpaces()
}
line.startsWith("- ") || line.startsWith("* ") -> {
@@ -189,14 +196,14 @@ fun markdown(text: String): List<MarkdownPart> {
index++
}
if (type == MarkdownType.CHECKBOX_LIST) {
if (buffer.isNotBlank()) {
addCheckbox(checkboxList, checkboxLine, buffer)
}
parts.add(MarkdownPart.CheckboxList(checkboxList))
} else {
parseBuffer()
}
/* if (type == MarkdownType.CHECKBOX_LIST) {
if (buffer.isNotBlank()) {
addCheckbox(checkboxList, checkboxLine, buffer, indentation)
}
parts.add(MarkdownPart.CheckboxList(checkboxList))
} else {
parseBuffer()
}*/
return parts
}
@@ -204,14 +211,16 @@ fun markdown(text: String): List<MarkdownPart> {
private fun addCheckbox(
checkboxList: MutableList<CheckboxItem>,
index: Int,
buffer: StringBuilder
buffer: StringBuilder,
indentation: Int = 0
) {
if (buffer.length >= 5) {
checkboxList.add(
CheckboxItem(
index,
buffer.startsWith("- [x]"),
buffer.substring(5).trim()
buffer.substring(5).trim(),
indentation
)
)
} else {
@@ -248,17 +257,20 @@ private fun handleBuffer(
}
MarkdownType.ORDERED_LIST -> {
listOf(MarkdownPart.OrderedList(text.lines()))
val lines = text.lines()
val list = mutableListOf<ListItem>()
parseList(lines, list)
listOf(MarkdownPart.OrderedList(list))
}
MarkdownType.UNORDERED_LIST -> {
val lines = text.lines()
val list = mutableListOf<Pair<Int, String>>()
val list = mutableListOf<ListItem>()
parseList(lines, list)
for (line in lines) {
val indent: Int = line.countSpaces()
list.add((indent / 2) to line.substring(indent + 2))
}
listOf(MarkdownPart.UnorderedList(list))
}
@@ -270,3 +282,23 @@ private fun handleBuffer(
parseTable(text)
}
}
private fun parseList(
lines: List<String>,
list: MutableList<ListItem>
) {
val orderMap = mutableMapOf<Int, Int>()
for (line in lines) {
val indent: Int = line.countSpaces()
val order = orderMap.getOrPut(indent) { 1 }
list.add(
ListItem(
line.substring(indent + 2),
order,
indent
)
)
orderMap[indent] = order + 1
}
}

View File

@@ -68,6 +68,22 @@ class ParseTest {
printMarkdownParts(md)
}
@Test
fun testUnorderedList2() {
val input = """
- First
- Second
- 2.1
- 2.1.1
- 2.1.1
- 3
""".trimIndent()
val md = markdown(input)
printMarkdownParts(md)
}
@Test
fun testUnorderedListAlternative() {
val input = """
@@ -115,6 +131,9 @@ class ParseTest {
More text
-. Second
More text
-. Sub 1
-. Sub 2
-. Third
Another paragraph
""".trimIndent()
@@ -204,6 +223,33 @@ class ParseTest {
printMarkdownParts(md)
}
@Test
fun testCheckboxList4() {
val input = """
Dit is een text
- [ ] Not checked,
with some more text here
- [x] Checked!
- [x] Checked,
text it!
Nog 1:
- [ ] Not checked,
with some more text here
- [x] Checked!
- [x] Checked,
text it!
Meer text
""".trimIndent()
val md = markdown(input)
printMarkdownParts(md)
}
@Test
fun testCheckboxListError() {