diff --git a/.gitignore b/.gitignore index ccb0c56..8d9cccd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ out/ output/ bin/ libs/ +doc_wiki/ .classpath .project diff --git a/common/build.gradle b/common/build.gradle index d1929f3..fe521a2 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -60,4 +60,11 @@ publishing { } } } +} + +tasks.register("genDoc", JavaExec) { + group = ApplicationPlugin.APPLICATION_GROUP + classpath = sourceSets.main.runtimeClasspath + mainClass = 'org.valkyrienskies.tournament.doc.DocumentedKt' + args = [rootDir.path] } \ No newline at end of file diff --git a/common/src/main/kotlin/org/valkyrienskies/tournament/blocks/BallastBlock.kt b/common/src/main/kotlin/org/valkyrienskies/tournament/blocks/BallastBlock.kt index 85fca6e..1fdf58f 100644 --- a/common/src/main/kotlin/org/valkyrienskies/tournament/blocks/BallastBlock.kt +++ b/common/src/main/kotlin/org/valkyrienskies/tournament/blocks/BallastBlock.kt @@ -9,6 +9,9 @@ import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.StateDefinition import net.minecraft.world.level.block.state.properties.BlockStateProperties.POWER import net.minecraft.world.level.material.Material +import org.valkyrienskies.tournament.doc.Doc +import org.valkyrienskies.tournament.doc.Documented +import org.valkyrienskies.tournament.doc.documentation class BallastBlock : Block( Properties.of(Material.STONE) @@ -50,4 +53,15 @@ class BallastBlock : Block( val signal = level.getBestNeighborSignal(pos) level.setBlock(pos, state.setValue(POWER, signal), 2) } + + class DocImpl: Documented { + override fun getDoc() = documentation { + page("Ballast") + .kind(Doc.Kind.BLOCK) + .summary("A block that has a redstone adjustable weight") + .section("Usage") { + content("Power with redstone to increase weight") + } + } + } } diff --git a/common/src/main/kotlin/org/valkyrienskies/tournament/doc/Documented.kt b/common/src/main/kotlin/org/valkyrienskies/tournament/doc/Documented.kt new file mode 100644 index 0000000..fd6d43a --- /dev/null +++ b/common/src/main/kotlin/org/valkyrienskies/tournament/doc/Documented.kt @@ -0,0 +1,176 @@ +package org.valkyrienskies.tournament.doc + +import com.google.common.reflect.ClassPath +import java.io.File +import java.util.stream.Collectors + +data class Doc( + val kind: Kind, + val name: String, + val content: String, +) { + enum class Kind(val path: String) { + NONE(""), + + BLOCK("blocks/"), + ITEM("items/"), + CC_API("cc/"), + ADVANCEMENT("advancements/"), + } +} + +interface Documented { + fun getDoc(): (DocumentationContext.Format) -> List +} + +class DocumentationContext { + private val pages = mutableListOf() + + fun page(title: String): Page { + val page = Page(vtitle = title) + pages.add(page) + return page + } + + class Page( + internal var vtitle: String, + internal var vkind: Doc.Kind = Doc.Kind.NONE, + internal var vdesc: MutableList = mutableListOf(), + internal val vsection: MutableList
= mutableListOf() + ) { + fun kind(kind: Doc.Kind): Page = + this.also { it.vkind = kind } + + fun summary(str: String): Page = + this.also { it.vdesc += str} + + fun section(name: String, fn: Section.() -> Unit): Page = + this.also { + val section = Section(name) + it.vsection.add(section) + fn(section) + } + } + + data class Section( + internal var vtitle: String, + internal var vcontent: MutableList> = mutableListOf(), + ) { + enum class ContentKind { + TEXT, + CODE, + } + + fun content(str: String): Section = + this.also { it.vcontent += str to ContentKind.TEXT } + + fun codeSnippet(str: String): Section = + this.also { it.vcontent += str to ContentKind.CODE } + } + + enum class Format { + GH_MARKDOWN, + MEDIAWIKI, + } + + internal fun build(fmt: Format): List = + pages.map { + val sb = StringBuilder() + + when (fmt) { + Format.MEDIAWIKI -> { + sb.append("== ") + sb.append(it.vtitle) + sb.appendLine(" ==") + } + Format.GH_MARKDOWN -> { + sb.append("# ") + sb.appendLine(it.vtitle) + } + } + + it.vdesc.forEachIndexed { index, s -> + if (index > 0) + sb.appendLine() + sb.append(s) + sb.appendLine() + } + + sb.appendLine() + + it.vsection.forEach { section -> + when (fmt) { + Format.MEDIAWIKI -> { + sb.append("=== ") + sb.append(section.vtitle) + sb.appendLine(" ===") + } + Format.GH_MARKDOWN -> { + sb.append("## ") + sb.appendLine(section.vtitle) + } + } + + section.vcontent.forEachIndexed { index, (text,kind) -> + if (index > 0) + sb.appendLine() + + when (fmt) { + Format.MEDIAWIKI -> when (kind) { + Section.ContentKind.TEXT -> sb.append(text) + Section.ContentKind.CODE -> { + sb.append("") + sb.append(text) + sb.append("") + } + } + Format.GH_MARKDOWN -> when (kind) { + Section.ContentKind.TEXT -> sb.append(text) + Section.ContentKind.CODE -> { + sb.appendLine("```") + sb.appendLine(text) + sb.appendLine("```") + } + } + } + } + } + + Doc(it.vkind, it.vtitle, sb.toString()) + } +} + +fun documentation(fn: DocumentationContext.() -> Unit): (DocumentationContext.Format) -> List { + val ctx = DocumentationContext() + fn(ctx) + return ctx::build +} + +fun main(args: Array) { + val outPath = args[0] + + val classes = ClassPath.from(ClassLoader.getSystemClassLoader()) + .getAllClasses() + .stream() + .filter { it.packageName.startsWith("org.valkyrienskies.tournament") } + .map { runCatching { it.load() } } + .filter { it.isSuccess } + .map { it.getOrThrow() } + .filter { Documented::class.java.isAssignableFrom(it) && it != Documented::class.java } + .map { it.getDeclaredConstructor().newInstance() as Documented } + .collect(Collectors.toSet()) + + fun format(outDir: String, fmt: DocumentationContext.Format) { + val doc = classes.flatMap { it.getDoc()(fmt) } + for (d in doc) { + val f = File(outPath + File.separator + outDir + File.separator + d.kind.path + d.name.replace(" ", "_").lowercase() + ".md ") + f.parentFile.mkdirs() + f.writeText(d.content) + } + + println("Wrote ${doc.size} ${fmt.name.lowercase().replace('_', ' ')} documentation entries into ${outPath + File.separator + outDir}") + } + + format("doc", DocumentationContext.Format.GH_MARKDOWN) + format("doc_wiki", DocumentationContext.Format.MEDIAWIKI) +} \ No newline at end of file