From 929764085298cbd8f2660aff281f7714909dc412 Mon Sep 17 00:00:00 2001 From: vmishenev Date: Wed, 19 Jan 2022 00:39:46 +0300 Subject: [PATCH 01/17] Add option to merge no expect-actual declarations --- .../src/main/kotlin/DokkaBaseConfiguration.kt | 4 +- .../SameMethodNamePageMergerStrategy.kt | 6 +- .../documentables/DefaultPageCreator.kt | 429 ++++++++++++------ .../documentables/PageContentBuilder.kt | 39 ++ 4 files changed, 331 insertions(+), 147 deletions(-) diff --git a/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt b/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt index 21757d702f..86a6016a1b 100644 --- a/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt +++ b/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt @@ -8,12 +8,14 @@ data class DokkaBaseConfiguration( var customStyleSheets: List = defaultCustomStyleSheets, var customAssets: List = defaultCustomAssets, var separateInheritedMembers: Boolean = separateInheritedMembersDefault, - var footerMessage: String = defaultFooterMessage + var footerMessage: String = defaultFooterMessage, + var mergeNoExpectActualDeclarations: Boolean = mergeNoExpectActualDeclarationsDefault ) : ConfigurableBlock { companion object { val defaultFooterMessage = "© ${Year.now().value} Copyright" val defaultCustomStyleSheets: List = emptyList() val defaultCustomAssets: List = emptyList() const val separateInheritedMembersDefault: Boolean = false + const val mergeNoExpectActualDeclarationsDefault: Boolean = false } } \ No newline at end of file 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 2fb70fc841..3b83050388 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt @@ -37,9 +37,9 @@ class SameMethodNamePageMergerStrategy(val logger: DokkaLogger) : PageMergerStra .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() + ((node.dfs { it is ContentDivergentGroup && it.groupID == g.groupID } as? ContentDivergentGroup) + ?.children ?: emptyList()) + ) ) } } diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index e2aca6f9f7..5fe6db7167 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -1,7 +1,11 @@ package org.jetbrains.dokka.base.translators.documentables +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.DokkaBaseConfiguration +import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions +import org.jetbrains.dokka.base.transformers.documentables.ClashingDriIdentifier import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder @@ -15,11 +19,6 @@ import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.kotlin.utils.addToStdlib.safeAs import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint -import org.jetbrains.dokka.base.transformers.documentables.ClashingDriIdentifier -import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider private typealias GroupedTags = Map, List>> @@ -35,6 +34,9 @@ open class DefaultPageCreator( ) { protected open val contentBuilder = PageContentBuilder(commentsToContentConverter, signatureProvider, logger) + protected val mergeNoExpectActualDeclarations = + configuration?.mergeNoExpectActualDeclarations ?: DokkaBaseConfiguration.mergeNoExpectActualDeclarationsDefault + protected val separateInheritedMembers = configuration?.separateInheritedMembers ?: DokkaBaseConfiguration.separateInheritedMembersDefault @@ -43,9 +45,13 @@ open class DefaultPageCreator( open fun pageForPackage(p: DPackage): PackagePageNode = PackagePageNode( p.name, contentForPackage(p), setOf(p.dri), p, - p.classlikes.renameClashingDocumentable().map(::pageForClasslike) + - p.functions.renameClashingDocumentable() - .map(::pageForFunction) + p.properties.mapNotNull(::pageForProperty) + if (mergeNoExpectActualDeclarations) + p.classlikes.mergeClashingDocumentable().map(::pageForClasslikesAndEntries) + + p.functions.mergeClashingDocumentable().map(::pageForFunctions) + + p.properties.mergeClashingDocumentable().map(::pageForProperties) + else p.classlikes.renameClashingDocumentable().map(::pageForClasslike) + + p.functions.renameClashingDocumentable().map(::pageForFunction) + + p.properties.mapNotNull(::pageForProperty) ) open fun pageForEnumEntry(e: DEnumEntry): ClasslikePageNode = @@ -70,32 +76,85 @@ open class DefaultPageCreator( ) } + open fun pageForClasslikesAndEntries(documentables: List): ClasslikePageNode { + val dri = documentables.dri.also { + if (it.size != 1) { + logger.error("Documentable dri should have the same one ${it.first()} inside the one page!") + } + } + + val constructors = documentables.flatMap { if (it is WithConstructors) it.constructors else emptyList() } + @Suppress("UNCHECKED_CAST") + val scopes = documentables as List + + val classlikes = scopes.flatMap { it.classlikes } + val functions = scopes.flatMap { it.filteredFunctions } + val props = scopes.flatMap { it.filteredProperties } + val entries = scopes.flatMap { if (it is DEnum) it.entries else emptyList() } + + val childrenPages = constructors.map(::pageForFunction) + + if (mergeNoExpectActualDeclarations) + (classlikes + entries).mergeClashingDocumentable().map(::pageForClasslikesAndEntries) + + functions.mergeClashingDocumentable().map(::pageForFunctions) + + props.mergeClashingDocumentable().map(::pageForProperties) + else + classlikes.renameClashingDocumentable().map(::pageForClasslike) + + functions.renameClashingDocumentable().map(::pageForFunction) + + props.renameClashingDocumentable().mapNotNull(::pageForProperty) + + entries.renameClashingDocumentable().map(::pageForEnumEntry) + + + return ClasslikePageNode( + documentables.first().nameAfterClash(), contentForClasslikesAndEntries(documentables), dri, documentables.singleOrNull(), + childrenPages + ) + } + private fun T.toClashedName() where T : Documentable, T : WithExtraProperties = (extra[ClashingDriIdentifier]?.value?.joinToString(", ", "[", "]") { it.displayName } ?: "") + name.orEmpty() - @Suppress("UNCHECKED_CAST") private fun List.renameClashingDocumentable(): List = groupBy { it.dri }.values.flatMap { elements -> if (elements.size == 1) elements else elements.mapNotNull { element -> - when (element) { - is DClass -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DObject -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DAnnotation -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DInterface -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DEnum -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DFunction -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DProperty -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - is DTypeAlias -> element.copy(extra = element.extra + DriClashAwareName(element.toClashedName())) - else -> null - } as? T? + element.renameClashingDocumentable() } } + @Suppress("UNCHECKED_CAST") + private fun T.renameClashingDocumentable():T? = when (this) { + is DClass -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DObject -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DAnnotation -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DInterface -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DEnum -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DFunction -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DProperty -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + is DTypeAlias -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) + else -> null + } as? T? + + private fun List.mergeClashingDocumentable(): List> = + groupBy { it.dri }.values.toList() + open fun pageForFunction(f: DFunction) = MemberPageNode(f.nameAfterClash(), contentForFunction(f), setOf(f.dri), f) + open fun pageForFunctions(fs: List): MemberPageNode { + val dri = fs.dri.also { if (it.size != 1) { + logger.error("Function dri should have the same one ${it.first()} inside the one page!") + } } + return MemberPageNode(fs.first().nameAfterClash(), contentForMembers(fs), dri, fs.singleOrNull()) + } + open fun pageForProperty(p: DProperty): MemberPageNode? = MemberPageNode(p.nameAfterClash(), contentForProperty(p), setOf(p.dri), p) + open fun pageForProperties(ps: List): MemberPageNode { + val dri = ps.dri.also { if (it.size != 1) { + logger.error("Property dri should have the same one ${it.first()} inside the one page!") + } } + return MemberPageNode(ps.first().nameAfterClash(), contentForMembers(ps), dri, ps.singleOrNull()) + } + private fun T.isInherited(): Boolean where T : Documentable, T : WithExtraProperties = sourceSets.all { sourceSet -> extra[InheritedMember]?.isInherited(sourceSet) == true } @@ -168,54 +227,95 @@ open class DefaultPageCreator( } } + protected open fun contentForScopes( + scopes: List, + sourceSets: Set + ): ContentGroup { + val types = listOf( + scopes.flatMap { it.classlikes }, + scopes.filterIsInstance().flatMap { it.typealiases } + ).flatten() + val inheritors = scopes.fold(HashMap>()) { acc, scope -> + val inheritorsForScope = scope.safeAs>()?.let { it.extra[InheritorsInfo] }?.let { inheritors -> + inheritors.value.filter { it.value.isNotEmpty() } + }.orEmpty() + inheritorsForScope.forEach { (k,v) -> + acc[k]?.plus(v) ?: acc.put(k,v) + } + acc + } + + return contentForScope( + @Suppress("UNCHECKED_CAST") + (scopes as List).dri, + sourceSets, + types, + scopes.flatMap { it.functions }, + scopes.flatMap { it.properties }, + inheritors + ) + } + protected open fun contentForScope( s: WithScope, dri: DRI, sourceSets: Set - ) = contentBuilder.contentFor(s as Documentable) { + ): ContentGroup { val types = listOf( s.classlikes, (s as? DPackage)?.typealiases ?: emptyList() ).flatten() + val inheritors = s.safeAs>()?.let { it.extra[InheritorsInfo] }?.let { inheritors -> + inheritors.value.filter { it.value.isNotEmpty() } + }.orEmpty() + + return contentForScope(setOf(dri), sourceSets, types, s.functions, s.properties, inheritors) + } + + protected open fun contentForScope( + dri: Set, + sourceSets: Set, + types: List, + functions: List, + properties: List, + inheritors: SourceSetDependent> + ) = contentBuilder.contentFor(dri, sourceSets) { divergentBlock("Types", types, ContentKind.Classlikes, extra = mainExtra + SimpleAttr.header("Types")) if (separateInheritedMembers) { - val (inheritedFunctions, memberFunctions) = s.functions.splitInherited() - val (inheritedProperties, memberProperties) = s.properties.splitInherited() + val (inheritedFunctions, memberFunctions) = functions.splitInherited() + val (inheritedProperties, memberProperties) = properties.splitInherited() propertiesBlock("Properties", memberProperties, sourceSets) propertiesBlock("Inherited properties", inheritedProperties, sourceSets) functionsBlock("Functions", memberFunctions) functionsBlock("Inherited functions", inheritedFunctions) } else { - functionsBlock("Functions", s.functions) - propertiesBlock("Properties", s.properties, sourceSets) + functionsBlock("Functions", functions) + propertiesBlock("Properties", properties, sourceSets) } - s.safeAs>()?.let { it.extra[InheritorsInfo] }?.let { inheritors -> - val map = inheritors.value.filter { it.value.isNotEmpty() } - if (map.values.any()) { - header(2, "Inheritors") { } - +ContentTable( - header = listOf(contentBuilder.contentFor(mainDRI, mainSourcesetData) { - text("Name") - }), - children = map.entries.flatMap { entry -> entry.value.map { Pair(entry.key, it) } } - .groupBy({ it.second }, { it.first }).map { (classlike, platforms) -> - val label = classlike.classNames?.substringAfterLast(".") ?: classlike.toString() - .also { logger.warn("No class name found for DRI $classlike") } - buildGroup( - setOf(classlike), - platforms.toSet(), - ContentKind.Inheritors, - extra = mainExtra + SymbolAnchorHint(label, ContentKind.Inheritors) - ) { - link(label, classlike) - } - }, - dci = DCI(setOf(dri), ContentKind.Inheritors), - sourceSets = sourceSets.toDisplaySourceSets(), - style = emptySet(), - extra = mainExtra + SimpleAttr.header("Inheritors") - ) - } + if (inheritors.values.any()) { + header(2, "Inheritors") { } + +ContentTable( + header = listOf(contentBuilder.contentFor(mainDRI, mainSourcesetData) { + text("Name") + }), + children = inheritors.entries.flatMap { entry -> entry.value.map { Pair(entry.key, it) } } + .groupBy({ it.second }, { it.first }).map { (classlike, platforms) -> + val label = classlike.classNames?.substringAfterLast(".") ?: classlike.toString() + .also { logger.warn("No class name found for DRI $classlike") } + buildGroup( + setOf(classlike), + platforms.toSet(), + ContentKind.Inheritors, + extra = mainExtra + SymbolAnchorHint(label, ContentKind.Inheritors) + ) { + link(label, classlike) + } + }, + dci = DCI(dri, ContentKind.Inheritors), + sourceSets = sourceSets.toDisplaySourceSets(), + style = emptySet(), + extra = mainExtra + SimpleAttr.header("Inheritors") + ) } } @@ -236,80 +336,97 @@ open class DefaultPageCreator( } } - protected open fun contentForClasslike(c: DClasslike) = contentBuilder.contentFor(c) { - @Suppress("UNCHECKED_CAST") - val extensions = (c as WithExtraProperties) - .extra[CallableExtensions]?.extensions - ?.filterIsInstance().orEmpty() - // Extensions are added to sourceSets since they can be placed outside the sourceSets from classlike - // Example would be an Interface in common and extension function in jvm - group(kind = ContentKind.Cover, sourceSets = mainSourcesetData + extensions.sourceSets) { - cover(c.name.orEmpty()) - sourceSetDependentHint(c.dri, c.sourceSets) { - +buildSignature(c) - +contentForDescription(c) + /** + * @param documentables a list of [DClasslike] and [DEnumEntry] with the same dri in different sourceSets + */ + protected open fun contentForClasslikesAndEntries(documentables: List): ContentGroup = + contentBuilder.contentFor(documentables.dri, documentables.sourceSets) { + val cs = documentables.filterIsInstance() + + @Suppress("UNCHECKED_CAST") + val extensions = (cs as List>).flatMap { + it.extra[CallableExtensions]?.extensions + ?.filterIsInstance().orEmpty() } - } - group(styles = setOf(ContentStyle.TabbedContent), sourceSets = mainSourcesetData + extensions.sourceSets) { - +contentForComments(c) - if (c is WithConstructors) { - block( - "Constructors", - 2, - ContentKind.Constructors, - c.constructors, - c.sourceSets, - needsAnchors = true, - extra = PropertyContainer.empty() + SimpleAttr.header("Constructors") - ) { - link(it.name, it.dri, kind = ContentKind.Main, styles = setOf(ContentStyle.RowTitle)) - sourceSetDependentHint( - it.dri, - it.sourceSets.toSet(), - kind = ContentKind.SourceSetDependentHint, - styles = emptySet(), - extra = PropertyContainer.empty() - ) { + // Extensions are added to sourceSets since they can be placed outside the sourceSets from classlike + // Example would be an Interface in common and extension function in jvm + group(kind = ContentKind.Cover, sourceSets = mainSourcesetData + extensions.sourceSets) { + cover(documentables.first().name.orEmpty()) + sourceSetDependentHint(documentables.dri, documentables.sourceSets) { + documentables.forEach { +buildSignature(it) - contentForBrief(it) + +contentForDescription(it) } } } - if (c is DEnum) { - block( - "Entries", - 2, - ContentKind.Classlikes, - c.entries, - c.sourceSets.toSet(), - needsSorting = false, - needsAnchors = true, - extra = mainExtra + SimpleAttr.header("Entries"), - styles = emptySet() - ) { - link(it.name, it.dri) - sourceSetDependentHint( - it.dri, - it.sourceSets.toSet(), - kind = ContentKind.SourceSetDependentHint, - extra = PropertyContainer.empty() + + group(styles = setOf(ContentStyle.TabbedContent), sourceSets = mainSourcesetData + extensions.sourceSets) { + +contentForComments(documentables) + val csWithConstructor = cs.filterIsInstance() + if (csWithConstructor.isNotEmpty()) { + block( + "Constructors", + 2, + ContentKind.Constructors, + csWithConstructor.flatMap { it.constructors } + .filter { it.extra[PrimaryConstructorExtra] == null || it.documentation.isNotEmpty() }, + @Suppress("UNCHECKED_CAST") + (csWithConstructor as List).sourceSets, + needsAnchors = true, + extra = PropertyContainer.empty() + SimpleAttr.header("Constructors") ) { - +buildSignature(it) - contentForBrief(it) + link(it.name, it.dri, kind = ContentKind.Main, styles = setOf(ContentStyle.RowTitle)) + sourceSetDependentHint( + it.dri, + it.sourceSets.toSet(), + kind = ContentKind.SourceSetDependentHint, + styles = emptySet(), + extra = PropertyContainer.empty() + ) { + +buildSignature(it) + contentForBrief(it) + } } } - } - +contentForScope(c, c.dri, c.sourceSets) + val csEnum = cs.filterIsInstance() + if (csEnum.isNotEmpty()) { + block( + "Entries", + 2, + ContentKind.Classlikes, + csEnum.flatMap { it.entries }, + csEnum.sourceSets, + needsSorting = false, + needsAnchors = true, + extra = mainExtra + SimpleAttr.header("Entries"), + styles = emptySet() + ) { + link(it.name, it.dri) + sourceSetDependentHint( + it.dri, + it.sourceSets.toSet(), + kind = ContentKind.SourceSetDependentHint, + extra = PropertyContainer.empty() + ) { + +buildSignature(it) + contentForBrief(it) + } + } + } + +contentForScopes(documentables.filterIsInstance(), documentables.sourceSets) - divergentBlock( - "Extensions", - extensions, - ContentKind.Extensions, - extra = mainExtra + SimpleAttr.header("Extensions") - ) + divergentBlock( + "Extensions", + extensions, + ContentKind.Extensions, + extra = mainExtra + SimpleAttr.header("Extensions") + ) + } } - } + + + protected open fun contentForClasslike(c: DClasslike) = contentForClasslikesAndEntries(listOf(c)) @Suppress("UNCHECKED_CAST") private inline fun GroupedTags.withTypeUnnamed(): SourceSetDependent = @@ -379,8 +496,8 @@ open class DefaultPageCreator( }.children } - private fun Documentable.getPossibleFallbackSourcesets(sourceSet: DokkaSourceSet) = - this.sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } + private fun Set.getPossibleFallbackSourcesets(sourceSet: DokkaSourceSet) = + this.filter { it.sourceSetID in sourceSet.dependentSourceSets } private fun Map.fallback(sourceSets: List): V? = sourceSets.firstOrNull { it in this.keys }.let { this[it] } @@ -388,8 +505,19 @@ open class DefaultPageCreator( protected open fun contentForComments( d: Documentable, isPlatformHintedContent: Boolean = true + ) = contentForComments(d.dri, d.sourceSets, d.groupedTags, isPlatformHintedContent) + + protected open fun contentForComments( + d: List, + isPlatformHintedContent: Boolean = true + ) = contentForComments(d.first().dri, d.sourceSets, d.groupedTags, isPlatformHintedContent) + + protected open fun contentForComments( + dri: DRI, + sourceSets: Set, + tags: GroupedTags, + isPlatformHintedContent: Boolean = true ): List { - val tags = d.groupedTags fun DocumentableContentBuilder.buildContent( platforms: Set, @@ -417,7 +545,7 @@ open class DefaultPageCreator( buildContent(availablePlatforms) { table(kind = ContentKind.Parameters, sourceSets = availablePlatforms) { availablePlatforms.forEach { platform -> - val possibleFallbacks = d.getPossibleFallbackSourcesets(platform) + val possibleFallbacks = sourceSets.getPossibleFallbackSourcesets(platform) params.mapNotNull { (_, param) -> (param[platform] ?: param.fallback(possibleFallbacks))?.let { row(sourceSets = setOf(platform), kind = ContentKind.Parameters) { @@ -451,7 +579,7 @@ open class DefaultPageCreator( buildContent(availablePlatforms) { table(kind = ContentKind.Sample) { availablePlatforms.forEach { platform -> - val possibleFallbacks = d.getPossibleFallbackSourcesets(platform) + val possibleFallbacks = sourceSets.getPossibleFallbackSourcesets(platform) seeAlsoTags.forEach { (_, see) -> (see[platform] ?: see.fallback(possibleFallbacks))?.let { row( @@ -537,12 +665,11 @@ open class DefaultPageCreator( } } - return contentBuilder.contentFor(d) { + return contentBuilder.contentFor(dri, sourceSets) { if (tags.isNotEmpty()) { contentForSamples() contentForSeeAlso() - if (d !is DProperty) - contentForParams() + contentForParams() contentForThrows() } }.children @@ -581,22 +708,27 @@ open class DefaultPageCreator( protected open fun contentForProperty(p: DProperty) = contentForMember(p) - protected open fun contentForMember(d: Documentable) = contentBuilder.contentFor(d) { - group(kind = ContentKind.Cover) { - cover(d.name.orEmpty()) - } - divergentGroup(ContentDivergentGroup.GroupID("member")) { - instance(setOf(d.dri), d.sourceSets.toSet()) { - divergent { - +buildSignature(d) - } - after { - +contentForDescription(d) - +contentForComments(d, isPlatformHintedContent = false) + protected open fun contentForMember(d: Documentable) = contentForMembers(listOf(d)) + + protected open fun contentForMembers(doumentables: List) = + contentBuilder.contentFor(doumentables.dri, doumentables.sourceSets) { + group(kind = ContentKind.Cover) { + cover(doumentables.first().name.orEmpty()) + } + divergentGroup(ContentDivergentGroup.GroupID("member")) { + doumentables.forEach { d -> + instance(setOf(d.dri), d.sourceSets) { + divergent { + +buildSignature(d) + } + after { + +contentForDescription(d) + +contentForComments(d, isPlatformHintedContent = false) + } + } } } } - } private fun DocumentableContentBuilder.functionsBlock(name: String, list: Collection) = divergentBlock( name, @@ -610,22 +742,24 @@ open class DefaultPageCreator( list: Collection, sourceSets: Set ) { - block( + multiBlock( name, 2, ContentKind.Properties, - list, + list.groupBy { it.name }.toList(), sourceSets, needsAnchors = true, extra = mainExtra + SimpleAttr.header(name), headers = listOf( headers("Name", "Summary") ) - ) { - link(it.name, it.dri, kind = ContentKind.Main) - sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependentHint) { - +buildSignature(it) - contentForBrief(it) + ) { key, props -> + link(key, props.first().dri, kind = ContentKind.Main) + sourceSetDependentHint(props.dri, props.sourceSets, kind = ContentKind.SourceSetDependentHint) { + props.forEach { + +buildSignature(it) + contentForBrief(it) + } } } } @@ -716,11 +850,19 @@ open class DefaultPageCreator( private val List.sourceSets: Set get() = flatMap { it.sourceSets }.toSet() + private val List.dri: Set + get() = map { it.dri }.toSet() + private val Documentable.groupedTags: GroupedTags get() = documentation.flatMap { (pd, doc) -> doc.children.asSequence().map { pd to it }.toList() }.groupBy { it.second::class } + private val List.groupedTags: GroupedTags + get() = this.flatMap { it.documentation.flatMap { (pd, doc) -> + doc.children.asSequence().map { pd to it }.toList() + } }.groupBy { it.second::class } + private val Documentable.descriptions: SourceSetDependent get() = groupedTags.withTypeUnnamed() @@ -730,6 +872,7 @@ open class DefaultPageCreator( private val Documentable.hasSeparatePage: Boolean get() = this !is DTypeAlias + @Suppress("UNCHECKED_CAST") private fun T.nameAfterClash(): String = ((this as? WithExtraProperties)?.extra?.get(DriClashAwareName)?.value ?: name).orEmpty() } diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index 8c3fcd84c5..fe2bc4abf0 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -227,6 +227,45 @@ open class PageContentBuilder( } } + fun multiBlock( + name: String, + level: Int, + kind: Kind = ContentKind.Main, + groupedElements: Iterable>>, + sourceSets: Set = mainSourcesetData, + styles: Set