Skip to content

Commit

Permalink
Cleanup paragraphs and tables in GFM renderer (#1946)
Browse files Browse the repository at this point in the history
* GFM renderer: cleanup paragraphs

* GFM renderer: cleanup tables

* GFM renderer: add tests for wrong header in table

Table with extra cell in row is really generated by `all-modules-page` plugin

* Remove commented-out lines

* Add BriefCommentPreprocessor which inserts a line break between a signature and a brief comment

Fixed a bug with `mapTransform` function which replaces table headers with their contents

Co-authored-by: Kamil Doległo <[email protected]>
  • Loading branch information
msink and lo2jaworzno authored Jun 25, 2021
1 parent c2182b7 commit 46b3371
Show file tree
Hide file tree
Showing 12 changed files with 379 additions and 102 deletions.
1 change: 1 addition & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3988,6 3988,7 @@ public final class org/jetbrains/dokka/pages/TextStyle : java/lang/Enum, org/jet

public final class org/jetbrains/dokka/pages/UtilsKt {
public static final fun mapTransform (Lorg/jetbrains/dokka/pages/ContentNode;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/dokka/pages/ContentNode;
public static final fun recursiveMapTransform (Lorg/jetbrains/dokka/pages/ContentNode;Lkotlin/reflect/KClass;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/dokka/pages/ContentNode;
}

public final class org/jetbrains/dokka/pages/WrongRendererTypeException : java/lang/Exception {
Expand Down
46 changes: 37 additions & 9 deletions core/src/main/kotlin/pages/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 5,55 @@ import kotlin.reflect.KClass
inline fun <reified T : ContentNode, R : ContentNode> R.mapTransform(noinline operation: (T) -> T): R =
mapTransform(T::class, operation)

inline fun <reified T : ContentNode, R : ContentNode> R.recursiveMapTransform(noinline operation: (T) -> T): R =
recursiveMapTransform(T::class, operation)

@PublishedApi
@Suppress("UNCHECKED_CAST")
internal fun <T : ContentNode, R : ContentNode> R.mapTransform(type: KClass<T>, operation: (T) -> T): R {
if (this::class == type) {
return operation(this as T) as R
}
val new = when (this) {
is ContentGroup -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentHeader -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentCodeBlock -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentCodeInline -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentTable -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentList -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentDivergentGroup -> this.copy(children.map { it.mapTransform(type, operation) })
is ContentDivergentInstance -> this.copy(
is ContentGroup -> copy(children = children.map { it.mapTransform(type, operation) })
is ContentHeader -> copy(children = children.map { it.mapTransform(type, operation) })
is ContentCodeBlock -> copy(children = children.map { it.mapTransform(type, operation) })
is ContentCodeInline -> copy(children = children.map { it.mapTransform(type, operation) })
is ContentTable -> copy(header = header.map { it.recursiveMapTransform(type, operation) }, children = children.map { it.recursiveMapTransform(type, operation) })
is ContentList -> copy(children = children.map { it.mapTransform(type, operation) })
is ContentDivergentGroup -> copy(children = children.map { it.mapTransform(type, operation) })
is ContentDivergentInstance -> copy(
before = before?.mapTransform(type, operation),
divergent = divergent.mapTransform(type, operation),
after = after?.mapTransform(type, operation)
)
is PlatformHintedContent -> this.copy(inner.mapTransform(type, operation))
is PlatformHintedContent -> copy(inner = inner.mapTransform(type, operation))
else -> this
}
return new as R
}

@PublishedApi
@Suppress("UNCHECKED_CAST")
internal fun <T : ContentNode, R : ContentNode> R.recursiveMapTransform(type: KClass<T>, operation: (T) -> T): R {
val new = when (this) {
is ContentGroup -> copy(children = children.map { it.recursiveMapTransform(type, operation) })
is ContentHeader -> copy(children = children.map { it.recursiveMapTransform(type, operation) })
is ContentCodeBlock -> copy(children = children.map { it.recursiveMapTransform(type, operation) })
is ContentCodeInline -> copy(children = children.map { it.recursiveMapTransform(type, operation) })
is ContentTable -> copy(header = header.map { it.recursiveMapTransform(type, operation) }, children = children.map { it.recursiveMapTransform(type, operation) })
is ContentList -> copy(children = children.map { it.recursiveMapTransform(type, operation) })
is ContentDivergentGroup -> copy(children = children.map { it.recursiveMapTransform(type, operation) })
is ContentDivergentInstance -> copy(
before = before?.recursiveMapTransform(type, operation),
divergent = divergent.recursiveMapTransform(type, operation),
after = after?.recursiveMapTransform(type, operation)
)
is PlatformHintedContent -> copy(inner = inner.recursiveMapTransform(type, operation))
else -> this
}
if (new::class == type) {
return operation(new as T) as R
}
return new as R
}
6 changes: 6 additions & 0 deletions plugins/gfm/api/gfm.api
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 11,7 @@ public final class org/jetbrains/dokka/gfm/GfmCommand$Companion {

public final class org/jetbrains/dokka/gfm/GfmPlugin : org/jetbrains/dokka/plugability/DokkaPlugin {
public fun <init> ()V
public final fun getBriefCommentPreprocessor ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getGfmPreprocessors ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getLocationProvider ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getPackageListCreator ()Lorg/jetbrains/dokka/plugability/Extension;
Expand All @@ -33,6 34,11 @@ public final class org/jetbrains/dokka/gfm/location/MarkdownLocationProvider$Fac
public fun getLocationProvider (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/gfm/location/MarkdownLocationProvider;
}

public final class org/jetbrains/dokka/gfm/renderer/BriefCommentPreprocessor : org/jetbrains/dokka/transformers/pages/PageTransformer {
public fun <init> ()V
public fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode;
}

public class org/jetbrains/dokka/gfm/renderer/CommonmarkRenderer : org/jetbrains/dokka/base/renderers/DefaultRenderer {
public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
public synthetic fun buildDRILink (Ljava/lang/Object;Lorg/jetbrains/dokka/pages/ContentDRILink;Lorg/jetbrains/dokka/pages/ContentPage;Ljava/util/Set;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 6,7 @@ import org.jetbrains.dokka.base.renderers.PackageListCreator
import org.jetbrains.dokka.base.renderers.RootCreator
import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat
import org.jetbrains.dokka.gfm.location.MarkdownLocationProvider
import org.jetbrains.dokka.gfm.renderer.BriefCommentPreprocessor
import org.jetbrains.dokka.gfm.renderer.CommonmarkRenderer
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.jetbrains.dokka.transformers.pages.PageTransformer
Expand All @@ -28,6 29,10 @@ class GfmPlugin : DokkaPlugin() {
gfmPreprocessors with RootCreator
}

val briefCommentPreprocessor by extending {
gfmPreprocessors with BriefCommentPreprocessor()
}

val packageListCreator by extending {
(gfmPreprocessors
providing { PackageListCreator(it, RecognizedLinkFormat.DokkaGFM) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 1,16 @@
package org.jetbrains.dokka.gfm.renderer

import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.transformers.pages.PageTransformer

class BriefCommentPreprocessor : PageTransformer {
override fun invoke(input: RootPageNode) = input.transformContentPagesTree { contentPage ->
contentPage.modified(content = contentPage.content.recursiveMapTransform<ContentGroup, ContentNode> {
if (it.dci.kind == ContentKind.BriefComment) {
it.copy(style = it.style setOf(TextStyle.Block))
} else {
it
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 26,7 @@ open class CommonmarkRenderer(
childrenCallback: StringBuilder.() -> Unit
) {
return when {
node.hasStyle(TextStyle.Block) -> {
childrenCallback()
buildParagraph()
}
node.hasStyle(TextStyle.Paragraph) -> {
node.hasStyle(TextStyle.Block) || node.hasStyle(TextStyle.Paragraph) -> {
buildParagraph()
childrenCallback()
buildParagraph()
Expand All @@ -43,7 39,7 @@ open class CommonmarkRenderer(
buildParagraph()
append("#".repeat(level) " ")
content()
appendNewLine()
buildParagraph()
}

override fun StringBuilder.buildLink(address: String, content: StringBuilder.() -> Unit) {
Expand All @@ -57,7 53,9 @@ open class CommonmarkRenderer(
pageContext: ContentPage,
sourceSetRestriction: Set<DisplaySourceSet>?
) {
buildParagraph()
buildListLevel(node, pageContext)
buildParagraph()
}

private fun StringBuilder.buildListItem(items: List<ContentNode>, pageContext: ContentPage) {
Expand Down Expand Up @@ -135,10 133,11 @@ open class CommonmarkRenderer(
}.groupBy(Pair<DisplaySourceSet, String>::second, Pair<DisplaySourceSet, String>::first)

distinct.filter { it.key.isNotBlank() }.forEach { (text, platforms) ->
append(" ")
buildParagraph()
buildSourceSetTags(platforms.toSet())
append(" $text")
appendNewLine()
buildNewLine()
append(text.trim())
buildParagraph()
}
}
}
Expand Down Expand Up @@ -170,46 169,40 @@ open class CommonmarkRenderer(
}
} else {
val size = node.header.firstOrNull()?.children?.size ?: node.children.firstOrNull()?.children?.size ?: 0
if (size <= 0) return

if (node.header.isNotEmpty()) {
node.header.forEach {
append("| ")
it.children.forEach {
append(" ")
append("| ")
it.build(this, pageContext, it.sourceSets)
append(" | ")
append(" ")
}
append("\n")
}
} else {
append("| ".repeat(size))
if (size > 0) {
append("|")
appendNewLine()
}
}
append("|")
appendNewLine()

append("|---".repeat(size))
if (size > 0) {
append("|")
appendNewLine()
}
append("|")
appendNewLine()

node.children.forEach {
val builder = StringBuilder()
it.children.forEach {
builder.append("| ")
builder.append("<a name=\"${it.dci.dri.first()}\"></a>")
builder.append(
buildString { it.build(this, pageContext) }.replace(
Regex("# "),
""
)
) // Workaround for headers inside tables
node.children.forEach { row ->
row.children.forEach { cell ->
append("| ")
append(buildString { cell.build(this, pageContext) }
.trim()
.replace("# ".toRegex(), "") // Workaround for headers inside tables
.replace("\\\n", "\n\n")
.replace("\n[\n] ".toRegex(), "<br>")
.replace("\n", " ")
)
append(" ")
}
append(builder.toString().withEntersAsHtml())
append("|".repeat(size 1 - it.children.size))
append("\n")
append("|")
appendNewLine()
}
}
}
Expand Down Expand Up @@ -259,25 252,22 @@ open class CommonmarkRenderer(
distinct.values.forEach { entry ->
val (instance, sourceSets) = entry.getInstanceAndSourceSets()

buildParagraph()
buildSourceSetTags(sourceSets)
buildNewLine()

instance.before?.let {
buildNewLine()
append("Brief description")
buildNewLine()
buildContentNode(
it,
pageContext,
sourceSets.first()
) // It's workaround to render content only once
buildParagraph()
}

buildNewLine()
append("Content")
entry.groupBy { buildString { buildContentNode(it.first.divergent, pageContext, setOf(it.second)) } }
.values.forEach { innerEntry ->
val (innerInstance, innerSourceSets) = innerEntry.getInstanceAndSourceSets()
buildNewLine()
if (sourceSets.size > 1) {
buildSourceSetTags(innerSourceSets)
buildNewLine()
Expand All @@ -287,12 277,10 @@ open class CommonmarkRenderer(
pageContext,
setOf(innerSourceSets.first())
) // It's workaround to render content only once
buildParagraph()
}

instance.after?.let {
buildNewLine()
append("More info")
buildNewLine()
buildContentNode(
it,
pageContext,
Expand Down Expand Up @@ -358,11 346,6 @@ open class CommonmarkRenderer(
}
}

private fun String.withEntersAsHtml(): String = this
.replace("\\\n", "\n\n")
.replace("\n[\n] ".toRegex(), "<br>")
.replace("\n", " ")

private fun List<Pair<ContentDivergentInstance, DisplaySourceSet>>.getInstanceAndSourceSets() =
this.let { Pair(it.first().first, it.map { it.second }.toSet()) }

Expand Down
Loading

0 comments on commit 46b3371

Please sign in to comment.