aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt')
-rw-r--r--plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt200
1 files changed, 200 insertions, 0 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
new file mode 100644
index 00000000..afee1b33
--- /dev/null
+++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -0,0 +1,200 @@
+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.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.resolvers.local.LocationProvider
+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
+
+abstract class DefaultRenderer<T>(
+ protected val context: DokkaContext
+) : Renderer {
+
+ protected val outputWriter = context.plugin<DokkaBase>().querySingle { outputWriter }
+
+ protected lateinit var locationProvider: LocationProvider
+ private set
+
+ protected open val preprocessors: Iterable<PageTransformer> = emptyList()
+
+ abstract fun T.buildHeader(level: Int, node: ContentHeader, content: T.() -> Unit)
+ abstract fun T.buildLink(address: String, content: T.() -> Unit)
+ abstract fun T.buildList(
+ node: ContentList,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ )
+
+ abstract fun T.buildNewLine()
+ abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage)
+ abstract fun T.buildTable(
+ node: ContentTable,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ )
+
+ abstract fun T.buildText(textNode: ContentText)
+ abstract fun T.buildNavigation(page: PageNode)
+
+ abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String
+ abstract fun buildError(node: ContentNode)
+
+ open fun T.buildPlatformDependent(
+ content: PlatformHintedContent,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>?
+ ) = buildContentNode(content.inner, pageContext)
+
+ open fun T.buildGroup(
+ node: ContentGroup,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ ) =
+ wrapGroup(node, pageContext) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } }
+
+ open fun T.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) =
+ node.children.forEach { it.build(this, pageContext) }
+
+ open fun T.wrapGroup(node: ContentGroup, pageContext: ContentPage, childrenCallback: T.() -> Unit) =
+ childrenCallback()
+
+ open fun T.buildLinkText(
+ nodes: List<ContentNode>,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ ) {
+ nodes.forEach { it.build(this, pageContext, sourceSetRestriction) }
+ }
+
+ open fun T.buildCodeBlock(code: ContentCodeBlock, pageContext: ContentPage) {
+ code.children.forEach { it.build(this, pageContext) }
+ }
+
+ open fun T.buildCodeInline(code: ContentCodeInline, pageContext: ContentPage) {
+ code.children.forEach { it.build(this, pageContext) }
+ }
+
+ open fun T.buildHeader(
+ node: ContentHeader,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ ) {
+ buildHeader(node.level, node) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } }
+ }
+
+ open fun ContentNode.build(
+ builder: T,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ ) =
+ builder.buildContentNode(this, pageContext, sourceSetRestriction)
+
+ open fun T.buildContentNode(
+ node: ContentNode,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>? = null
+ ) {
+ if (sourceSetRestriction == null || 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 ->
+ buildLink(locationProvider.resolve(node.address, node.sourceSets, pageContext)) {
+ buildLinkText(node.children, pageContext, sourceSetRestriction)
+ }
+ is ContentResolvedLink -> buildLink(node.address) {
+ buildLinkText(node.children, 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 -> buildNewLine()
+ is PlatformHintedContent -> buildPlatformDependent(node, pageContext, sourceSetRestriction)
+ is ContentDivergentGroup -> buildDivergent(node, pageContext)
+ is ContentDivergentInstance -> buildDivergentInstance(node, pageContext)
+ else -> buildError(node)
+ }
+ }
+ }
+
+ open fun T.buildDivergentInstance(node: ContentDivergentInstance, pageContext: ContentPage) {
+ node.before?.build(this, pageContext)
+ node.divergent.build(this, pageContext)
+ node.after?.build(this, pageContext)
+ }
+
+ open fun buildPageContent(context: T, page: ContentPage) {
+ context.buildNavigation(page)
+ page.content.build(context, page)
+ }
+
+ open suspend fun renderPage(page: PageNode) {
+ val path by lazy { locationProvider.resolve(page, skipExtension = true) }
+ 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")
+ 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<DokkaBase>().querySingle { locationProviderFactory }.getLocationProvider(newRoot)
+
+ runBlocking(Dispatchers.Default) {
+ renderPages(newRoot)
+ }
+ }
+
+ protected fun ContentDivergentGroup.groupDivergentInstances(
+ pageContext: ContentPage,
+ beforeTransformer: (ContentDivergentInstance, ContentPage, DokkaSourceSet) -> String,
+ afterTransformer: (ContentDivergentInstance, ContentPage, DokkaSourceSet) -> String
+ ): Map<SerializedBeforeAndAfter, List<InstanceWithSource>> =
+ children.flatMap { instance ->
+ instance.sourceSets.map { sourceSet ->
+ Pair(instance, sourceSet) to Pair(
+ beforeTransformer(instance, pageContext, sourceSet),
+ afterTransformer(instance, pageContext, sourceSet)
+ )
+ }
+ }.groupBy(
+ Pair<InstanceWithSource, SerializedBeforeAndAfter>::second,
+ Pair<InstanceWithSource, SerializedBeforeAndAfter>::first
+ )
+}
+
+internal typealias SerializedBeforeAndAfter = Pair<String, String>
+internal typealias InstanceWithSource = Pair<ContentDivergentInstance, DokkaSourceSet>
+
+fun ContentPage.sourceSets() = this.content.sourceSets \ No newline at end of file