summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/kotlin/moe/nea/blog/md/BlockList.kt12
-rw-r--r--src/main/kotlin/moe/nea/blog/md/LinePreProcessor.kt5
-rw-r--r--src/main/kotlin/moe/nea/blog/md/ListParser.kt60
-rw-r--r--src/main/kotlin/moe/nea/blog/md/MDList.kt24
-rw-r--r--src/main/kotlin/moe/nea/blog/md/MarkdownParser.kt55
-rw-r--r--src/main/kotlin/moe/nea/blog/md/ParagraphParser.kt2
-rw-r--r--src/test/kotlin/moe/nea/blog/md/test/ListTest.kt104
-rw-r--r--src/test/kotlin/moe/nea/blog/md/test/TestItalics.kt1
8 files changed, 255 insertions, 8 deletions
diff --git a/src/main/kotlin/moe/nea/blog/md/BlockList.kt b/src/main/kotlin/moe/nea/blog/md/BlockList.kt
new file mode 100644
index 0000000..ce51a58
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/md/BlockList.kt
@@ -0,0 +1,12 @@
+package moe.nea.blog.md
+
+import java.io.PrintStream
+
+data class BlockList(val elements: List<MarkdownBlock>) : MarkdownBlock {
+ override fun debugFormat(indent: Int, printStream: PrintStream) {
+ elements.forEach {
+ it.debugFormat(indent, printStream)
+ }
+ }
+
+}
diff --git a/src/main/kotlin/moe/nea/blog/md/LinePreProcessor.kt b/src/main/kotlin/moe/nea/blog/md/LinePreProcessor.kt
new file mode 100644
index 0000000..cb9d81f
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/md/LinePreProcessor.kt
@@ -0,0 +1,5 @@
+package moe.nea.blog.md
+
+interface LinePreProcessor {
+ fun preprocess(lineIndex: Int, line: String): String?
+}
diff --git a/src/main/kotlin/moe/nea/blog/md/ListParser.kt b/src/main/kotlin/moe/nea/blog/md/ListParser.kt
new file mode 100644
index 0000000..7d1e63b
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/md/ListParser.kt
@@ -0,0 +1,60 @@
+package moe.nea.blog.md
+
+import moe.nea.blog.util.indentSize
+
+object ListParser : BlockParser {
+ override fun detect(line: String): Boolean {
+ return line.matches(" *[*+-] .*".toRegex())
+ }
+
+ override fun parse(parser: MarkdownParser): MarkdownBlock {
+ val line = parser.peekLine()!!
+ val indentSize = line.indentSize()!!
+ val prefix = line.substring(0, indentSize + 2)
+ var bypassLineIndex = parser.getLineIndex()
+ val indentDepth = parser.getIndent()
+ val processor = object : LinePreProcessor {
+ override fun preprocess(lineIndex: Int, line: String): String? {
+ if (line.substring(indentDepth).startsWith(prefix)) {
+ if (bypassLineIndex == lineIndex) {
+ return line.substring(0, indentDepth) +
+ " ".repeat(indentSize + 2) +
+ line.substring(indentDepth + indentSize + 2)
+ } else {
+ return null
+ }
+ }
+ return line
+ }
+ }
+ parser.pushPreProcessor(processor)
+ parser.pushIndent(indentSize + 2)
+
+ val listItems = mutableListOf<MarkdownBlock>()
+
+ while (true) {
+ bypassLineIndex = parser.getLineIndex()
+ val elements = mutableListOf<MarkdownBlock>()
+ while (true) {
+ val child = parser.readChildBlock() ?: break
+ elements.add(child)
+ }
+ parser.popPreProcessor()
+ parser.popIndent()
+ val realLine = parser.peekLine()
+ parser.pushPreProcessor(processor)
+ parser.pushIndent(indentSize + 2)
+ listItems.add(parser.mergeBlocks(elements))
+ if (realLine == null || !realLine.startsWith(prefix)) {
+ break
+ }
+ }
+
+ parser.popIndent()
+ parser.popPreProcessor()
+ return MDList(listItems)
+ }
+
+ override val prio: Int
+ get() = 10
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/md/MDList.kt b/src/main/kotlin/moe/nea/blog/md/MDList.kt
new file mode 100644
index 0000000..6fd0f74
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/md/MDList.kt
@@ -0,0 +1,24 @@
+package moe.nea.blog.md
+
+import moe.nea.blog.util.indent
+import java.io.PrintStream
+
+data class MDList(
+ val elements: List<MarkdownBlock>
+) : MarkdownBlock {
+ override fun debugFormat(indent: Int, printStream: PrintStream) {
+ printStream.indent(indent)
+ printStream.println("<list>")
+
+ elements.forEach {
+ printStream.indent(indent + 2)
+ printStream.println("<element>")
+ it.debugFormat(indent + 4, printStream)
+ printStream.indent(indent + 2)
+ printStream.println("</element>")
+ }
+
+ printStream.indent(indent)
+ printStream.println("</list>")
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/md/MarkdownParser.kt b/src/main/kotlin/moe/nea/blog/md/MarkdownParser.kt
index ad818dd..aaac6a1 100644
--- a/src/main/kotlin/moe/nea/blog/md/MarkdownParser.kt
+++ b/src/main/kotlin/moe/nea/blog/md/MarkdownParser.kt
@@ -12,6 +12,8 @@ class MarkdownParser(source: String) {
private val blockParsers = mutableListOf<BlockParser>()
private val inlineParsers = mutableListOf<InlineParser>()
+ private val preprocessors = Stack<LinePreProcessor>()
+ private var peekedLine: String? = null
fun findParserFor(line: String): BlockParser? {
return blockParsers.filter { it.detect(line) }
@@ -24,30 +26,58 @@ class MarkdownParser(source: String) {
return blockParser.parse(this)
}
+ fun pushPreProcessor(preProcessor: LinePreProcessor) {
+ peekedLine = null
+ preprocessors.push(preProcessor)
+ }
+
+ fun popPreProcessor() {
+ peekedLine = null
+ preprocessors.pop()
+ }
+
+ fun preProcessLine(string: String): String? {
+ var acc = string
+ for (processor in preprocessors) {
+ acc = processor.preprocess(lineIndex, acc) ?: return null
+ }
+ return acc
+ }
+
fun pushIndent(newIndent: Int) {
- require(newIndent > blockIndents)
indentStack.push(blockIndents)
- blockIndents = newIndent
+ blockIndents += newIndent
+ peekedLine = null
}
fun popIndent() {
blockIndents = indentStack.pop()
+ peekedLine = null
+ }
+
+ fun unpeekLine() {
+ peekedLine = null
}
fun consumeLine(): String? {
val line = peekLine()
- if (line != null)
+ if (line != null) {
+ peekedLine = null
lineIndex++
+ }
return line
}
fun peekLine(): String? {
if (lineIndex !in lines.indices) return null
- val line = lines[lineIndex]
+ val line = peekedLine ?: preProcessLine(lines[lineIndex]) ?: return null
+ peekedLine = line
val indent = line.indentSize()
- if (indent != null && indent < blockIndents)
+ if (indent != null && indent < blockIndents) {
+ peekedLine = null
return null
- return line.substring(blockIndents)
+ }
+ return line.drop(blockIndents)
}
fun parseInlineTextUntil(
@@ -139,8 +169,21 @@ class MarkdownParser(source: String) {
fun addDefaultParsers() {
blockParsers.add(CodeBlockParser)
blockParsers.add(HeaderParser)
+ blockParsers.add(ListParser)
inlineParsers.add(ItalicsParser)
inlineParsers.add(LinkParser)
}
+
+ fun getLineIndex(): Int {
+ return lineIndex
+ }
+
+ fun mergeBlocks(elements: List<MarkdownBlock>): MarkdownBlock {
+ return elements.singleOrNull() ?: BlockList(elements)
+ }
+
+ fun getIndent(): Int {
+ return blockIndents
+ }
}
diff --git a/src/main/kotlin/moe/nea/blog/md/ParagraphParser.kt b/src/main/kotlin/moe/nea/blog/md/ParagraphParser.kt
index 328c49b..5e51786 100644
--- a/src/main/kotlin/moe/nea/blog/md/ParagraphParser.kt
+++ b/src/main/kotlin/moe/nea/blog/md/ParagraphParser.kt
@@ -17,7 +17,7 @@ object ParagraphParser : BlockParser {
} else {
emptyLineCount = 0
}
- if (emptyLineCount == 2) {
+ if (emptyLineCount == 1) {
break
}
text += " $trimmedLine"
diff --git a/src/test/kotlin/moe/nea/blog/md/test/ListTest.kt b/src/test/kotlin/moe/nea/blog/md/test/ListTest.kt
new file mode 100644
index 0000000..0ac8f1e
--- /dev/null
+++ b/src/test/kotlin/moe/nea/blog/md/test/ListTest.kt
@@ -0,0 +1,104 @@
+package moe.nea.blog.md.test
+
+import kotlin.test.Test
+
+class ListTest : MarkdownTest() {
+ @Test
+ fun testBasicList() {
+ assertDocumentFormat(
+ """
+ Some Text:
+ <list>
+ <element>
+ First
+ </element>
+ <element>
+ Second
+ </element>
+ </list>
+ """.trimIndent(),
+ """
+ Some Text:
+ - First
+ - Second
+ """.trimIndent()
+ )
+ }
+ @Test
+ fun testBlockContinuation() {
+ assertDocumentFormat(
+ """
+ Some Text:
+ <list>
+ <element>
+ First More First
+ </element>
+ <element>
+ Second
+ More Second
+ </element>
+ </list>
+ """.trimIndent(),
+ """
+ Some Text:
+ - First
+ More First
+ - Second
+
+ More Second
+ """.trimIndent()
+ )
+ }
+ @Test
+ fun testNestedLists() {
+ assertDocumentFormat(
+ """
+ Some Text:
+ <list>
+ <element>
+ First
+ <list>
+ <element>
+ Another List
+ </element>
+ </list>
+ </element>
+ <element>
+ Second
+ </element>
+ </list>
+ """.trimIndent(),
+ """
+ Some Text:
+ - First
+ - Another List
+ - Second
+ """.trimIndent()
+ )
+ }
+ @Test
+ fun testCodeBlockInList() {
+ assertDocumentFormat(
+ """
+ <list>
+ <element>
+ <code language=java>
+ public class Test {
+
+ public static void main(String...args) {}
+ }
+ </code>
+ </element>
+ </list>
+ """.trimIndent(),
+ """
+ - ```java
+ public class Test {
+
+ public static void main(String...args) {}
+ }
+ ```
+ """.trimIndent()
+ )
+ }
+} \ No newline at end of file
diff --git a/src/test/kotlin/moe/nea/blog/md/test/TestItalics.kt b/src/test/kotlin/moe/nea/blog/md/test/TestItalics.kt
index f4c1669..dee92bd 100644
--- a/src/test/kotlin/moe/nea/blog/md/test/TestItalics.kt
+++ b/src/test/kotlin/moe/nea/blog/md/test/TestItalics.kt
@@ -14,7 +14,6 @@ class TestItalics : MarkdownTest() {
assertInlineFormat("<i><b>both</b> just italics</i>", "***both** just italics*")
}
-
@Test
fun otherMarkdownInItalics() {
assertInlineFormat(