generated from rnentjes/kotlin-server-web-empty
Add support for checkbox lists, indented code blocks, and escaped characters. Extend tests for new Markdown features. Update project version to 1.0.4.
This commit is contained in:
8
.idea/artifacts/markdown_parser_js_1_0_1.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_js_1_0_1.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-js-1.0.1">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-js-1.0.1.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_js_1_0_2.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_js_1_0_2.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-js-1.0.2">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-js-1.0.2.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_js_1_0_3.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_js_1_0_3.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-js-1.0.3">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-js-1.0.3.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_js_1_0_4_SNAPSHOT.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_js_1_0_4_SNAPSHOT.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-js-1.0.4-SNAPSHOT">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-js-1.0.4-SNAPSHOT.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jsMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_jvm_1_0_1.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_jvm_1_0_1.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-jvm-1.0.1">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-jvm-1.0.1.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_jvm_1_0_2.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_jvm_1_0_2.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-jvm-1.0.2">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-jvm-1.0.2.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_jvm_1_0_3.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_jvm_1_0_3.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-jvm-1.0.3">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-jvm-1.0.3.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
8
.idea/artifacts/markdown_parser_jvm_1_0_4_SNAPSHOT.xml
generated
Normal file
8
.idea/artifacts/markdown_parser_jvm_1_0_4_SNAPSHOT.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ArtifactManager">
|
||||||
|
<artifact type="jar" name="markdown-parser-jvm-1.0.4-SNAPSHOT">
|
||||||
|
<output-path>$PROJECT_DIR$/build/libs</output-path>
|
||||||
|
<root id="archive" name="markdown-parser-jvm-1.0.4-SNAPSHOT.jar">
|
||||||
|
<element id="module-output" name="markdown-parser.jvmMain" />
|
||||||
|
</root>
|
||||||
|
</artifact>
|
||||||
|
</component>
|
||||||
@@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "nl.astraeus"
|
group = "nl.astraeus"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package nl.astraeus.markdown.parser
|
package nl.astraeus.markdown.parser
|
||||||
|
|
||||||
|
data class CheckboxItem(
|
||||||
|
val line: Int,
|
||||||
|
val checked: Boolean,
|
||||||
|
val text: String,
|
||||||
|
)
|
||||||
|
|
||||||
sealed class MarkdownPart {
|
sealed class MarkdownPart {
|
||||||
|
|
||||||
data object NewLine : MarkdownPart()
|
data object NewLine : MarkdownPart()
|
||||||
|
|
||||||
data object PageBreak : MarkdownPart()
|
data object PageBreak : MarkdownPart()
|
||||||
|
|
||||||
sealed class ParagraphPart() {
|
sealed class ParagraphPart {
|
||||||
data class Text(
|
data class Text(
|
||||||
val text: String
|
val text: String
|
||||||
) : ParagraphPart()
|
) : ParagraphPart()
|
||||||
@@ -44,6 +50,10 @@ sealed class MarkdownPart {
|
|||||||
class InlineCode(
|
class InlineCode(
|
||||||
val text: String
|
val text: String
|
||||||
) : ParagraphPart()
|
) : ParagraphPart()
|
||||||
|
|
||||||
|
data class IndentedCode(
|
||||||
|
val code: String
|
||||||
|
) : ParagraphPart()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Paragraph(
|
data class Paragraph(
|
||||||
@@ -59,6 +69,10 @@ sealed class MarkdownPart {
|
|||||||
val lines: List<String>,
|
val lines: List<String>,
|
||||||
) : MarkdownPart()
|
) : MarkdownPart()
|
||||||
|
|
||||||
|
data class CheckboxList(
|
||||||
|
val lines: List<CheckboxItem>,
|
||||||
|
) : MarkdownPart()
|
||||||
|
|
||||||
data class OrderedList(
|
data class OrderedList(
|
||||||
val lines: List<String>,
|
val lines: List<String>,
|
||||||
) : MarkdownPart()
|
) : MarkdownPart()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import nl.astraeus.markdown.parser.MarkdownPart.ParagraphPart.*
|
|||||||
|
|
||||||
private enum class ParType {
|
private enum class ParType {
|
||||||
TEXT,
|
TEXT,
|
||||||
|
CODE,
|
||||||
LINK_LABEL,
|
LINK_LABEL,
|
||||||
LINK_URL,
|
LINK_URL,
|
||||||
LINK_TITLE,
|
LINK_TITLE,
|
||||||
@@ -30,6 +31,14 @@ private data class ParState(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val states = listOf(
|
private val states = listOf(
|
||||||
|
// Inline Code
|
||||||
|
ParState(ParType.TEXT, "`", ParType.INLINE_CODE) { data ->
|
||||||
|
Text(data[ParType.TEXT]!!)
|
||||||
|
},
|
||||||
|
ParState(ParType.INLINE_CODE, "`", ParType.TEXT) { data ->
|
||||||
|
InlineCode(data[ParType.INLINE_CODE]!!)
|
||||||
|
},
|
||||||
|
|
||||||
// Image with link
|
// Image with link
|
||||||
ParState(ParType.TEXT, "[![", ParType.LINK_IMAGE_ALT) { data ->
|
ParState(ParType.TEXT, "[![", ParType.LINK_IMAGE_ALT) { data ->
|
||||||
Text(data[ParType.TEXT]!!)
|
Text(data[ParType.TEXT]!!)
|
||||||
@@ -103,14 +112,7 @@ private val states = listOf(
|
|||||||
Text(data[ParType.TEXT]!!)
|
Text(data[ParType.TEXT]!!)
|
||||||
},
|
},
|
||||||
ParState(ParType.ITALIC, "*", ParType.TEXT) { data ->
|
ParState(ParType.ITALIC, "*", ParType.TEXT) { data ->
|
||||||
BoldItalic(data[ParType.ITALIC]!!)
|
Italic(data[ParType.ITALIC]!!)
|
||||||
},
|
|
||||||
|
|
||||||
ParState(ParType.TEXT, "`", ParType.INLINE_CODE) { data ->
|
|
||||||
Text(data[ParType.TEXT]!!)
|
|
||||||
},
|
|
||||||
ParState(ParType.INLINE_CODE, "`", ParType.TEXT) { data ->
|
|
||||||
InlineCode(data[ParType.INLINE_CODE]!!)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -125,9 +127,22 @@ fun parseParagraph(text: String): MarkdownPart.Paragraph {
|
|||||||
val data: ParagraphData = mutableMapOf()
|
val data: ParagraphData = mutableMapOf()
|
||||||
var index = 0
|
var index = 0
|
||||||
var activeStates = states.filter { it.fromType == type }
|
var activeStates = states.filter { it.fromType == type }
|
||||||
|
var escaped = false
|
||||||
|
|
||||||
while (index < text.length) {
|
while (index < text.length) {
|
||||||
var found = false
|
var found = false
|
||||||
|
val ch = text[index]
|
||||||
|
if (!escaped && ch == '\\') {
|
||||||
|
escaped = true
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
} else if (escaped) {
|
||||||
|
escaped = false
|
||||||
|
buffer.append(ch)
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for (state in activeStates) {
|
for (state in activeStates) {
|
||||||
if (state.fromType == type && text.test(index, state.text)) {
|
if (state.fromType == type && text.test(index, state.text)) {
|
||||||
data[state.fromType] = buffer.toString()
|
data[state.fromType] = buffer.toString()
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ enum class MarkdownType {
|
|||||||
PARAGRAPH,
|
PARAGRAPH,
|
||||||
ORDERED_LIST,
|
ORDERED_LIST,
|
||||||
UNORDERED_LIST,
|
UNORDERED_LIST,
|
||||||
|
CHECKBOX_LIST,
|
||||||
TABLE,
|
TABLE,
|
||||||
|
INDENTED_CODE,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun markdown(text: String): List<MarkdownPart> {
|
fun markdown(text: String): List<MarkdownPart> {
|
||||||
@@ -17,6 +19,7 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
|
|
||||||
var index = 0
|
var index = 0
|
||||||
val buffer = StringBuilder()
|
val buffer = StringBuilder()
|
||||||
|
val checkboxList = mutableListOf<CheckboxItem>()
|
||||||
|
|
||||||
fun parseBuffer() {
|
fun parseBuffer() {
|
||||||
if (buffer.isNotBlank()) {
|
if (buffer.isNotBlank()) {
|
||||||
@@ -33,7 +36,7 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
//println("BUFFER [${buffer.length}] TYPE ${type} \t LINE - ${line}")
|
//println("BUFFER [${buffer.length}] TYPE ${type} \t LINE - ${line}")
|
||||||
when {
|
when {
|
||||||
type == MarkdownType.ORDERED_LIST -> {
|
type == MarkdownType.ORDERED_LIST -> {
|
||||||
if (!line.startsWith("${listIndex++}.")) {
|
if (!line.startsWith("${listIndex++}.") && !line.startsWith("-.")) {
|
||||||
parseBuffer()
|
parseBuffer()
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@@ -42,6 +45,22 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type == MarkdownType.CHECKBOX_LIST -> {
|
||||||
|
if (!line.startsWith("- [ ]") && !line.startsWith("- [x]")) {
|
||||||
|
parts.add(MarkdownPart.CheckboxList(checkboxList))
|
||||||
|
parseBuffer()
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
checkboxList.add(
|
||||||
|
CheckboxItem(
|
||||||
|
index,
|
||||||
|
line.startsWith("- [x]"),
|
||||||
|
line.substring(5).trim()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type == MarkdownType.UNORDERED_LIST -> {
|
type == MarkdownType.UNORDERED_LIST -> {
|
||||||
if (!line.startsWith("- ") &&
|
if (!line.startsWith("- ") &&
|
||||||
!line.startsWith("* ")
|
!line.startsWith("* ")
|
||||||
@@ -69,6 +88,16 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
parseBuffer()
|
parseBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type == MarkdownType.INDENTED_CODE -> {
|
||||||
|
if (!rawLine.startsWith(" ")) {
|
||||||
|
parseBuffer()
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
buffer.append(line)
|
||||||
|
buffer.append("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
line.startsWith("```") -> {
|
line.startsWith("```") -> {
|
||||||
if (type != MarkdownType.CODE) {
|
if (type != MarkdownType.CODE) {
|
||||||
parseBuffer()
|
parseBuffer()
|
||||||
@@ -86,7 +115,7 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
line.startsWith("1.") -> {
|
line.startsWith("1.") || line.startsWith("-.") -> {
|
||||||
parseBuffer()
|
parseBuffer()
|
||||||
type = MarkdownType.ORDERED_LIST
|
type = MarkdownType.ORDERED_LIST
|
||||||
listIndex = 2
|
listIndex = 2
|
||||||
@@ -94,6 +123,18 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
buffer.append("\n")
|
buffer.append("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
line.startsWith("- [ ]") || line.startsWith("- [x]") -> {
|
||||||
|
parseBuffer()
|
||||||
|
type = MarkdownType.CHECKBOX_LIST
|
||||||
|
checkboxList.add(
|
||||||
|
CheckboxItem(
|
||||||
|
index,
|
||||||
|
line.startsWith("- [x]"),
|
||||||
|
line.substring(5).trim()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
line.startsWith("- ") || line.startsWith("* ") -> {
|
line.startsWith("- ") || line.startsWith("* ") -> {
|
||||||
parseBuffer()
|
parseBuffer()
|
||||||
type = MarkdownType.UNORDERED_LIST
|
type = MarkdownType.UNORDERED_LIST
|
||||||
@@ -120,6 +161,13 @@ fun markdown(text: String): List<MarkdownPart> {
|
|||||||
parts.add(MarkdownPart.Header(headerText, headerLevel))
|
parts.add(MarkdownPart.Header(headerText, headerLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rawLine.startsWith(" ") -> {
|
||||||
|
parseBuffer()
|
||||||
|
type = MarkdownType.INDENTED_CODE
|
||||||
|
buffer.append(line)
|
||||||
|
buffer.append("\n")
|
||||||
|
}
|
||||||
|
|
||||||
line == "[break]" -> {
|
line == "[break]" -> {
|
||||||
parseBuffer()
|
parseBuffer()
|
||||||
parts.add(MarkdownPart.PageBreak)
|
parts.add(MarkdownPart.PageBreak)
|
||||||
@@ -148,6 +196,10 @@ private fun handleBuffer(
|
|||||||
listOf(MarkdownPart.CodeBlock(text, language))
|
listOf(MarkdownPart.CodeBlock(text, language))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MarkdownType.INDENTED_CODE -> {
|
||||||
|
listOf(MarkdownPart.CodeBlock(text, "block"))
|
||||||
|
}
|
||||||
|
|
||||||
MarkdownType.PARAGRAPH -> {
|
MarkdownType.PARAGRAPH -> {
|
||||||
listOf(parseParagraph(text))
|
listOf(parseParagraph(text))
|
||||||
}
|
}
|
||||||
@@ -160,6 +212,10 @@ private fun handleBuffer(
|
|||||||
listOf(MarkdownPart.UnorderedList(text.lines()))
|
listOf(MarkdownPart.UnorderedList(text.lines()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MarkdownType.CHECKBOX_LIST -> {
|
||||||
|
error("Checkbox list is handled separately")
|
||||||
|
}
|
||||||
|
|
||||||
MarkdownType.TABLE -> {
|
MarkdownType.TABLE -> {
|
||||||
parseTable(text)
|
parseTable(text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,38 @@ class ParseTest {
|
|||||||
printMarkdownParts(md)
|
printMarkdownParts(md)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIndentedCode() {
|
||||||
|
val input = """
|
||||||
|
Dit is een text
|
||||||
|
|
||||||
|
Code block
|
||||||
|
Code block
|
||||||
|
|
||||||
|
Meer text
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val md = markdown(input)
|
||||||
|
|
||||||
|
printMarkdownParts(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCheckboxList() {
|
||||||
|
val input = """
|
||||||
|
Dit is een text
|
||||||
|
|
||||||
|
- [ ] Not checked
|
||||||
|
- [x] Checked
|
||||||
|
|
||||||
|
Meer text
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val md = markdown(input)
|
||||||
|
|
||||||
|
printMarkdownParts(md)
|
||||||
|
}
|
||||||
|
|
||||||
private fun printMarkdownParts(md: List<MarkdownPart>) {
|
private fun printMarkdownParts(md: List<MarkdownPart>) {
|
||||||
for (part in md) {
|
for (part in md) {
|
||||||
if (part is MarkdownPart.Paragraph) {
|
if (part is MarkdownPart.Paragraph) {
|
||||||
|
|||||||
@@ -19,6 +19,31 @@ class TestParagraph {
|
|||||||
assertTrue(result.parts[2] is MarkdownPart.ParagraphPart.Text)
|
assertTrue(result.parts[2] is MarkdownPart.ParagraphPart.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCode() {
|
||||||
|
val input = "Text `code` Text"
|
||||||
|
|
||||||
|
val result = parseParagraph(input)
|
||||||
|
|
||||||
|
assertEquals(3, result.parts.size)
|
||||||
|
|
||||||
|
assertTrue(result.parts[0] is MarkdownPart.ParagraphPart.Text)
|
||||||
|
assertTrue(result.parts[1] is MarkdownPart.ParagraphPart.InlineCode)
|
||||||
|
assertTrue(result.parts[2] is MarkdownPart.ParagraphPart.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIndentedCode() {
|
||||||
|
val input = "Text \n code\n line 2\n\nText"
|
||||||
|
|
||||||
|
val result = parseParagraph(input)
|
||||||
|
|
||||||
|
assertEquals(3, result.parts.size)
|
||||||
|
|
||||||
|
assertTrue(result.parts[0] is MarkdownPart.ParagraphPart.Text)
|
||||||
|
assertTrue(result.parts[1] is MarkdownPart.ParagraphPart.IndentedCode)
|
||||||
|
assertTrue(result.parts[2] is MarkdownPart.ParagraphPart.Text)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLink() {
|
fun testLink() {
|
||||||
@@ -30,4 +55,28 @@ class TestParagraph {
|
|||||||
|
|
||||||
assertTrue(result.parts[0] is MarkdownPart.ParagraphPart.Link)
|
assertTrue(result.parts[0] is MarkdownPart.ParagraphPart.Link)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEscaped() {
|
||||||
|
val input = "Test \\`escaped\\` text"
|
||||||
|
|
||||||
|
val result = parseParagraph(input)
|
||||||
|
|
||||||
|
assertEquals(1, result.parts.size)
|
||||||
|
|
||||||
|
assertTrue(result.parts[0] is MarkdownPart.ParagraphPart.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFormatting() {
|
||||||
|
val input = "test **bold**, *italic*, ***bold and italic***, ~~strikethrough~~, and `code`."
|
||||||
|
|
||||||
|
val result = parseParagraph(input)
|
||||||
|
|
||||||
|
assertEquals(1, result.parts.size)
|
||||||
|
|
||||||
|
assertTrue(result.parts[0] is MarkdownPart.ParagraphPart.Text)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user