From 430d7d6453a0b63dcabecd54aea915410cd35103 Mon Sep 17 00:00:00 2001 From: Kamil Doległo Date: Tue, 5 May 2020 11:53:16 +0200 Subject: Add a draft version of divergent rendering --- plugins/base/src/main/kotlin/DokkaBase.kt | 1 + .../src/main/kotlin/renderers/DefaultRenderer.kt | 21 ++- .../src/main/kotlin/renderers/html/HtmlRenderer.kt | 95 ++++++++++-- .../kotlin/signatures/KotlinSignatureProvider.kt | 2 +- .../merger/SameMethodNamePageMergerStrategy.kt | 40 ++--- .../pages/sourcelinks/SourceLinksTransformer.kt | 2 +- .../documentables/DefaultPageCreator.kt | 77 ++++++---- .../documentables/PageContentBuilder.kt | 166 ++++++++++++++++++--- 8 files changed, 319 insertions(+), 85 deletions(-) (limited to 'plugins/base/src/main/kotlin') diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index ab7d089d..5368207e 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -27,6 +27,7 @@ import org.jetbrains.dokka.base.translators.descriptors.DefaultDescriptorToDocum import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToPageTranslator import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.base.translators.psi.DefaultPsiToDocumentableTranslator +import org.jetbrains.dokka.pages.ContentDivergentGroup import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.transformers.pages.PageTransformer import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt index 1340529c..e229f3a6 100644 --- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.base.renderers import kotlinx.coroutines.* +import kotlinx.html.FlowContent import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.resolvers.local.LocationProvider import org.jetbrains.dokka.model.SourceSetData @@ -27,7 +28,7 @@ abstract class DefaultRenderer( abstract fun T.buildList( node: ContentList, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) abstract fun T.buildNewLine() @@ -35,7 +36,7 @@ abstract class DefaultRenderer( abstract fun T.buildTable( node: ContentTable, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) abstract fun T.buildText(textNode: ContentText) @@ -50,17 +51,20 @@ abstract class DefaultRenderer( open fun T.buildGroup( node: ContentGroup, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) = wrapGroup(node, pageContext) { node.children.forEach { it.build(this, pageContext, platformRestriction) } } + open fun T.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) = + node.children.forEach { it.build(this, pageContext) } + open fun T.wrapGroup(node: ContentGroup, pageContext: ContentPage, childrenCallback: T.() -> Unit) = childrenCallback() open fun T.buildLinkText( nodes: List, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) { nodes.forEach { it.build(this, pageContext, platformRestriction) } } @@ -72,7 +76,7 @@ abstract class DefaultRenderer( open fun T.buildHeader( node: ContentHeader, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) { buildHeader(node.level) { node.children.forEach { it.build(this, pageContext, platformRestriction) } } } @@ -80,16 +84,16 @@ abstract class DefaultRenderer( open fun ContentNode.build( builder: T, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) = builder.buildContentNode(this, pageContext, platformRestriction) open fun T.buildContentNode( node: ContentNode, pageContext: ContentPage, - platformRestriction: SourceSetData? = null + platformRestriction: Set? = null ) { - if (platformRestriction == null || platformRestriction in node.sourceSets) { + if (platformRestriction == null || node.sourceSets.any { it in platformRestriction } ) { when (node) { is ContentText -> buildText(node) is ContentHeader -> buildHeader(node, pageContext, platformRestriction) @@ -107,6 +111,7 @@ abstract class DefaultRenderer( is ContentGroup -> buildGroup(node, pageContext, platformRestriction) is ContentBreakLine -> buildNewLine() is PlatformHintedContent -> buildPlatformDependent(node, pageContext) + is ContentDivergentGroup -> buildDivergent(node, pageContext) else -> buildError(node) } } diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index caabcda7..97f0bdbd 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -40,14 +40,40 @@ open class HtmlRenderer( } } - override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) { + private fun FlowContent.wrapPlatformTagged( + node: ContentGroup, + pageContext: ContentPage, + childrenCallback: FlowContent.() -> Unit + ) { + div("platform-tagged") { + node.sourceSets.forEach { + div("platform-tag") { + if (it.sourceSetName.equals("common", ignoreCase = true)) classes = classes + "common" + text(it.sourceSetName) + } + } + div("content") { + childrenCallback() + } + } + } + + override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) = + buildPlatformDependent(content.sourceSets.map { it to setOf(content.inner) }.toMap(), pageContext) + + private fun FlowContent.buildPlatformDependent( + nodes: Map>, + pageContext: ContentPage + ) { div("platform-hinted") { attributes["data-platform-hinted"] = "data-platform-hinted" - val contents = content.sourceSets.mapIndexed { index, platform -> - platform to createHTML(prettyPrint = false).div(classes = "content") { + val contents = nodes.toList().mapIndexed { index, (sourceSet, elements) -> + sourceSet to createHTML(prettyPrint = false).div(classes = "content") { if (index == 0) attributes["data-active"] = "" - attributes["data-togglable"] = platform.sourceSetName - buildContentNode(content.inner, pageContext, platform) + attributes["data-togglable"] = sourceSet.sourceSetName + elements.forEach { + buildContentNode(it, pageContext, setOf(sourceSet)) + } } } @@ -69,17 +95,61 @@ open class HtmlRenderer( } } + override fun FlowContent.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { + val distinct = + node.children.map { instance -> + instance to Pair( + createHTML(prettyPrint = false).div { + instance.before?.let { before -> + buildContentNode(before, pageContext, instance.sourceSets) + } + }.drop(5).dropLast(6), + createHTML(prettyPrint = false).div { + instance.after?.let { after -> + buildContentNode(after, pageContext, instance.sourceSets) + } + }.drop(5).dropLast(6) // TODO: Find a way to do it without arbitrary trims + ) + + }.groupBy( + Pair>::second, + Pair>::first + ) + + distinct.forEach { + consumer.onTagContentUnsafe { +it.key.first } + consumer.onTagContentUnsafe { + +createHTML(prettyPrint = false).div { + if (node.implicitlySourceSetHinted) { + buildPlatformDependent( + it.value.groupBy { it.sourceSets } + .flatMap { (sourceSets, elements) -> + sourceSets.map { sourceSet -> sourceSet to elements.map { e -> e.divergent } } + }.toMap(), + pageContext + ) + } else { + it.value.forEach { + buildContentNode(it.divergent, pageContext, null) + } + } + }.drop(5).dropLast(6) + } + consumer.onTagContentUnsafe { +it.key.second } + } + } + override fun FlowContent.buildList( node: ContentList, pageContext: ContentPage, - sourceSetRestriction: SourceSetData? + sourceSetRestriction: Set? ) = if (node.ordered) ol { buildListItems(node.children, pageContext, sourceSetRestriction) } else ul { buildListItems(node.children, pageContext, sourceSetRestriction) } open fun OL.buildListItems( items: List, pageContext: ContentPage, - sourceSetRestriction: SourceSetData? = null + sourceSetRestriction: Set? = null ) { items.forEach { if (it is ContentList) @@ -92,7 +162,7 @@ open class HtmlRenderer( open fun UL.buildListItems( items: List, pageContext: ContentPage, - sourceSetRestriction: SourceSetData? = null + sourceSetRestriction: Set? = null ) { items.forEach { if (it is ContentList) @@ -119,18 +189,18 @@ open class HtmlRenderer( private fun FlowContent.buildRow( node: ContentGroup, pageContext: ContentPage, - sourceSetRestriction: SourceSetData? + sourceSetRestriction: Set? ) { node.children .filter { - sourceSetRestriction == null || sourceSetRestriction in it.sourceSets + sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } .takeIf { it.isNotEmpty() } ?.let { div(classes = "table-row") { it.filter { it.dci.kind != ContentKind.Symbol }.takeIf { it.isNotEmpty() }?.let { div("main-subrow ${node.style.joinToString { it.toString().decapitalize() }}") { - it.filter { sourceSetRestriction == null || sourceSetRestriction in it.sourceSets } + it.filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } .forEach { when(it.dci.kind){ ContentKind.SourceSetDependantHint -> { @@ -170,6 +240,7 @@ open class HtmlRenderer( } } + private fun FlowContent.createPlatformTags(node: ContentNode) { div("platform-tags") { node.sourceSets.forEach { @@ -184,7 +255,7 @@ open class HtmlRenderer( override fun FlowContent.buildTable( node: ContentTable, pageContext: ContentPage, - sourceSetRestriction: SourceSetData? + sourceSetRestriction: Set? ) { div(classes = "table") { node.children.forEach { diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt index 241103bf..3836f45d 100644 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -169,7 +169,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog is OtherParameter -> text(p.name) is TypeConstructor -> if (p.function) - +funType(this.mainDRI, this.mainPlatformData, p) + +funType(mainDRI.single(), mainPlatformData, p) else group { link(p.dri.classNames.orEmpty(), p.dri) diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt index 31e79555..77226310 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt @@ -4,34 +4,34 @@ import org.jetbrains.dokka.pages.* object SameMethodNamePageMergerStrategy : PageMergerStrategy { override fun tryMerge(pages: List, path: List): List { - val name = pages.first().name - val members = pages.filterIsInstance() - - if (members.isEmpty()) return pages - - val others = pages.filterNot { it is MemberPageNode } - - val resChildren = members.flatMap { it.children }.distinct() + val members = pages.filterIsInstance().takeIf { it.isNotEmpty() } ?: return pages + val name = pages.first().name.also { + if (pages.any { page -> page.name != it }) { // Is this even possible? + println("Page names for $it do not match!") // TODO pass logger here somehow + } + } val dri = members.flatMap { it.dri }.toSet() - val dci = DCI( - dri = dri, - kind = members.first().content.dci.kind - ) val merged = MemberPageNode( dri = dri, name = name, - children = resChildren, - content = asGroup( - dci, - members.map { it.content }), + children = members.flatMap { it.children }.distinct(), + content = squashDivergentInstances(members), documentable = null ) - return others + listOf(merged) + return (pages - members) + listOf(merged) } - fun asGroup(dci: DCI, nodes: List): ContentGroup = - nodes.first().let { ContentGroup(nodes, dci, it.sourceSets, it.style, it.extra) } - + private fun squashDivergentInstances(nodes: List): ContentNode = + nodes.map { it.content } + .reduce { acc, node -> + acc.mapTransform { g -> + g.copy(children = (g.children + + (node.dfs { it is ContentDivergentGroup && it.groupID == g.groupID } as? ContentDivergentGroup) + ?.children?.single() + ).filterNotNull() + ) + } + } } \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt index 876326d1..c7b65aa9 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt @@ -64,7 +64,7 @@ class SourceLinksTransformer(val context: DokkaContext, val builder: PageContent +ContentTable( emptyList(), sources.map { - buildGroup(node.dri.first(), setOf(it.first)) { + buildGroup(node.dri, setOf(it.first)) { +link("(source)", it.second) } }, diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 232f6721..51e95160 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -95,20 +95,8 @@ open class DefaultPageCreator( dri: DRI, sourceSets: List ) = contentBuilder.contentFor(s as Documentable) { - block("Types", 2, ContentKind.Classlikes, s.classlikes, sourceSets.toSet()) { - link(it.name ?: "", it.dri, kind = ContentKind.Main) - sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { - +buildSignature(it) - contentForBrief(it) - } - } - block("Functions", 2, ContentKind.Functions, s.functions, sourceSets.toSet()) { - link(it.name, it.dri, kind = ContentKind.Main) - sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { - +buildSignature(it) - contentForBrief(it) - } - } + divergentBlock("Types", s.classlikes, ContentKind.Classlikes) + divergentBlock("Functions", s.functions, ContentKind.Functions) block("Properties", 2, ContentKind.Properties, s.properties, sourceSets.toSet()) { link(it.name, it.dri, kind = ContentKind.Main) sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) { @@ -125,7 +113,7 @@ open class DefaultPageCreator( emptyList(), map.entries.flatMap { entry -> entry.value.map { Pair(entry.key, it) } } .groupBy({ it.second }, { it.first }).map { (classlike, platforms) -> - buildGroup(dri, platforms.toSet(), ContentKind.Inheritors) { + buildGroup(setOf(dri), platforms.toSet(), ContentKind.Inheritors) { link( classlike.classNames?.substringBeforeLast(".") ?: classlike.toString() .also { logger.warn("No class name found for DRI $classlike") }, classlike @@ -219,6 +207,7 @@ open class DefaultPageCreator( } } } + } } @@ -334,23 +323,59 @@ open class DefaultPageCreator( } } - protected open fun contentForFunction(f: DFunction) = contentBuilder.contentFor(f) { - group(kind = ContentKind.Cover) { - header(1) { text(f.name) } - sourceSetDependentHint(f.dri, f.sourceSets.toSet()) { - +buildSignature(f) + protected open fun contentForFunction(f: DFunction) = contentForMember(f) + protected open fun contentForTypeAlias(t: DTypeAlias) = contentForMember(t) + protected open fun contentForMember(d: Documentable) = contentBuilder.contentFor(d) { + header(1) { text(d.name.orEmpty()) } + divergentGroup(ContentDivergentGroup.GroupID("member")) { + instance(setOf(d.dri), d.sourceSets.toSet()) { + divergent(kind = ContentKind.Symbol) { + +buildSignature(d) + } + after { + +contentForComments(d) + } } } - +contentForComments(f) } - protected open fun contentForTypeAlias(t: DTypeAlias) = contentBuilder.contentFor(t) { - group(kind = ContentKind.Cover) { - header(1) { text(t.name) } - +buildSignature(t) + protected open fun DocumentableContentBuilder.divergentBlock( + name: String, + collection: Collection, + kind: ContentKind + ) { + if (collection.any()) { + header(2) { text(name) } + table(kind) { + collection.groupBy { it.name }.map { (elementName, elements) -> // This groupBy should probably use LocationProvider + buildGroup(elements.map { it.dri }.toSet(), elements.flatMap { it.sourceSets }.toSet()) { + link(elementName.orEmpty(), elements.first().dri) + divergentGroup( + ContentDivergentGroup.GroupID(name), + elements.map { it.dri }.toSet(), + kind = ContentKind.Symbol + ) { + elements.map { + instance(setOf(it.dri), it.sourceSets.toSet()) { + divergent { + group(kind = ContentKind.Symbol) { + +buildSignature(it) + } + } + after { + group(kind = ContentKind.BriefComment) { + contentForBrief(it) + } + } + } + } + } + } + } + } } - +contentForComments(t) } + protected open fun TagWrapper.toHeaderString() = this.javaClass.toGenericString().split('.').last() } diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index 3ed19afa..2f04e2a0 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -24,6 +24,18 @@ open class PageContentBuilder( styles: Set