org.jetbrains.dokka.base.renderers.DefaultRenderer.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dokka-base Show documentation
Show all versions of dokka-base Show documentation
Dokka is an API documentation engine for Kotlin
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package org.jetbrains.dokka.base.renderers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.jetbrains.dokka.DokkaException
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.resolvers.local.LocationProvider
import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.renderers.Renderer
import org.jetbrains.dokka.transformers.pages.PageTransformer
public abstract class DefaultRenderer(
protected val context: DokkaContext
) : Renderer {
protected val outputWriter: OutputWriter = context.plugin().querySingle { outputWriter }
protected lateinit var locationProvider: LocationProvider
private set
protected open val preprocessors: Iterable = emptyList()
public abstract fun T.buildHeader(level: Int, node: ContentHeader, content: T.() -> Unit)
public abstract fun T.buildLink(address: String, content: T.() -> Unit)
public abstract fun T.buildList(
node: ContentList,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
)
public abstract fun T.buildLineBreak()
public open fun T.buildLineBreak(node: ContentBreakLine, pageContext: ContentPage) {
buildLineBreak()
}
public abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage)
public abstract fun T.buildTable(
node: ContentTable,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
)
public abstract fun T.buildText(textNode: ContentText)
public abstract fun T.buildNavigation(page: PageNode)
public abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String
public abstract fun buildError(node: ContentNode)
public open fun T.buildPlatformDependent(
content: PlatformHintedContent,
pageContext: ContentPage,
sourceSetRestriction: Set?
) {
buildContentNode(content.inner, pageContext)
}
public open fun T.buildGroup(
node: ContentGroup,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
) {
wrapGroup(node, pageContext) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } }
}
public open fun T.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) {
node.children.forEach { it.build(this, pageContext) }
}
public open fun T.wrapGroup(node: ContentGroup, pageContext: ContentPage, childrenCallback: T.() -> Unit) {
childrenCallback()
}
public open fun T.buildText(
nodes: List,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
) {
nodes.forEach { it.build(this, pageContext, sourceSetRestriction) }
}
public open fun T.buildCodeBlock(code: ContentCodeBlock, pageContext: ContentPage) {
code.children.forEach { it.build(this, pageContext) }
}
public open fun T.buildCodeInline(code: ContentCodeInline, pageContext: ContentPage) {
code.children.forEach { it.build(this, pageContext) }
}
public open fun T.buildHeader(
node: ContentHeader,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
) {
buildHeader(node.level, node) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } }
}
public open fun ContentNode.build(
builder: T,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
) {
builder.buildContentNode(this, pageContext, sourceSetRestriction)
}
public fun T.buildContentNode(
node: ContentNode,
pageContext: ContentPage,
sourceSetRestriction: DisplaySourceSet
) {
buildContentNode(node, pageContext, setOf(sourceSetRestriction))
}
public open fun T.buildContentNode(
node: ContentNode,
pageContext: ContentPage,
sourceSetRestriction: Set? = null
) {
if (sourceSetRestriction.isNullOrEmpty() || node.sourceSets.any { it in sourceSetRestriction }) {
when (node) {
is ContentText -> buildText(node)
is ContentHeader -> buildHeader(node, pageContext, sourceSetRestriction)
is ContentCodeBlock -> buildCodeBlock(node, pageContext)
is ContentCodeInline -> buildCodeInline(node, pageContext)
is ContentDRILink -> buildDRILink(node, pageContext, sourceSetRestriction)
is ContentResolvedLink -> buildResolvedLink(node, pageContext, sourceSetRestriction)
is ContentEmbeddedResource -> buildResource(node, pageContext)
is ContentList -> buildList(node, pageContext, sourceSetRestriction)
is ContentTable -> buildTable(node, pageContext, sourceSetRestriction)
is ContentGroup -> buildGroup(node, pageContext, sourceSetRestriction)
is ContentBreakLine -> buildLineBreak(node, pageContext)
is PlatformHintedContent -> buildPlatformDependent(node, pageContext, sourceSetRestriction)
is ContentDivergentGroup -> buildDivergent(node, pageContext)
is ContentDivergentInstance -> buildDivergentInstance(node, pageContext)
else -> buildError(node)
}
}
}
public open fun T.buildDRILink(
node: ContentDRILink,
pageContext: ContentPage,
sourceSetRestriction: Set?
) {
locationProvider.resolve(node.address, node.sourceSets, pageContext)?.let { address ->
buildLink(address) {
buildText(node.children, pageContext, sourceSetRestriction)
}
} ?: buildText(node.children, pageContext, sourceSetRestriction)
}
public open fun T.buildResolvedLink(
node: ContentResolvedLink,
pageContext: ContentPage,
sourceSetRestriction: Set?
) {
buildLink(node.address) {
buildText(node.children, pageContext, sourceSetRestriction)
}
}
public open fun T.buildDivergentInstance(node: ContentDivergentInstance, pageContext: ContentPage) {
node.before?.build(this, pageContext)
node.divergent.build(this, pageContext)
node.after?.build(this, pageContext)
}
public open fun buildPageContent(context: T, page: ContentPage) {
context.buildNavigation(page)
page.content.build(context, page)
}
public open suspend fun renderPage(page: PageNode) {
val path by lazy {
locationProvider.resolve(page, skipExtension = true)
?: throw DokkaException("Cannot resolve path for ${page.name}")
}
when (page) {
is ContentPage -> outputWriter.write(path, buildPage(page) { c, p -> buildPageContent(c, p) }, ".html")
is RendererSpecificPage -> when (val strategy = page.strategy) {
is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path)
is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "")
is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".html")
is RenderingStrategy.DriLocationResolvableWrite -> outputWriter.write(
path,
strategy.contentToResolve { dri, sourcesets ->
locationProvider.resolve(dri, sourcesets)
},
""
)
is RenderingStrategy.PageLocationResolvableWrite -> outputWriter.write(
path,
strategy.contentToResolve { pageToLocate, context ->
locationProvider.resolve(pageToLocate, context)
},
""
)
RenderingStrategy.DoNothing -> Unit
}
else -> throw AssertionError(
"Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content"
)
}
}
private suspend fun renderPages(root: PageNode) {
coroutineScope {
renderPage(root)
root.children.forEach {
launch { renderPages(it) }
}
}
}
override fun render(root: RootPageNode) {
val newRoot = preprocessors.fold(root) { acc, t -> t(acc) }
locationProvider =
context.plugin().querySingle { locationProviderFactory }.getLocationProvider(newRoot)
runBlocking(Dispatchers.Default) {
renderPages(newRoot)
}
}
protected fun ContentDivergentGroup.groupDivergentInstances(
pageContext: ContentPage,
beforeTransformer: (ContentDivergentInstance, ContentPage, DisplaySourceSet) -> String,
afterTransformer: (ContentDivergentInstance, ContentPage, DisplaySourceSet) -> String
): Map> =
children.flatMap { instance ->
instance.sourceSets.map { sourceSet ->
Pair(instance, sourceSet) to Pair(
beforeTransformer(instance, pageContext, sourceSet),
afterTransformer(instance, pageContext, sourceSet)
)
}
}.groupBy(
Pair::second,
Pair::first
)
}
internal typealias SerializedBeforeAndAfter = Pair
internal typealias InstanceWithSource = Pair
public fun ContentPage.sourceSets(): Set = this.content.sourceSets
© 2015 - 2025 Weber Informatics LLC | Privacy Policy