diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-02-17 09:32:35 +0100 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-02-18 13:28:23 +0100 |
commit | f625cef495d625d81ee22e950083f57cc4fab875 (patch) | |
tree | d4587be659154a3000ee58fe2297e55604e418e8 /plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt | |
parent | 3b200cf10e0c50c2eee4b9da3f7039d678fa4aad (diff) | |
download | dokka-f625cef495d625d81ee22e950083f57cc4fab875.tar.gz dokka-f625cef495d625d81ee22e950083f57cc4fab875.tar.bz2 dokka-f625cef495d625d81ee22e950083f57cc4fab875.zip |
Moves PsiToDocumentablesTranslator to the base plugin
Diffstat (limited to 'plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt')
-rw-r--r-- | plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt new file mode 100644 index 00000000..c9270681 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -0,0 +1,216 @@ +package org.jetbrains.dokka.renderers.html + +import kotlinx.html.* +import kotlinx.html.stream.createHTML +import org.jetbrains.dokka.base.renderers.DefaultRenderer +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.renderers.OutputWriter +import java.io.File + +open class HtmlRenderer( + context: DokkaContext +) : DefaultRenderer<FlowContent>(context) { + + private val pageList = mutableListOf<String>() + + override val preprocessors = listOf( + RootCreator, + SearchPageInstaller, + ResourceInstaller, + NavigationPageInstaller, + StyleAndScriptsAppender + ) + + override fun FlowContent.buildList(node: ContentList, pageContext: ContentPage) = + if (node.ordered) ol { + buildListItems(node.children, pageContext) + } + else ul { + buildListItems(node.children, pageContext) + } + + open fun OL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) { + items.forEach { + if (it is ContentList) + buildList(it, pageContext) + else + li { it.build(this, pageContext) } + } + } + + open fun UL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) { + items.forEach { + if (it is ContentList) + buildList(it, pageContext) + else + li { it.build(this, pageContext) } + } + } + + override fun FlowContent.buildResource( + node: ContentEmbeddedResource, + 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) { + //TODO: add imgAttrs parsing + val imgAttrs = node.extras.filterIsInstance<HTMLSimpleAttr>().joinAttr() + img(src = node.address, alt = node.altText) + } else { + println("Unrecognized resource type: $node") + } + } + + override fun FlowContent.buildTable(node: ContentTable, pageContext: ContentPage) { + table { + thead { + node.header.forEach { + tr { + it.children.forEach { + th { + it.build(this@table, pageContext) + } + } + } + } + } + tbody { + node.children.forEach { + tr { + it.children.forEach { + td { + it.build(this, pageContext) + } + } + } + } + } + } + } + + override fun FlowContent.buildHeader(level: Int, content: FlowContent.() -> Unit) { + when (level) { + 1 -> h1(block = content) + 2 -> h2(block = content) + 3 -> h3(block = content) + 4 -> h4(block = content) + 5 -> h5(block = content) + else -> h6(block = content) + } + } + + override fun FlowContent.buildNavigation(page: PageNode) = + locationProvider.ancestors(page).asReversed().forEach { node -> + text("/") + if (node.isNavigable) buildLink(node, page) + else text(node.name) + } + + private fun FlowContent.buildLink(to: PageNode, from: PageNode) = + buildLink(locationProvider.resolve(to, from)) { + text(to.name) + } + + fun FlowContent.buildLink( + to: DRI, + platforms: List<PlatformData>, + from: PageNode? = null, + block: FlowContent.() -> Unit + ) = buildLink(locationProvider.resolve(to, platforms, from), block) + + override fun buildError(node: ContentNode) { + context.logger.error("Unknown ContentNode type: $node") + } + + override fun FlowContent.buildNewLine() { + br() + } + + override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) = + a(href = address, block = content) + + 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!"); "" }) + buildNewLine() + } + } + + override fun renderPage(page: PageNode) { + super.renderPage(page) + if (page is ContentPage) { + pageList.add( + """{ "name": "${page.name}", ${if (page is ClasslikePageNode) "\"class\": \"${page.name}\"," else ""} "location": "${locationProvider.resolve( + page + )}" }""" + ) + } + } + + override fun FlowContent.buildText(textNode: ContentText) { + text(textNode.text) + } + + override fun render(root: RootPageNode) { + super.render(root) + outputWriter.write("scripts/pages", "var pages = [\n${pageList.joinToString(",\n")}\n]", ".js") + } + + private fun PageNode.root(path: String) = locationProvider.resolveRoot(this) + path + + override fun buildPage(page: ContentPage, content: (FlowContent, ContentPage) -> Unit): String = + buildHtml(page, page.embeddedResources) { content(this, page) } + + open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) = + createHTML().html { + head { + title(page.name) + with(resources) { + filter { it.substringBefore('?').substringAfterLast('.') == "css" } + .forEach { link(rel = LinkRel.stylesheet, href = page.root(it)) } + filter { it.substringBefore('?').substringAfterLast('.') == "js" } + .forEach { script(type = ScriptType.textJavaScript, src = page.root(it)) { async = true } } + } + script { unsafe { +"""var pathToRoot = "${locationProvider.resolveRoot(page)}";""" } } + } + body { + div { + id = "navigation" + div { + id = "searchBar" + form(action = page.root("-search.html"), method = FormMethod.get) { + id = "searchForm" + input(type = InputType.search, name = "query") + input(type = InputType.submit) { value = "Search" } + } + } + div { + id = "sideMenu" + } + } + div { + id = "content" + content() + } + } + } +} + +fun List<HTMLMetadata>.joinAttr() = joinToString(" ") { it.key + "=" + it.value } + +private fun PageNode.pageKind() = when (this) { + is PackagePageNode -> "package" + is ClasslikePageNode -> "class" + is MemberPageNode -> when (this.documentable) { + is Function -> "function" + else -> "other" + } + else -> "other" +} + +private val PageNode.isNavigable: Boolean + get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing
\ No newline at end of file |