package org.jetbrains.dokka.base.renderers.html
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlinx.html.h1
import kotlinx.html.id
import kotlinx.html.table
import kotlinx.html.tbody
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.base.renderers.sourceSets
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.DEnum
import org.jetbrains.dokka.model.DEnumEntry
import org.jetbrains.dokka.model.withDescendants
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.configuration
import org.jetbrains.dokka.transformers.pages.PageTransformer
object NavigationPageInstaller : PageTransformer {
private val mapper = jacksonObjectMapper()
private data class NavigationNodeView(
val name: String,
val label: String = name,
val searchKey: String = name,
@get:JsonSerialize(using = ToStringSerializer::class) val dri: DRI,
val location: String
) {
companion object {
fun from(node: NavigationNode, location: String): NavigationNodeView =
NavigationNodeView(name = node.name, dri = node.dri, location = location)
}
}
override fun invoke(input: RootPageNode): RootPageNode {
val nodes = input.children.filterIsInstance().single()
.let(NavigationPageInstaller::visit)
val page = RendererSpecificResourcePage(
name = "scripts/navigation-pane.json",
children = emptyList(),
strategy = RenderingStrategy.LocationResolvableWrite { resolver ->
mapper.writeValueAsString(
nodes.withDescendants().map { NavigationNodeView.from(it, resolver(it.dri, it.sourceSets)) })
})
return input.modified(
children = input.children + page + NavigationPage(nodes)
)
}
private fun visit(page: ContentPage): NavigationNode =
NavigationNode(
name = page.name,
dri = page.dri.first(),
sourceSets = page.sourceSets(),
children = page.navigableChildren()
)
private fun ContentPage.navigableChildren(): List =
when {
this !is ClasslikePageNode ->
children.filterIsInstance().map { visit(it) }
documentable is DEnum ->
children.filter { it is ContentPage && it.documentable is DEnumEntry }.map { visit(it as ContentPage) }
else -> emptyList()
}.sortedBy { it.name.toLowerCase() }
}
class CustomResourceInstaller(val dokkaContext: DokkaContext) : PageTransformer {
private val configuration = configuration(dokkaContext)
private val customAssets = configuration?.customAssets?.map {
RendererSpecificResourcePage("images/${it.name}", emptyList(), RenderingStrategy.Copy(it.absolutePath))
}.orEmpty()
private val customStylesheets = configuration?.customStyleSheets?.map {
RendererSpecificResourcePage("styles/${it.name}", emptyList(), RenderingStrategy.Copy(it.absolutePath))
}.orEmpty()
override fun invoke(input: RootPageNode): RootPageNode {
val customResourcesPaths = (customAssets + customStylesheets).map { it.name }.toSet()
val withEmbeddedResources = input.transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + customResourcesPaths) }
val (currentResources, otherPages) = withEmbeddedResources.children.partition { it is RendererSpecificResourcePage }
return input.modified(children = otherPages + currentResources.filterNot { it.name in customResourcesPaths } + customAssets + customStylesheets)
}
}
object ScriptsInstaller : PageTransformer {
private val scriptsPages = listOf(
"scripts/clipboard.js",
"scripts/navigation-loader.js",
"scripts/platform-content-handler.js",
"scripts/main.js"
)
override fun invoke(input: RootPageNode): RootPageNode {
return input.modified(
children = input.children + scriptsPages.toRenderSpecificResourcePage()
).transformContentPagesTree {
it.modified(
embeddedResources = it.embeddedResources + scriptsPages
)
}
}
}
object StylesInstaller : PageTransformer {
private val stylesPages = listOf(
"styles/style.css",
"styles/logo-styles.css",
"styles/jetbrains-mono.css",
"styles/main.css"
)
override fun invoke(input: RootPageNode): RootPageNode =
input.modified(
children = input.children + stylesPages.toRenderSpecificResourcePage()
).transformContentPagesTree {
it.modified(
embeddedResources = it.embeddedResources + stylesPages
)
}
}
object AssetsInstaller : PageTransformer {
private val imagesPages = listOf(
"images/arrow_down.svg",
"images/docs_logo.svg",
"images/logo-icon.svg"
)
override fun invoke(input: RootPageNode) = input.modified(
children = input.children + imagesPages.toRenderSpecificResourcePage()
)
}
private fun List.toRenderSpecificResourcePage(): List =
map { RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it")) }
class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer {
private val name = "scripts/sourceset_dependencies.js"
override fun invoke(input: RootPageNode): RootPageNode {
val dependenciesMap = context.configuration.sourceSets.map {
it.sourceSetID to it.dependentSourceSets
}.toMap()
fun createDependenciesJson(): String = "sourceset_dependencies = '{${
dependenciesMap.entries.joinToString(", ") {
"\"${it.key}\": [${
it.value.joinToString(",") {
"\"$it\""
}
}]"
}
}}'"
val deps = RendererSpecificResourcePage(
name = name,
children = emptyList(),
strategy = RenderingStrategy.Write(createDependenciesJson())
)
return input.modified(
children = input.children + deps
).transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + name) }
}
}