aboutsummaryrefslogtreecommitdiff
path: root/core
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
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')
-rw-r--r--core/src/main/kotlin/DokkaGenerator.kt7
-rw-r--r--core/src/main/kotlin/pages/PageNodes.kt92
-rw-r--r--core/src/main/kotlin/pages/RendererSpecificPage.kt40
-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
-rw-r--r--core/src/main/kotlin/resolvers/DefaultLocationProvider.kt81
-rw-r--r--core/src/main/kotlin/resolvers/LocationProvider.kt6
-rw-r--r--core/src/main/kotlin/resolvers/LocationProviderFactory.kt5
-rw-r--r--core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt1
-rw-r--r--core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt3
-rw-r--r--core/src/main/kotlin/utilities/nodeDebug.kt2
-rw-r--r--core/src/main/resources/dokka/scripts/navigationLoader.js0
15 files changed, 302 insertions, 211 deletions
diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt
index 95f9e647..add7eedb 100644
--- a/core/src/main/kotlin/DokkaGenerator.kt
+++ b/core/src/main/kotlin/DokkaGenerator.kt
@@ -8,6 +8,7 @@ import org.jetbrains.dokka.analysis.DokkaResolutionFacade
import org.jetbrains.dokka.model.Module
import org.jetbrains.dokka.pages.ModulePageNode
import org.jetbrains.dokka.pages.PlatformData
+import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.single
import org.jetbrains.dokka.utilities.DokkaLogger
@@ -88,12 +89,12 @@ class DokkaGenerator(
) = context.single(CoreExtensions.documentationToPageTranslator).invoke(transformedDocumentation, context)
fun transformPages(
- pages: ModulePageNode,
+ pages: RootPageNode,
context: DokkaContext
- ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc, context) }
+ ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) }
fun render(
- transformedPages: ModulePageNode,
+ transformedPages: RootPageNode,
context: DokkaContext
) {
val renderer = context.single(CoreExtensions.renderer)
diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt
index 9274ac2b..80b21547 100644
--- a/core/src/main/kotlin/pages/PageNodes.kt
+++ b/core/src/main/kotlin/pages/PageNodes.kt
@@ -7,18 +7,57 @@ import java.util.*
interface PageNode {
val name: String
+ val children: List<PageNode>
+
+ fun modified(
+ name: String = this.name,
+ children: List<PageNode> = this.children
+ ): PageNode
+}
+
+interface ContentPage: PageNode {
val content: ContentNode
val dri: DRI
val documentable: Documentable?
val embeddedResources: List<String>
- val children: List<PageNode>
fun modified(
name: String = this.name,
content: ContentNode = this.content,
embeddedResources: List<String> = this.embeddedResources,
children: List<PageNode> = this.children
- ): PageNode
+ ): ContentPage
+}
+
+abstract class RootPageNode: PageNode {
+ val parentMap: Map<PageNode, PageNode> by lazy {
+ IdentityHashMap<PageNode, PageNode>().apply {
+ fun process(parent: PageNode) {
+ parent.children.forEach { child ->
+ put(child, parent)
+ process(child)
+ }
+ }
+ process(this@RootPageNode)
+ }
+ }
+
+ fun transformPageNodeTree(operation: (PageNode) -> PageNode) =
+ this.transformNode(operation) as RootPageNode
+
+ fun transformContentPagesTree(operation: (ContentPage) -> ContentPage) = transformPageNodeTree {
+ if (it is ContentPage) operation(it) else it
+ } as RootPageNode
+
+ private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode =
+ operation(this).let { newNode ->
+ newNode.modified(children = newNode.children.map { it.transformNode(operation) })
+ }
+
+ abstract override fun modified(
+ name: String,
+ children: List<PageNode>
+ ): RootPageNode
}
class ModulePageNode(
@@ -28,9 +67,12 @@ class ModulePageNode(
override val documentable: Documentable?,
override val children: List<PageNode>,
override val embeddedResources: List<String> = listOf()
-) : PageNode {
+) : RootPageNode(), ContentPage {
override val dri: DRI = DRI.topLevel
+ override fun modified(name: String, children: List<PageNode>): ModulePageNode =
+ modified(name = name, content = this.content, children = children)
+
override fun modified(
name: String,
content: ContentNode,
@@ -39,26 +81,6 @@ class ModulePageNode(
): ModulePageNode =
if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
else ModulePageNode(name, content, documentable, children, embeddedResources)
-
- private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode =
- operation(this).let { newNode ->
- newNode.modified(children = newNode.children.map { it.transformNode(operation) })
- }
-
- fun transformPageNodeTree(operation: (PageNode) -> PageNode) =
- this.transformNode(operation) as ModulePageNode
-
- val parentMap: IdentityHashMap<PageNode, PageNode> by lazy {
- IdentityHashMap<PageNode, PageNode>().apply {
- fun addParent(parent: PageNode) {
- parent.children.forEach { child ->
- put(child, parent)
- addParent(child)
- }
- }
- addParent(this@ModulePageNode)
- }
- }
}
class PackagePageNode(
@@ -69,7 +91,9 @@ class PackagePageNode(
override val documentable: Documentable?,
override val children: List<PageNode>,
override val embeddedResources: List<String> = listOf()
-) : PageNode {
+) : ContentPage {
+ override fun modified(name: String, children: List<PageNode>): PackagePageNode =
+ modified(name = name, content = this.content, children = children)
override fun modified(
name: String,
@@ -88,7 +112,9 @@ class ClassPageNode(
override val documentable: Documentable?,
override val children: List<PageNode>,
override val embeddedResources: List<String> = listOf()
-) : PageNode {
+) : ContentPage {
+ override fun modified(name: String, children: List<PageNode>): ClassPageNode =
+ modified(name = name, content = this.content, children = children)
override fun modified(
name: String,
@@ -107,7 +133,9 @@ class MemberPageNode(
override val documentable: Documentable?,
override val children: List<PageNode> = emptyList(),
override val embeddedResources: List<String> = listOf()
-) : PageNode {
+) : ContentPage {
+ override fun modified(name: String, children: List<PageNode>): MemberPageNode =
+ modified(name = name, content = this.content, children = children)
override fun modified(
name: String,
@@ -129,10 +157,12 @@ fun PageNode.dfs(predicate: (PageNode) -> Boolean): PageNode? = if (predicate(th
this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull()
}
-private infix fun <T> List<T>.shallowEq(other: List<T>) =
- this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b })
+fun PageNode.asSequence(): Sequence<PageNode> = sequence {
+ yield(this@asSequence)
+ children.asSequence().flatMap { it.asSequence() }.forEach { yield(it) }
+}
-// Navigation??
+inline fun <reified T: PageNode> PageNode.children() = children.filterIsInstance<T>()
-// content modifier?
-//data class ContentLink(val link: String): ContentNode \ No newline at end of file
+private infix fun <T> List<T>.shallowEq(other: List<T>) =
+ this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b })
diff --git a/core/src/main/kotlin/pages/RendererSpecificPage.kt b/core/src/main/kotlin/pages/RendererSpecificPage.kt
new file mode 100644
index 00000000..85e6d530
--- /dev/null
+++ b/core/src/main/kotlin/pages/RendererSpecificPage.kt
@@ -0,0 +1,40 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.renderers.Renderer
+import kotlin.reflect.KClass
+
+interface RendererSpecificPage : PageNode {
+ val strategy: RenderingStrategy
+}
+
+class RendererSpecificRootPage(
+ override val name: String,
+ override val children: List<PageNode>,
+ override val strategy: RenderingStrategy
+) : RootPageNode(), RendererSpecificPage {
+ override fun modified(name: String, children: List<PageNode>): RendererSpecificRootPage =
+ RendererSpecificRootPage(name, children, strategy)
+}
+
+class RendererSpecificResourcePage(
+ override val name: String,
+ override val children: List<PageNode>,
+ override val strategy: RenderingStrategy
+): RendererSpecificPage {
+ override fun modified(name: String, children: List<PageNode>): RendererSpecificResourcePage =
+ RendererSpecificResourcePage(name, children, strategy)
+}
+
+sealed class RenderingStrategy {
+ class Callback(val instructions: Renderer.(PageNode) -> String): RenderingStrategy()
+ data class Copy(val from: String) : RenderingStrategy()
+ data class Write(val text: String) : RenderingStrategy()
+ object DoNothing : RenderingStrategy()
+
+ companion object {
+ inline operator fun <reified T: Renderer> invoke(crossinline instructions: T.(PageNode) -> String) =
+ Callback { if (this is T) instructions(it) else throw WrongRendererTypeException(T::class) }
+ }
+}
+
+data class WrongRendererTypeException(val expectedType: KClass<*>): Exception() \ No newline at end of file
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"
+ )
+ )
+ }
+}
diff --git a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
index 8e848b63..48ea5316 100644
--- a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
+++ b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt
@@ -8,19 +8,35 @@ import org.jetbrains.dokka.plugability.single
import org.jetbrains.dokka.utilities.htmlEscape
import java.util.*
+private const val PAGE_WITH_CHILDREN_SUFFIX = "index"
+
open class DefaultLocationProvider(
- private val pageGraphRoot: ModulePageNode,
- private val dokkaContext: DokkaContext
-) : LocationProvider { // TODO: cache
- private val extension = dokkaContext.single(CoreExtensions.fileExtension)
+ protected val pageGraphRoot: RootPageNode,
+ protected val dokkaContext: DokkaContext
+) : LocationProvider {
+ protected val extension = dokkaContext.single(CoreExtensions.fileExtension)
+
+ protected val pagesIndex: Map<DRI, ContentPage> = pageGraphRoot.asSequence().filterIsInstance<ContentPage>()
+ .groupingBy { it.dri }
+ .aggregate { dri, _, page, first ->
+ if (first) page else throw AssertionError("Multiple pages associated with dri: $dri")
+ }
- private val pagesCache = mutableMapOf<DRI, Maybe<PageNode>>()
- private val pathCache: MutableMap<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>()
+ protected val pathsIndex: Map<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>().apply {
+ fun registerPath(page: PageNode, prefix: List<String>) {
+ val newPrefix = prefix + page.pathName
+ put(page, newPrefix)
+ page.children.forEach { registerPath(it, newPrefix) }
+ }
+ put(pageGraphRoot, emptyList())
+ pageGraphRoot.children.forEach { registerPath(it, emptyList()) }
+ }
- override fun resolve(node: PageNode, context: PageNode?): String = pathTo(node, context) + extension
+ override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String =
+ pathTo(node, context) + if (!skipExtension) extension else ""
override fun resolve(dri: DRI, platforms: List<PlatformData>, context: PageNode?): String =
- findInPageGraph(dri, platforms)?.let { resolve(it, context) } ?:
+ pagesIndex[dri]?.let { resolve(it, context) } ?:
// Not found in PageGraph, that means it's an external link
ExternalLocationProvider.getLocation(dri,
this.dokkaContext.configuration.passesConfigurations
@@ -31,50 +47,30 @@ open class DefaultLocationProvider(
.flatMap { it.externalDocumentationLinks }.distinct()
)
- override fun resolveRoot(node: PageNode): String = "../${pathTo(pageGraphRoot, node).removeSuffix(
- PAGE_WITH_CHILDREN_SUFFIX
- )}"
-
- private fun PageNode.parent() = pageGraphRoot.parentMap[this]
-
- override fun ancestors(node: PageNode?): List<PageNode> = when (node) {
- null -> emptyList()
- else -> ancestors(node.parent()) + node
- }
-
- override fun top(): PageNode = pageGraphRoot
-
- protected open fun findInPageGraph(dri: DRI, platforms: List<PlatformData>): PageNode? =
- pagesCache.getOrPut(dri) { pageGraphRoot.dfs { it.dri == dri }.wrapped }.unwrapped
+ override fun resolveRoot(node: PageNode): String =
+ pathTo(pageGraphRoot, node).removeSuffix(PAGE_WITH_CHILDREN_SUFFIX)
+ override fun ancestors(node: PageNode): List<PageNode> =
+ generateSequence(node) { it.parent() }.toList()
protected open fun pathTo(node: PageNode, context: PageNode?): String {
-
- fun PageNode.pathName(): String =
- if (this is PackagePageNode) name
- else identifierToFilename(name)
-
- fun getPathInternal(pathNode: PageNode?, path: List<String>): List<String> = when (pathNode) {
- null -> path
- else -> getPathInternal(pathNode.parent(), path + pathNode.pathName().ifEmpty { "root" })
- }
-
- fun getPath(pathNode: PageNode) = pathCache.getOrPut(pathNode) { getPathInternal(pathNode, emptyList()) }
+ fun pathFor(page: PageNode) = pathsIndex[page] ?: throw AssertionError(
+ "${page::class.simpleName}(${page.name}) does not belong to current page graph so it is impossible to compute its path"
+ )
val contextNode =
if (context?.children?.isEmpty() == true && context.parent() != null) context.parent() else context
- val nodePath = getPath(node).reversed()
- val contextPath = contextNode?.let(::getPath).orEmpty().reversed()
+ val nodePath = pathFor(node)
+ val contextPath = contextNode?.let { pathFor(it) }.orEmpty()
- val commonPathElements = nodePath.zip(contextPath).takeWhile { (a, b) -> a == b }.size
+ val commonPathElements = nodePath.asSequence().zip(contextPath.asSequence())
+ .takeWhile { (a, b) -> a == b }.count()
return (List(contextPath.size - commonPathElements) { ".." } + nodePath.drop(commonPathElements) +
if (node.children.isNotEmpty()) listOf(PAGE_WITH_CHILDREN_SUFFIX) else emptyList()).joinToString("/")
}
- private companion object {
- const val PAGE_WITH_CHILDREN_SUFFIX = "index"
- }
+ private fun PageNode.parent() = pageGraphRoot.parentMap[this]
}
fun DRI.toJavadocLocation(jdkVersion: Int): String { // TODO: classes without packages?
@@ -122,6 +118,5 @@ private fun identifierToFilename(name: String): String {
return if (lowercase in reservedFilenames) "--$lowercase--" else lowercase
}
-private class Maybe<T : Any>(val unwrapped: T?)
-
-private val <T : Any> T?.wrapped: Maybe<T> get() = Maybe(this) \ No newline at end of file
+private val PageNode.pathName: String
+ get() = if (this is PackagePageNode) name else identifierToFilename(name)
diff --git a/core/src/main/kotlin/resolvers/LocationProvider.kt b/core/src/main/kotlin/resolvers/LocationProvider.kt
index 7d77ccb8..3bc9ab72 100644
--- a/core/src/main/kotlin/resolvers/LocationProvider.kt
+++ b/core/src/main/kotlin/resolvers/LocationProvider.kt
@@ -1,13 +1,13 @@
package org.jetbrains.dokka.resolvers
import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.PageNode
import org.jetbrains.dokka.pages.PlatformData
interface LocationProvider {
fun resolve(dri: DRI, platforms: List<PlatformData>, context: PageNode? = null): String
- fun resolve(node: PageNode, context: PageNode? = null): String
+ fun resolve(node: PageNode, context: PageNode? = null, skipExtension: Boolean = false): String
fun resolveRoot(node: PageNode): String
- fun ancestors(node: PageNode?): List<PageNode>
- fun top(): PageNode
+ fun ancestors(node: PageNode): List<PageNode>
}
diff --git a/core/src/main/kotlin/resolvers/LocationProviderFactory.kt b/core/src/main/kotlin/resolvers/LocationProviderFactory.kt
index c657846a..782795de 100644
--- a/core/src/main/kotlin/resolvers/LocationProviderFactory.kt
+++ b/core/src/main/kotlin/resolvers/LocationProviderFactory.kt
@@ -1,13 +1,14 @@
package org.jetbrains.dokka.resolvers
import org.jetbrains.dokka.pages.ModulePageNode
+import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
interface LocationProviderFactory {
- fun getLocationProvider(pageNode: ModulePageNode): LocationProvider
+ fun getLocationProvider(pageNode: RootPageNode): LocationProvider
}
class DefaultLocationProviderFactory(val context: DokkaContext) : LocationProviderFactory {
- override fun getLocationProvider(pageNode: ModulePageNode) = DefaultLocationProvider(pageNode, context)
+ override fun getLocationProvider(pageNode: RootPageNode) = DefaultLocationProvider(pageNode, context)
} \ No newline at end of file
diff --git a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt
index a2867504..cac9d44c 100644
--- a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt
+++ b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt
@@ -1,6 +1,5 @@
package org.jetbrains.dokka.transformers.documentation
-import com.intellij.openapi.diagnostic.logger
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.Function
import org.jetbrains.dokka.plugability.DokkaContext
diff --git a/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt b/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt
index 286835f9..45357060 100644
--- a/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt
+++ b/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt
@@ -1,8 +1,9 @@
package org.jetbrains.dokka.transformers.pages
import org.jetbrains.dokka.pages.ModulePageNode
+import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
interface PageNodeTransformer {
- operator fun invoke(input: ModulePageNode, dokkaContext: DokkaContext): ModulePageNode
+ operator fun invoke(input: RootPageNode): RootPageNode
} \ No newline at end of file
diff --git a/core/src/main/kotlin/utilities/nodeDebug.kt b/core/src/main/kotlin/utilities/nodeDebug.kt
index 984f13ed..0e8c61f7 100644
--- a/core/src/main/kotlin/utilities/nodeDebug.kt
+++ b/core/src/main/kotlin/utilities/nodeDebug.kt
@@ -34,7 +34,7 @@ fun Documentable.pretty(prefix: String = "", isLast: Boolean = true): String {
//}
private fun Any.stringify() = when(this) {
is ContentNode -> toString() + this.dci
- is PageNode -> this.name + this::class.simpleName
+ is ContentPage -> this.name + this::class.simpleName
else -> toString()
}
//private fun Any.allChildren() = when(this){
diff --git a/core/src/main/resources/dokka/scripts/navigationLoader.js b/core/src/main/resources/dokka/scripts/navigationLoader.js
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/core/src/main/resources/dokka/scripts/navigationLoader.js