aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/renderers
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-01-27 09:34:16 +0100
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-01-31 15:07:06 +0100
commit885ecd28153b484277c9ddcbf4a7f9d761a59545 (patch)
treedb453e66762ebebb3ee05c0301b38c4465ea20a3 /core/src/main/kotlin/renderers
parentc29605d92d1999434ecc79e774281a8280ae2823 (diff)
downloaddokka-885ecd28153b484277c9ddcbf4a7f9d761a59545.tar.gz
dokka-885ecd28153b484277c9ddcbf4a7f9d761a59545.tar.bz2
dokka-885ecd28153b484277c9ddcbf4a7f9d761a59545.zip
Unifing model for pages with content ant technical renderer specific pages
Diffstat (limited to 'core/src/main/kotlin/renderers')
-rw-r--r--core/src/main/kotlin/renderers/DefaultRenderer.kt71
-rw-r--r--core/src/main/kotlin/renderers/FileWriter.kt6
-rw-r--r--core/src/main/kotlin/renderers/HtmlRenderer.kt138
-rw-r--r--core/src/main/kotlin/renderers/Renderer.kt4
-rw-r--r--core/src/main/kotlin/renderers/htmlPreprocessors.kt57
5 files changed, 150 insertions, 126 deletions
diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt
index 5e3eadbe..606eea54 100644
--- a/core/src/main/kotlin/renderers/DefaultRenderer.kt
+++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -5,55 +5,62 @@ import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.single
import org.jetbrains.dokka.resolvers.LocationProvider
+import org.jetbrains.dokka.transformers.pages.PageNodeTransformer
abstract class DefaultRenderer<T>(
protected val outputWriter: OutputWriter,
protected val context: DokkaContext
) : Renderer {
+ private val extension = context.single(CoreExtensions.fileExtension)
+
protected lateinit var locationProvider: LocationProvider
private set
+ protected open val preprocessors: Iterable<PageNodeTransformer> = emptyList()
+
protected abstract fun T.buildHeader(level: Int, content: T.() -> Unit)
protected abstract fun T.buildLink(address: String, content: T.() -> Unit)
- protected abstract fun T.buildList(node: ContentList, pageContext: PageNode)
+ protected abstract fun T.buildList(node: ContentList, pageContext: ContentPage)
protected abstract fun T.buildNewLine()
- protected abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: PageNode)
- protected abstract fun T.buildTable(node: ContentTable, pageContext: PageNode)
+ protected abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage)
+ protected abstract fun T.buildTable(node: ContentTable, pageContext: ContentPage)
protected abstract fun T.buildText(textNode: ContentText)
protected abstract fun T.buildNavigation(page: PageNode)
- protected abstract fun buildPage(page: PageNode, content: (T, PageNode) -> Unit): String
+ protected abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String
protected abstract fun buildError(node: ContentNode)
- protected open fun T.buildGroup(node: ContentGroup, pageContext: PageNode){
+ protected open fun T.buildGroup(node: ContentGroup, pageContext: ContentPage) {
node.children.forEach { it.build(this, pageContext) }
}
- protected open fun T.buildLinkText(nodes: List<ContentNode>, pageContext: PageNode) {
+ protected open fun T.buildLinkText(nodes: List<ContentNode>, pageContext: ContentPage) {
nodes.forEach { it.build(this, pageContext) }
}
- protected open fun T.buildCode(code: List<ContentNode>, language: String, pageContext: PageNode) {
+ protected open fun T.buildCode(code: List<ContentNode>, language: String, pageContext: ContentPage) {
code.forEach { it.build(this, pageContext) }
}
- protected open fun T.buildHeader(node: ContentHeader, pageContext: PageNode) {
+ protected open fun T.buildHeader(node: ContentHeader, pageContext: ContentPage) {
buildHeader(node.level) { node.children.forEach { it.build(this, pageContext) } }
}
- protected open fun ContentNode.build(builder: T, pageContext: PageNode) = builder.buildContentNode(this, pageContext)
+ protected open fun ContentNode.build(builder: T, pageContext: ContentPage) =
+ builder.buildContentNode(this, pageContext)
- protected open fun T.buildContentNode(node: ContentNode, pageContext: PageNode) {
+ protected open fun T.buildContentNode(node: ContentNode, pageContext: ContentPage) {
when (node) {
is ContentText -> buildText(node)
is ContentHeader -> buildHeader(node, pageContext)
is ContentCode -> buildCode(node.children, node.language, pageContext)
is ContentDRILink -> buildLink(
- locationProvider.resolve(node.address, node.platforms.toList(), pageContext)) {
+ locationProvider.resolve(node.address, node.platforms.toList(), pageContext)
+ ) {
buildLinkText(node.children, pageContext)
}
- is ContentResolvedLink -> buildLink(node.address) {buildLinkText(node.children, pageContext)}
+ is ContentResolvedLink -> buildLink(node.address) { buildLinkText(node.children, pageContext) }
is ContentEmbeddedResource -> buildResource(node, pageContext)
is ContentList -> buildList(node, pageContext)
is ContentTable -> buildTable(node, pageContext)
@@ -62,22 +69,34 @@ abstract class DefaultRenderer<T>(
}
}
- protected open fun buildPageContent(context: T, page: PageNode) {
+ protected open fun buildPageContent(context: T, page: ContentPage) {
context.buildNavigation(page)
page.content.build(context, page)
}
- protected open fun renderPage(page: PageNode) =
- outputWriter.write(locationProvider.resolve(page), buildPage(page, ::buildPageContent), "")
+ protected open 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) }, extension)
+ 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))
+ RenderingStrategy.DoNothing -> Unit
+ }
+ else -> throw AssertionError(
+ "Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content"
+ )
+ }
+ }
protected open fun renderPages(root: PageNode) {
renderPage(root)
root.children.forEach { renderPages(it) }
}
- protected open fun buildSupportFiles() {}
-
- protected open fun renderPackageList(root: PageNode) =
+ // reimplement this as preprocessor
+ protected open fun renderPackageList(root: ContentPage) =
getPackageNamesAndPlatforms(root)
.keys
.joinToString("\n")
@@ -93,12 +112,16 @@ abstract class DefaultRenderer<T>(
emptyMap()
}
- override fun render(root: PageNode) {
- locationProvider = context.single(CoreExtensions.locationProviderFactory).getLocationProvider(root as ModulePageNode)
- renderPackageList(root)
- buildSupportFiles()
- renderPages(root)
+ override fun render(root: RootPageNode) {
+ val newRoot = preprocessors.fold(root) { acc, t -> t(acc) }
+
+ locationProvider =
+ context.single(CoreExtensions.locationProviderFactory).getLocationProvider(newRoot)
+
+ root.children<ModulePageNode>().forEach { renderPackageList(it) }
+
+ renderPages(newRoot)
}
}
-fun PageNode.platforms() = this.content.platforms.toList() \ No newline at end of file
+fun ContentPage.platforms() = this.content.platforms.toList() \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/FileWriter.kt b/core/src/main/kotlin/renderers/FileWriter.kt
index 22a2e8f9..2fba649d 100644
--- a/core/src/main/kotlin/renderers/FileWriter.kt
+++ b/core/src/main/kotlin/renderers/FileWriter.kt
@@ -1,6 +1,5 @@
package org.jetbrains.dokka.renderers
-import com.intellij.util.io.isDirectory
import java.io.File
import java.io.IOException
import java.net.URI
@@ -17,10 +16,9 @@ class FileWriter(val root: String, override val extension: String): OutputWriter
createdFiles.add(path)
try {
-// println("Writing $root/$path$ext")
val dir = Paths.get(root, path.dropLastWhile { it != '/' }).toFile()
dir.mkdirsOrFail()
- Paths.get(root, "$path$ext").toFile().writeText(text)
+ Files.write(Paths.get(root, "$path$ext"), text.lines())
} catch (e: Throwable) {
println("Failed to write $this. ${e.message}")
e.printStackTrace()
@@ -36,7 +34,7 @@ class FileWriter(val root: String, override val extension: String): OutputWriter
val fs = getFileSystemForURI(uri)
val path = fs.getPath(pathFrom)
for (file in Files.walk(path).iterator()) {
- if (file.isDirectory()) {
+ if (Files.isDirectory(file)) {
val dirPath = file.toAbsolutePath().toString()
Paths.get(root, rebase(dirPath)).toFile().mkdirsOrFail()
} else {
diff --git a/core/src/main/kotlin/renderers/HtmlRenderer.kt b/core/src/main/kotlin/renderers/HtmlRenderer.kt
index ae120795..af99d349 100644
--- a/core/src/main/kotlin/renderers/HtmlRenderer.kt
+++ b/core/src/main/kotlin/renderers/HtmlRenderer.kt
@@ -1,15 +1,12 @@
package org.jetbrains.dokka.renderers
import kotlinx.html.*
-import kotlinx.html.dom.document
-import kotlinx.html.stream.appendHTML
-import org.jetbrains.dokka.links.DRI
-import org.jetbrains.dokka.model.Documentable
+import kotlinx.html.stream.createHTML
+import org.jetbrains.dokka.model.Function
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File
-import java.net.URL
open class HtmlRenderer(
outputWriter: OutputWriter,
@@ -39,7 +36,9 @@ open class HtmlRenderer(
}
}
- override fun FlowContent.buildList(node: ContentList, pageContext: PageNode) =
+ override val preprocessors = listOf(RootCreator, SearchPageInstaller, ResourceInstaller, StyleAndScriptsAppender)
+
+ override fun FlowContent.buildList(node: ContentList, pageContext: ContentPage) =
if (node.ordered) ol {
buildListItems(node.children, pageContext)
}
@@ -47,7 +46,7 @@ open class HtmlRenderer(
buildListItems(node.children, pageContext)
}
- protected open fun OL.buildListItems(items: List<ContentNode>, pageContext: PageNode) {
+ protected open fun OL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) {
items.forEach {
if (it is ContentList)
buildList(it, pageContext)
@@ -56,7 +55,7 @@ open class HtmlRenderer(
}
}
- protected open fun UL.buildListItems(items: List<ContentNode>, pageContext: PageNode) {
+ protected open fun UL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) {
items.forEach {
if (it is ContentList)
buildList(it, pageContext)
@@ -67,7 +66,7 @@ open class HtmlRenderer(
override fun FlowContent.buildResource(
node: ContentEmbeddedResource,
- pageContext: PageNode
+ pageContext: ContentPage
) { // TODO: extension point there
val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg")
return if (File(node.address).extension.toLowerCase() in imageExtensions) {
@@ -79,7 +78,7 @@ open class HtmlRenderer(
}
}
- override fun FlowContent.buildTable(node: ContentTable, pageContext: PageNode) {
+ override fun FlowContent.buildTable(node: ContentTable, pageContext: ContentPage) {
table {
thead {
node.header.forEach {
@@ -138,60 +137,48 @@ open class HtmlRenderer(
override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) =
a(href = address, block = content)
- override fun FlowContent.buildCode(code: List<ContentNode>, language: String, pageContext: PageNode) {
+ override fun FlowContent.buildCode(code: List<ContentNode>, language: String, pageContext: ContentPage) {
buildNewLine()
code.forEach {
- + ((it as? ContentText)?.text ?: run { context.logger.error("Cannot cast $it as ContentText!"); ""} )
+ +((it as? ContentText)?.text ?: run { context.logger.error("Cannot cast $it as ContentText!"); "" })
buildNewLine()
}
}
override fun renderPage(page: PageNode) {
super.renderPage(page)
- pageList.add("""{ "name": "${page.name}", ${if (page is ClassPageNode) "\"class\": \"${page.name}\"," else ""} "location": "${locationProvider.resolve(page)}" }""")
+ if (page is ContentPage) {
+ pageList.add(
+ """{ "name": "${page.name}", ${if (page is ClassPageNode) "\"class\": \"${page.name}\"," else ""} "location": "${locationProvider.resolve(
+ page
+ )}" }"""
+ )
+ }
}
override fun FlowContent.buildText(textNode: ContentText) {
text(textNode.text)
}
- override fun render(root: PageNode) {
+ override fun render(root: RootPageNode) {
super.render(root)
outputWriter.write("scripts/pages", "var pages = [\n${pageList.joinToString(",\n")}\n]", ".js")
}
- override fun buildSupportFiles() { // TODO copy file instead of reading
- outputWriter.write(
- "style.css",
- javaClass.getResourceAsStream("/dokka/styles/style.css").reader().readText()
- )
- renderPage(searchPageNode)
- outputWriter.writeResources("/dokka/styles", "styles")
- outputWriter.writeResources("/dokka/scripts", "scripts")
- outputWriter.writeResources("/dokka/images", "images")
- }
+ private fun PageNode.root(path: String) = locationProvider.resolveRoot(this) + path
- private fun PageNode.root(path: String) =
- "${if (this != searchPageNode) locationProvider.resolveRoot(this) else ""}$path"
+ override fun buildPage(page: ContentPage, content: (FlowContent, ContentPage) -> Unit): String =
+ buildHtml(page, page.embeddedResources) { content(this, page) }
- override fun buildPage(page: PageNode, content: (FlowContent, PageNode) -> Unit): String =
- StringBuilder().appendHTML().html {
- document {
-
- }
+ open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) =
+ createHTML().html {
head {
title(page.name)
- link(rel = LinkRel.stylesheet, href = page.root("styles/style.css"))
- page.embeddedResources.filter {
- URL(it).path.substringAfterLast('.') == "js"
- }.forEach {
- script(type = ScriptType.textJavaScript, src = it) { async = true }
- }
- if (page == searchPageNode) {
- script(
- type = ScriptType.textJavaScript,
- src = page.root("scripts/pages.js")
- ) { async = true }
+ with(resources) {
+ filter { it.substringAfterLast('.') == "css" }
+ .forEach { link(rel = LinkRel.stylesheet, href = page.root(it)) }
+ filter { it.substringAfterLast('.') == "js" }
+ .forEach { script(type = ScriptType.textJavaScript, src = it) { async = true } }
}
}
body {
@@ -207,65 +194,24 @@ open class HtmlRenderer(
}
div {
id = "sideMenu"
- img(src = page.root("images/logo-icon.svg"))
- img(src = page.root("images/logo-text.svg"))
- hr()
- input(type = InputType.search) {
- id = "navigationFilter"
- }
- script(
- type = ScriptType.textJavaScript,
- src = page.root("scripts/scripts.js")
- ) { async = true }
- buildSideMenu(page, locationProvider.top())
}
}
div {
id = "content"
- if (page != searchPageNode) content(this, page)
- else {
- h1 {
- id = "searchTitle"
- text("Search results for ")
- }
- table {
- tbody {
- id = "searchTable"
- }
- }
- script(
- type = ScriptType.textJavaScript,
- src = page.root("scripts/search.js")
- ) { async = true }
- }
+ content()
}
}
- }.toString()
-
- protected open fun List<HTMLMetadata>.joinAttr() = this.joinToString(" ") { it.key + "=" + it.value }
-
- private val searchPageNode =
- object: PageNode {
- override val name: String
- get() = "Search"
- override val content = object: ContentNode{
- override val dci: DCI = DCI(DRI.topLevel, ContentKind.Main)
- override val platforms: Set<PlatformData> = emptySet()
- override val style: Set<Style> = emptySet()
- override val extras: Set<Extra> = emptySet()
-
- }
- override val dri: DRI = DRI.topLevel
- override val documentable: Documentable? = null
- override val embeddedResources: List<String> = emptyList()
- override val children: List<PageNode> = emptyList()
+ }
+}
- override fun modified(
- name: String,
- content: ContentNode,
- embeddedResources: List<String>,
- children: List<PageNode>
- ): PageNode = this
+fun List<HTMLMetadata>.joinAttr() = joinToString(" ") { it.key + "=" + it.value }
- }
-} \ No newline at end of file
+private fun PageNode.pageKind() = when (this) {
+ is PackagePageNode -> "package"
+ is ClassPageNode -> "class"
+ is MemberPageNode -> when (this.documentable) {
+ is Function -> "function"
+ else -> "other"
+ }
+ else -> "other"
+}
diff --git a/core/src/main/kotlin/renderers/Renderer.kt b/core/src/main/kotlin/renderers/Renderer.kt
index 271bdf77..10235f21 100644
--- a/core/src/main/kotlin/renderers/Renderer.kt
+++ b/core/src/main/kotlin/renderers/Renderer.kt
@@ -1,7 +1,7 @@
package org.jetbrains.dokka.renderers
-import org.jetbrains.dokka.pages.PageNode
+import org.jetbrains.dokka.pages.RootPageNode
interface Renderer {
- fun render(root: PageNode)
+ fun render(root: RootPageNode)
} \ No newline at end of file
diff --git a/core/src/main/kotlin/renderers/htmlPreprocessors.kt b/core/src/main/kotlin/renderers/htmlPreprocessors.kt
new file mode 100644
index 00000000..a6ad267b
--- /dev/null
+++ b/core/src/main/kotlin/renderers/htmlPreprocessors.kt
@@ -0,0 +1,57 @@
+package org.jetbrains.dokka.renderers
+
+import kotlinx.html.h1
+import kotlinx.html.id
+import kotlinx.html.table
+import kotlinx.html.tbody
+import org.jetbrains.dokka.pages.RendererSpecificResourcePage
+import org.jetbrains.dokka.pages.RendererSpecificRootPage
+import org.jetbrains.dokka.pages.RenderingStrategy
+import org.jetbrains.dokka.pages.RootPageNode
+import org.jetbrains.dokka.transformers.pages.PageNodeTransformer
+
+object RootCreator : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) =
+ RendererSpecificRootPage("", listOf(input), RenderingStrategy.DoNothing)
+}
+
+object SearchPageInstaller : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.modified(children = input.children + searchPage)
+
+ private val searchPage = RendererSpecificResourcePage(
+ name = "Search",
+ children = emptyList(),
+ strategy = RenderingStrategy<HtmlRenderer> {
+ buildHtml(it, listOf("styles/style.css", "scripts/pages.js")) {
+ h1 {
+ id = "searchTitle"
+ text("Search results for ")
+ }
+ table {
+ tbody {
+ id = "searchTable"
+ }
+ }
+ }
+ })
+
+}
+
+object ResourceInstaller : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.modified(children = input.children + resourcePages)
+
+ private val resourcePages = listOf("styles", "scripts", "images").map {
+ RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it"))
+ }
+}
+
+object StyleAndScriptsAppender : PageNodeTransformer {
+ override fun invoke(input: RootPageNode) = input.transformContentPagesTree {
+ it.modified(
+ embeddedResources = it.embeddedResources + listOf(
+ "styles/style.css",
+ "scripts/navigationLoader.js"
+ )
+ )
+ }
+}