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
|
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.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<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)
}
}
|