summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-05-02 16:05:38 +0200
committerLinnea Gräf <nea@nea.moe>2024-05-02 16:05:38 +0200
commit5316d761132a28df639eb70099c8af367f792733 (patch)
tree2a16543bb74216b7a853332bf7382dbfbaabcc5d
parent5f0552a7ea251a52fb89f7886f6a8dd48ce566d5 (diff)
downloadblog-infra-5316d761132a28df639eb70099c8af367f792733.tar.gz
blog-infra-5316d761132a28df639eb70099c8af367f792733.tar.bz2
blog-infra-5316d761132a28df639eb70099c8af367f792733.zip
Split up html generation
-rw-r--r--.github/workflows/test.yml20
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--src/main/kotlin/moe/nea/blog/gen/HtmlDsl.kt50
-rw-r--r--src/main/kotlin/moe/nea/blog/gen/HtmlDslMarker.kt3
-rw-r--r--src/main/kotlin/moe/nea/blog/gen/HtmlFragment.kt18
-rw-r--r--src/main/kotlin/moe/nea/blog/gen/HtmlFragmentGenerator.kt7
-rw-r--r--src/main/kotlin/moe/nea/blog/gen/HtmlGenerator.kt149
-rw-r--r--src/main/kotlin/moe/nea/blog/gen/MD2HtmlGenerator.kt87
-rw-r--r--src/test/kotlin/moe/nea/blog/gen/HtmlTest.kt101
9 files changed, 240 insertions, 197 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..a9ea5f6
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,20 @@
+name: Test
+
+on:
+ push:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout sources
+ uses: actions/checkout@v4
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: 21
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v3
+ - name: Test with Gradle
+ run: ./gradlew test
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3fa8f86..b82aa23 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/src/main/kotlin/moe/nea/blog/gen/HtmlDsl.kt b/src/main/kotlin/moe/nea/blog/gen/HtmlDsl.kt
new file mode 100644
index 0000000..4574b50
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/gen/HtmlDsl.kt
@@ -0,0 +1,50 @@
+package moe.nea.blog.gen
+
+@HtmlDslMarker
+class HtmlDsl {
+ val parts = mutableListOf<HtmlFragment>()
+
+ fun append(string: String) {
+ parts.add(HtmlFragment.ofUnescaped(string))
+ }
+
+ fun append(fragment: HtmlFragment) {
+ parts.add(fragment)
+ }
+
+ fun appendPreEscaped(markup: String) {
+ append(HtmlFragment.ofPreEscaped(markup))
+ }
+
+ operator fun String.unaryPlus() = append(this)
+ operator fun HtmlFragment.unaryPlus() = append(this)
+
+ fun element(name: String, attributes: Map<String, String>, block: HtmlDsl.() -> Unit) {
+ element(name, attributes, HtmlDsl().also(block).intoFragment())
+ }
+
+ fun element(name: String, attributes: Map<String, String>, fragment: HtmlFragment) {
+ appendPreEscaped(("<"))
+ append(name)
+
+ for ((key, value) in attributes) {
+ appendPreEscaped((" "))
+ append(key)
+ appendPreEscaped(("=\""))
+ append(value)
+ appendPreEscaped(("\""))
+ }
+
+ appendPreEscaped((">"))
+
+ append(fragment)
+
+ appendPreEscaped(("</"))
+ append(name)
+ appendPreEscaped((">"))
+ }
+
+ fun intoFragment(): HtmlFragment {
+ return HtmlFragment.ofPreEscaped(parts.joinToString("") { it.text })
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/gen/HtmlDslMarker.kt b/src/main/kotlin/moe/nea/blog/gen/HtmlDslMarker.kt
new file mode 100644
index 0000000..e30a516
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/gen/HtmlDslMarker.kt
@@ -0,0 +1,3 @@
+package moe.nea.blog.gen
+
+annotation class HtmlDslMarker \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/gen/HtmlFragment.kt b/src/main/kotlin/moe/nea/blog/gen/HtmlFragment.kt
new file mode 100644
index 0000000..0b75182
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/gen/HtmlFragment.kt
@@ -0,0 +1,18 @@
+package moe.nea.blog.gen
+
+class HtmlFragment private constructor(val text: String) {
+ companion object {
+ fun ofUnescaped(text: String) = HtmlFragment(
+ text.replace("&", "&amp;")
+ .replace("<", "&lt;")
+ .replace(">", "&gt;")
+ .replace("\"", "&quot;")
+ .replace("'", "&#39;")
+ )
+
+ fun ofPreEscaped(text: String) = HtmlFragment(text)
+ fun empty(): HtmlFragment {
+ return HtmlFragment("")
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/gen/HtmlFragmentGenerator.kt b/src/main/kotlin/moe/nea/blog/gen/HtmlFragmentGenerator.kt
new file mode 100644
index 0000000..3ba4f79
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/gen/HtmlFragmentGenerator.kt
@@ -0,0 +1,7 @@
+package moe.nea.blog.gen
+
+import moe.nea.blog.md.MarkdownElement
+
+fun interface HtmlFragmentGenerator<T : MarkdownElement> {
+ fun generateHtml(htmlGenerator: MD2HtmlGenerator, node: T): HtmlFragment
+} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/gen/HtmlGenerator.kt b/src/main/kotlin/moe/nea/blog/gen/HtmlGenerator.kt
deleted file mode 100644
index ef0858a..0000000
--- a/src/main/kotlin/moe/nea/blog/gen/HtmlGenerator.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-package moe.nea.blog.gen
-
-import moe.nea.blog.md.*
-import kotlin.reflect.KClass
-
-fun interface HtmlFragmentGenerator<T : MarkdownElement> {
- fun generateHtml(htmlGenerator: HtmlGenerator, node: T): HtmlFragment
-}
-
-
-class HtmlFragment private constructor(val text: String) {
- companion object {
- fun ofUnescaped(text: String) = HtmlFragment(
- text.replace("&", "&amp;")
- .replace("<", "&lt;")
- .replace(">", "&gt;")
- .replace("\"", "&quot;")
- .replace("'", "&#39;")
- )
-
- fun ofPreEscaped(text: String) = HtmlFragment(text)
- fun empty(): HtmlFragment {
- return HtmlFragment("")
- }
- }
-}
-
-annotation class HtmlDslMarker
-
-@HtmlDslMarker
-class HtmlDsl {
- val parts = mutableListOf<HtmlFragment>()
-
- fun append(string: String) {
- parts.add(HtmlFragment.ofUnescaped(string))
- }
-
- fun append(fragment: HtmlFragment) {
- parts.add(fragment)
- }
-
- fun appendPreEscaped(markup: String) {
- append(HtmlFragment.ofPreEscaped(markup))
- }
-
- operator fun String.unaryPlus() = append(this)
- operator fun HtmlFragment.unaryPlus() = append(this)
-
- fun element(name: String, attributes: Map<String, String>, block: HtmlDsl.() -> Unit) {
- element(name, attributes, HtmlDsl().also(block).intoFragment())
- }
-
- fun element(name: String, attributes: Map<String, String>, fragment: HtmlFragment) {
- appendPreEscaped(("<"))
- append(name)
-
- for ((key, value) in attributes) {
- appendPreEscaped((" "))
- append(key)
- appendPreEscaped(("=\""))
- append(value)
- appendPreEscaped(("\""))
- }
-
- appendPreEscaped((">"))
-
- append(fragment)
-
- appendPreEscaped(("</"))
- append(name)
- appendPreEscaped((">"))
- }
-
- fun intoFragment(): HtmlFragment {
- return HtmlFragment.ofPreEscaped(parts.joinToString("") { it.text })
- }
-}
-
-
-class HtmlGenerator {
- 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: HtmlGenerator, 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)
- }
-} \ No newline at end of file
diff --git a/src/main/kotlin/moe/nea/blog/gen/MD2HtmlGenerator.kt b/src/main/kotlin/moe/nea/blog/gen/MD2HtmlGenerator.kt
new file mode 100644
index 0000000..0077469
--- /dev/null
+++ b/src/main/kotlin/moe/nea/blog/gen/MD2HtmlGenerator.kt
@@ -0,0 +1,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)
+ }
+} \ No newline at end of file
diff --git a/src/test/kotlin/moe/nea/blog/gen/HtmlTest.kt b/src/test/kotlin/moe/nea/blog/gen/HtmlTest.kt
index 07c5a24..4b76a5f 100644
--- a/src/test/kotlin/moe/nea/blog/gen/HtmlTest.kt
+++ b/src/test/kotlin/moe/nea/blog/gen/HtmlTest.kt
@@ -1,56 +1,63 @@
package moe.nea.blog.gen
-import moe.nea.blog.md.*
+import moe.nea.blog.md.Bold
+import moe.nea.blog.md.FormatSequence
+import moe.nea.blog.md.MarkdownElement
+import moe.nea.blog.md.MarkdownParser
+import moe.nea.blog.md.Paragraph
+import moe.nea.blog.md.Whitespace
+import moe.nea.blog.md.Word
import kotlin.test.Test
import kotlin.test.assertEquals
class HtmlTest {
- fun assertGenerator(generatedHtml: String, markdown: MarkdownElement) {
- val generator = HtmlGenerator()
- generator.registerDefaultGenerators()
- assertEquals(generatedHtml, generator.generateHtml(markdown).text)
- }
-
- fun assertGeneratorMD(generatedHtml: String, text: String) {
- val generator = HtmlGenerator()
- generator.registerDefaultGenerators()
- assertEquals(generatedHtml, generator.generateHtml(MarkdownParser(text).also { it.addDefaultParsers() }.readDocument()).text)
- }
-
- @Test
- fun testBiggerFile() {
- assertGeneratorMD(
- """
- <h1>Hello World</h1><p></p><p><b><em>lol</em> hehe</b></p><pre><code class="language-java">public class ObjectControllerFactoryManagerProvider&lt;T extends ObjectControllerFactoryManager&lt;T&gt;&gt; {
-
- }</code></pre>
- """.trimIndent(),
- """
- # Hello World
-
- ***lol* hehe**
-
- ```java
- public class ObjectControllerFactoryManagerProvider<T extends ObjectControllerFactoryManager<T>> {
-
- }
- ```
- """.trimIndent()
- )
- }
-
- @Test
- fun testBold() {
- assertGenerator("<b>Hii</b>", Bold(Word("Hii")))
- }
-
- @Test
- fun testParagraphs() {
- assertGenerator(
- "<p>Test <b>Whatever</b></p>",
- Paragraph(FormatSequence(Word("Test"), Whitespace(), Bold(Word("Whatever"))))
- )
- }
+ fun assertGenerator(generatedHtml: String, markdown: MarkdownElement) {
+ val generator = MD2HtmlGenerator()
+ generator.registerDefaultGenerators()
+ assertEquals(generatedHtml, generator.generateHtml(markdown).text)
+ }
+
+ fun assertGeneratorMD(generatedHtml: String, text: String) {
+ val generator = MD2HtmlGenerator()
+ generator.registerDefaultGenerators()
+ assertEquals(generatedHtml,
+ generator.generateHtml(MarkdownParser(text).also { it.addDefaultParsers() }.readDocument()).text)
+ }
+
+ @Test
+ fun testBiggerFile() {
+ assertGeneratorMD(
+ """
+ |<h1>Hello World</h1><p></p><p><b><em>lol</em> hehe</b></p><pre><code class="language-java">public class ObjectControllerFactoryManagerProvider&lt;T extends ObjectControllerFactoryManager&lt;T&gt;&gt; {
+ | public ObjectControllerFactoryManagerProvider() {}
+ |}</code></pre>
+ """.trimMargin(),
+ """
+ |# Hello World
+ |
+ |***lol* hehe**
+ |
+ |```java
+ |public class ObjectControllerFactoryManagerProvider<T extends ObjectControllerFactoryManager<T>> {
+ | public ObjectControllerFactoryManagerProvider() {}
+ |}
+ |```
+ """.trimMargin()
+ )
+ }
+
+ @Test
+ fun testBold() {
+ assertGenerator("<b>Hii</b>", Bold(Word("Hii")))
+ }
+
+ @Test
+ fun testParagraphs() {
+ assertGenerator(
+ "<p>Test <b>Whatever</b></p>",
+ Paragraph(FormatSequence(Word("Test"), Whitespace(), Bold(Word("Whatever"))))
+ )
+ }
} \ No newline at end of file