aboutsummaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
authorBłażej Kardyś <bkardys@virtuslab.com>2019-12-20 23:45:04 +0100
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-01-15 11:47:10 +0100
commit9da523dd83af5f4bae5cda581baf1416092547a0 (patch)
treecd7f161cc1099de16e27db7e4a90177697ee7b98 /core/src/main
parente3ffd929230b675af020a707ea12923637b56872 (diff)
downloaddokka-9da523dd83af5f4bae5cda581baf1416092547a0.tar.gz
dokka-9da523dd83af5f4bae5cda581baf1416092547a0.tar.bz2
dokka-9da523dd83af5f4bae5cda581baf1416092547a0.zip
Rewriting HtmlRenderer to use kotlinx.html
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/kotlin/renderers/DefaultRenderer.kt67
-rw-r--r--core/src/main/kotlin/renderers/HtmlRenderer.kt159
2 files changed, 132 insertions, 94 deletions
diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt
index ae065305..e2ebf8b3 100644
--- a/core/src/main/kotlin/renderers/DefaultRenderer.kt
+++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -4,62 +4,67 @@ import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.resolvers.LocationProvider
-abstract class DefaultRenderer(
+abstract class DefaultRenderer<T>(
protected val fileWriter: FileWriter,
protected val locationProvider: LocationProvider,
protected val context: DokkaContext
) : Renderer {
- protected abstract fun buildHeader(level: Int, text: String): String
- protected abstract fun buildLink(text: String, address: String): String
- protected abstract fun buildList(node: ContentList, pageContext: PageNode): String
- protected abstract fun buildNewLine(): String
- protected abstract fun buildResource(node: ContentEmbeddedResource, pageContext: PageNode): String
- protected abstract fun buildTable(node: ContentTable, pageContext: PageNode): String
+ 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.buildNewLine()
+ protected abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: PageNode)
+ protected abstract fun T.buildTable(node: ContentTable, pageContext: PageNode)
+ protected abstract fun T.buildText(textNode: ContentText)
+ protected abstract fun T.buildNavigation(page: PageNode)
- protected open fun buildText(textNode: ContentText): String = textNode.text
+ protected abstract fun buildPage(page: PageNode, content: (T, PageNode) -> Unit): String
+ protected abstract fun buildError(node: ContentNode)
- protected open fun buildGroup(node: ContentGroup, pageContext: PageNode): String = node.children.joinToString("") { it.build(pageContext) }
-
- protected open fun buildLinkText(nodes: List<ContentNode>, pageContext: PageNode): String =
- nodes.joinToString(" ") { it.build(pageContext) }
+ protected open fun T.buildGroup(node: ContentGroup, pageContext: PageNode){
+ node.children.forEach { it.build(this, pageContext) }
+ }
- protected open fun buildCode(code: List<ContentNode>, language: String, pageContext: PageNode): String =
- code.joinToString { it.build(pageContext) }
+ protected open fun T.buildLinkText(nodes: List<ContentNode>, pageContext: PageNode) {
+ nodes.forEach { it.build(this, pageContext) }
+ }
- protected open fun buildHeader(node: ContentHeader, pageContext: PageNode): String =
- buildHeader(node.level, node.children.joinToString { it.build(pageContext) })
+ protected open fun T.buildCode(code: List<ContentNode>, language: String, pageContext: PageNode) {
+ code.forEach { it.build(this, pageContext) }
+ }
- protected open fun buildNavigation(page: PageNode): String =
- locationProvider.ancestors(page).fold("") { acc, node -> "$acc/${buildLink(
- node.name,
- locationProvider.resolve(node, page)
- )}" }
+ protected open fun T.buildHeader(node: ContentHeader, pageContext: PageNode) {
+ buildHeader(node.level) { node.children.forEach { it.build(this, pageContext) } }
+ }
- protected open fun ContentNode.build(pageContext: PageNode): String = buildContentNode(this, pageContext)
+ protected open fun ContentNode.build(builder: T, pageContext: PageNode) = builder.buildContentNode(this, pageContext)
- protected open fun buildContentNode(node: ContentNode, pageContext: PageNode): String =
+ protected open fun T.buildContentNode(node: ContentNode, pageContext: PageNode) {
when (node) {
is ContentText -> buildText(node)
is ContentHeader -> buildHeader(node, pageContext)
is ContentCode -> buildCode(node.children, node.language, pageContext)
is ContentDRILink -> buildLink(
- buildLinkText(node.children, pageContext),
- locationProvider.resolve(node.address, node.platforms.toList(), pageContext)
- )
- is ContentResolvedLink -> buildLink(buildLinkText(node.children, pageContext), node.address)
+ locationProvider.resolve(node.address, node.platforms.toList(), pageContext)) {
+ 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)
is ContentGroup -> buildGroup(node, pageContext)
- else -> "".also { println("Unrecognized ContentNode: $node") }
+ else -> buildError(node)
}
+ }
- protected open fun buildPageContent(page: PageNode): String =
- buildNavigation(page) + page.content.build(page)
+ protected open fun buildPageContent(context: T, page: PageNode) {
+ context.buildNavigation(page)
+ page.content.build(context, page)
+ }
protected open fun renderPage(page: PageNode) =
- fileWriter.write(locationProvider.resolve(page), buildPageContent(page), "")
+ fileWriter.write(locationProvider.resolve(page), buildPage(page, ::buildPageContent), "")
protected open fun renderPages(root: PageNode) {
renderPage(root)
diff --git a/core/src/main/kotlin/renderers/HtmlRenderer.kt b/core/src/main/kotlin/renderers/HtmlRenderer.kt
index 961e942c..2c450ae5 100644
--- a/core/src/main/kotlin/renderers/HtmlRenderer.kt
+++ b/core/src/main/kotlin/renderers/HtmlRenderer.kt
@@ -1,6 +1,7 @@
package org.jetbrains.dokka.renderers
-import org.jetbrains.dokka.utilities.htmlEscape
+import kotlinx.html.*
+import kotlinx.html.stream.appendHTML
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.resolvers.LocationProvider
@@ -11,71 +12,112 @@ open class HtmlRenderer(
fileWriter: FileWriter,
locationProvider: LocationProvider,
context: DokkaContext
-) : DefaultRenderer(fileWriter, locationProvider, context) {
+) : DefaultRenderer<FlowContent>(fileWriter, locationProvider, context) {
- override fun buildList(node: ContentList, pageContext: PageNode): String = if (node.ordered) {
- "<ol>${buildListItems(node.children, pageContext)}</ol>"
- } else {
- "<ul>${buildListItems(node.children, pageContext)}</ul>"
+ override fun FlowContent.buildList(node: ContentList, pageContext: PageNode) =
+ if (node.ordered) ol {
+ buildListItems(node.children, pageContext)
+ }
+ else ul {
+ buildListItems(node.children, pageContext)
+ }
+
+ protected open fun OL.buildListItems(items: List<ContentNode>, pageContext: PageNode) {
+ items.forEach {
+ if (it is ContentText)
+ li { it.build(this, pageContext) }
+ else buildList(it as ContentList, pageContext)
+ }
}
- protected open fun buildListItems(items: List<ContentNode>, pageContext: PageNode) =
- items.joinToString("") {
- if (it is ContentList) buildList(
- it,
- pageContext
- ) else "<li>\n${it.build(pageContext)}\n</li>\n"
+ protected open fun UL.buildListItems(items: List<ContentNode>, pageContext: PageNode) {
+ items.forEach {
+ if (it is ContentText)
+ li { it.build(this, pageContext) }
+ else buildList(it as ContentList, pageContext)
}
+ }
- override fun buildResource(
+ override fun FlowContent.buildResource(
node: ContentEmbeddedResource,
pageContext: PageNode
- ): String { // TODO: extension point there
+ ) { // 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}" $imgAttrs>"""
+ img(src = node.address, alt = node.altText)
} else {
println("Unrecognized resource type: $node")
- ""
}
}
- override fun buildTable(node: ContentTable, pageContext: PageNode): String {
- val tableHeader =
- """<thead>
- |<tr>
- |<th> ${node.header.joinToString("<th>\n</tr>\n<tr>\n<th>") { it.build(pageContext) }} </th>
- |</tr>
- |</thead>""".trimMargin()
-
- return """<table>
- |$tableHeader
- |<tbody>
- |<tr>
- |<td>${node.children.joinToString("</td>\n</tr>\n<tr>\n<td>\n") { it.buildTableRow(pageContext) }}</td>
- |</tr>
- |</tbody>
- |</table>""".trimMargin()
+ override fun FlowContent.buildTable(node: ContentTable, pageContext: PageNode) {
+ 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)
+ }
+ }
+ }
+ }
+ }
+ }
}
- protected open fun ContentGroup.buildTableRow(pageContext: PageNode) =
- children.joinToString("</td>\n<td>\n") { it.build(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)
+ else -> h5(block = content)
+ }
+ }
- override fun buildHeader(level: Int, text: String): String = "<h$level>$text</h$level>\n"
+ override fun FlowContent.buildNavigation(page: PageNode) {
+ locationProvider.ancestors(page).forEach { node ->
+ text("/")
+ buildLink(locationProvider.resolve(node, page)) {
+ text(node.name)
+ }
+ }
+ }
- override fun buildNewLine(): String = "<br/>"
+ override fun buildError(node: ContentNode) {
+ context.logger.error("Unknown ContentNode type: $node")
+ }
- override fun buildLink(text: String, address: String): String = "<a href=\"$address\">$text</a>"
+ override fun FlowContent.buildNewLine() { br() }
- override fun buildCode(code: List<ContentNode>, language: String, pageContext: PageNode): String =
- buildNewLine() + "<code>${code.joinToString("") { (it as ContentText).text + buildNewLine() }}</code>"
+ override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) {
+ a (href = address, block = content)
+ }
- override fun buildText(textNode: ContentText): String = super.buildText(textNode).htmlEscape()
+ override fun FlowContent.buildCode(code: List<ContentNode>, language: String, pageContext: PageNode) {
+ buildNewLine()
+ code.forEach {
+ + (it as ContentText).text
+ buildNewLine()
+ }
+ }
- override fun renderPage(page: PageNode) {
- val pageText = buildStartHtml(page) + buildPageContent(page) + buildEndHtml()
- fileWriter.write(locationProvider.resolve(page), pageText, "")
+ override fun FlowContent.buildText(textNode: ContentText) {
+ text(textNode.text)
}
override fun buildSupportFiles() { // TODO copy file instead of reading
@@ -85,26 +127,17 @@ open class HtmlRenderer(
)
}
- protected open fun buildScripts(page: PageNode) =
- page.embeddedResources.filter { URL(it).path.substringAfterLast('.') == "js" }
- .joinToString(separator = "") { """<script type = "text/javascript" async src = "$it"></script>""" + "\n" }
-
- protected open fun buildStartHtml(page: PageNode) = """<!DOCTYPE html>
- |<html>
- |<head>
- |<title>${page.name}</title>
- |<link rel="stylesheet" href="${locationProvider.resolveRoot(page)}style.css" />
- |${buildScripts(page)}
- |</head>
- |<body>
- |""".trimMargin()
-
- protected open fun buildEndHtml() =
- """
- |
- |</body>
- |</html>
- """.trimMargin()
+ override fun buildPage(page: PageNode, content: (FlowContent, PageNode) -> Unit): String = StringBuilder().appendHTML().html {
+ head {
+ title(page.name)
+ link(rel = LinkRel.stylesheet, href = "${locationProvider.resolveRoot(page)}style.css")
+ page.embeddedResources.filter { URL(it).path.substringAfterLast('.') == "js" }
+ .forEach { script(type = ScriptType.textJavaScript, src = it) { async = true } }
+ }
+ body {
+ content(this, page)
+ }
+ }.toString()
protected open fun List<HTMLMetadata>.joinAttr() = this.joinToString(" ") { it.key + "=" + it.value }
} \ No newline at end of file