1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
package moe.nea.blog.gen
import moe.nea.blog.md.Begin
import moe.nea.blog.md.Bold
import moe.nea.blog.md.CodeBlock
import moe.nea.blog.md.Document
import moe.nea.blog.md.FormatSequence
import moe.nea.blog.md.Header
import moe.nea.blog.md.Italics
import moe.nea.blog.md.Link
import moe.nea.blog.md.MDList
import moe.nea.blog.md.MarkdownElement
import moe.nea.blog.md.Paragraph
import moe.nea.blog.md.Whitespace
import moe.nea.blog.md.Word
import kotlin.reflect.KClass
class MD2HtmlGenerator {
private val generators = mutableMapOf<KClass<out MarkdownElement>, HtmlFragmentGenerator<MarkdownElement>>()
fun <T : MarkdownElement> getGeneratorFor(kClass: KClass<out T>) = generators[kClass] as HtmlFragmentGenerator<T>?
inline fun <reified T : MarkdownElement> registerFragmentGenerator(noinline outputter: HtmlDsl.(generator: MD2HtmlGenerator, node: T) -> Unit) {
registerGeneratorFor(T::class) { gen, node ->
HtmlDsl()
.apply { outputter.invoke(this, gen, node) }
.intoFragment()
}
}
inline fun <reified T : MarkdownElement> registerGeneratorFor(outputter: HtmlFragmentGenerator<T>) {
registerGeneratorFor(T::class, outputter)
}
fun <T : MarkdownElement> registerGeneratorFor(kClass: KClass<T>, outputter: HtmlFragmentGenerator<T>) {
generators[kClass] = outputter as HtmlFragmentGenerator<MarkdownElement>
}
fun registerDefaultGenerators() {
registerGeneratorFor<Begin> { _, _ -> HtmlFragment.empty() }
registerFragmentGenerator<Header> { gen, header ->
element("h${header.level}", mapOf()) {
+header.text
}
}
registerFragmentGenerator<Bold> { gen, node ->
element("b", mapOf(), gen.generateHtml(node.inner))
}
registerFragmentGenerator<Italics> { generator, node ->
element("em", mapOf(), generator.generateHtml(node.inner))
}
registerFragmentGenerator<MDList> { generator, node ->
element("ul", mapOf()) {
for (item in node.elements) {
element("li", mapOf()) {
+generator.generateHtml(item)
}
}
}
}
registerFragmentGenerator<Link> { generator, node ->
element("a", mapOf("href" to node.target), generator.generateHtml(node.label ?: Begin()))
}
registerFragmentGenerator<Paragraph> { generator, node ->
element("p", mapOf(), generator.generateHtml(node.format))
}
registerFragmentGenerator<CodeBlock> { generator, node ->
element("pre", mapOf()) {
element("code", mapOf("class" to "language-${node.language}")) {
append(node.lines.joinToString("\n"))
}
}
}
registerFragmentGenerator<FormatSequence> { generator, node ->
for (markdownFormat in node.list) {
append(generator.generateHtml(markdownFormat))
}
}
registerFragmentGenerator<Word> { generator, node ->
append(node.text)
}
registerFragmentGenerator<Whitespace> { generator, node ->
append(" ")
}
registerFragmentGenerator<Document> { generator, node ->
for (markdownBlock in node.list) {
append(generator.generateHtml(markdownBlock))
}
}
}
fun <T : MarkdownElement> generateHtml(node: T): HtmlFragment {
val gen = getGeneratorFor(node::class) ?: error("Missing html generator for $node")
return gen.generateHtml(this, node)
}
}
|