aboutsummaryrefslogtreecommitdiff
path: root/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt
blob: 95ac476f6e24ad6ae9299d7aafffe50fa532ecc1 (plain)
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 org.jetbrains.dokka.templates

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.templating.Command
import org.jetbrains.dokka.model.withDescendants
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.query
import org.jetbrains.dokka.plugability.querySingle
import org.jsoup.nodes.Node
import java.io.File

interface TemplateProcessor

interface SubmoduleTemplateProcessor : TemplateProcessor {
    fun process(modules: List<DokkaModuleDescription>): TemplatingResult
}

interface MultiModuleTemplateProcessor : TemplateProcessor {
    fun process(generatedPagesTree: RootPageNode)
}

interface TemplateProcessingStrategy {
    fun process(input: File, output: File, moduleContext: DokkaModuleDescription?): Boolean
    fun finish(output: File) {}
}

class DefaultSubmoduleTemplateProcessor(
    private val context: DokkaContext,
) : SubmoduleTemplateProcessor {

    private val strategies: List<TemplateProcessingStrategy> =
        context.plugin<TemplatingPlugin>().query { templateProcessingStrategy }

    private val configuredModulesPaths =
            context.configuration.modules.associate { it.sourceOutputDirectory.absolutePath to it.name }

    override fun process(modules: List<DokkaModuleDescription>) =
        runBlocking(Dispatchers.Default) {
            coroutineScope {
                modules.fold(TemplatingResult()) { acc, module ->
                    acc + module.sourceOutputDirectory.visit(context.configuration.outputDir.resolve(module.relativePathToOutputDirectory), module)
                }
            }
        }

    private suspend fun File.visit(target: File, module: DokkaModuleDescription, acc: TemplatingResult = TemplatingResult()): TemplatingResult =
        coroutineScope {
            val source = this@visit
            if (source.isDirectory) {
                target.mkdirs()
                val files = source.list().orEmpty()
                val accWithSelf = configuredModulesPaths[source.absolutePath]
                    ?.takeIf { files.firstOrNull { !it.startsWith(".") } != null }
                    ?.let { acc.copy(modules = acc.modules + it) }
                    ?: acc

                files.fold(accWithSelf) { acc, path ->
                    source.resolve(path).visit(target.resolve(path), module, acc)
                }
            } else {
                strategies.first { it.process(source, target, module) }
                acc
            }
        }
}

class DefaultMultiModuleTemplateProcessor(
    val context: DokkaContext,
) : MultiModuleTemplateProcessor {
    private val strategies: List<TemplateProcessingStrategy> =
        context.plugin<TemplatingPlugin>().query { templateProcessingStrategy }

    private val locationProviderFactory = context.plugin<DokkaBase>().querySingle { locationProviderFactory }

    override fun process(generatedPagesTree: RootPageNode) {
        val locationProvider = locationProviderFactory.getLocationProvider(generatedPagesTree)
        generatedPagesTree.withDescendants().mapNotNull { pageNode -> locationProvider.resolve(pageNode)?.let { File(it) } }
            .forEach { location -> strategies.first { it.process(location, location, null) } }
    }
}

data class TemplatingContext<out T : Command>(
    val input: File,
    val output: File,
    val body: List<Node>,
    val command: T,
)

data class TemplatingResult(val modules: List<String> = emptyList()) {
    operator fun plus(rhs: TemplatingResult): TemplatingResult = TemplatingResult((modules + rhs.modules).distinct())
}