diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-01-27 09:34:16 +0100 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-01-31 15:07:06 +0100 |
commit | 885ecd28153b484277c9ddcbf4a7f9d761a59545 (patch) | |
tree | db453e66762ebebb3ee05c0301b38c4465ea20a3 /core/src/main/kotlin/renderers | |
parent | c29605d92d1999434ecc79e774281a8280ae2823 (diff) | |
download | dokka-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.kt | 71 | ||||
-rw-r--r-- | core/src/main/kotlin/renderers/FileWriter.kt | 6 | ||||
-rw-r--r-- | core/src/main/kotlin/renderers/HtmlRenderer.kt | 138 | ||||
-rw-r--r-- | core/src/main/kotlin/renderers/Renderer.kt | 4 | ||||
-rw-r--r-- | core/src/main/kotlin/renderers/htmlPreprocessors.kt | 57 |
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" + ) + ) + } +} |