From 8e5c63d035ef44a269b8c43430f43f5c8eebfb63 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Fri, 10 Nov 2023 11:46:54 +0100 Subject: Restructure the project to utilize included builds (#3174) * Refactor and simplify artifact publishing * Update Gradle to 8.4 * Refactor and simplify convention plugins and build scripts Fixes #3132 --------- Co-authored-by: Adam <897017+aSemy@users.noreply.github.com> Co-authored-by: Oleg Yukhnevich --- .../DefaultDocumentableToPageTranslator.kt | 34 - .../documentables/DefaultPageCreator.kt | 779 -------------------- .../documentables/DeprecationSectionCreator.kt | 194 ----- .../documentables/DescriptionSections.kt | 349 --------- .../translators/documentables/DriClashAwareName.kt | 13 - .../documentables/PageContentBuilder.kt | 781 --------------------- .../documentables/briefFromContentNodes.kt | 62 -- 7 files changed, 2212 deletions(-) delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt delete mode 100644 plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt (limited to 'plugins/base/src/main/kotlin/translators/documentables') diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt deleted file mode 100644 index 0b2597d5..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.plugability.* -import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -public class DefaultDocumentableToPageTranslator( - context: DokkaContext -) : DocumentableToPageTranslator { - private val configuration = configuration(context) - private val commentsToContentConverter = context.plugin().querySingle { commentsToContentConverter } - private val signatureProvider = context.plugin().querySingle { signatureProvider } - private val customTagContentProviders = context.plugin().query { customTagContentProvider } - private val documentableSourceLanguageParser = context.plugin().querySingle { documentableSourceLanguageParser } - private val logger = context.logger - - override fun invoke(module: DModule): ModulePageNode = - DefaultPageCreator( - configuration, - commentsToContentConverter, - signatureProvider, - logger, - customTagContentProviders, - documentableSourceLanguageParser - ).pageForModule(module) -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt deleted file mode 100644 index 5c8ac512..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ /dev/null @@ -1,779 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -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.transformers.documentation.ClashingDriIdentifier -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.base.utils.canonicalAlphabeticalOrder -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableSourceLanguageParser -import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableLanguage -import kotlin.reflect.KClass - -internal typealias GroupedTags = Map, List>> - -public open class DefaultPageCreator( - configuration: DokkaBaseConfiguration?, - commentsToContentConverter: CommentsToContentConverter, - signatureProvider: SignatureProvider, - public val logger: DokkaLogger, - public val customTagContentProviders: List = emptyList(), - public val documentableAnalyzer: DocumentableSourceLanguageParser -) { - protected open val contentBuilder: PageContentBuilder = PageContentBuilder( - commentsToContentConverter, signatureProvider, logger - ) - - protected val mergeImplicitExpectActualDeclarations: Boolean = - configuration?.mergeImplicitExpectActualDeclarations - ?: DokkaBaseConfiguration.mergeImplicitExpectActualDeclarationsDefault - - protected val separateInheritedMembers: Boolean = - configuration?.separateInheritedMembers ?: DokkaBaseConfiguration.separateInheritedMembersDefault - - public open fun pageForModule(m: DModule): ModulePageNode = - ModulePageNode(m.name.ifEmpty { "" }, contentForModule(m), listOf(m), m.packages.map(::pageForPackage)) - - /** - * We want to generate separated pages for no-actual typealias. - * Actual typealias are displayed on pages for their expect class (trough [ActualTypealias] extra). - * - * @see ActualTypealias - */ - private fun List.filterOutActualTypeAlias(): List { - fun List.hasExpectClass(dri: DRI) = find { it is DClasslike && it.dri == dri && it.expectPresentInSet != null } != null - return this.filterNot { it is DTypeAlias && this.hasExpectClass(it.dri) } - } - - public open fun pageForPackage(p: DPackage): PackagePageNode { - val children = if (mergeImplicitExpectActualDeclarations) { - (p.classlikes + p.typealiases).filterOutActualTypeAlias() - .mergeClashingDocumentable().map(::pageForClasslikes) + - p.functions.mergeClashingDocumentable().map(::pageForFunctions) + - p.properties.mergeClashingDocumentable().map(::pageForProperties) - } else { - (p.classlikes + p.typealiases).filterOutActualTypeAlias() - .renameClashingDocumentable().map(::pageForClasslike) + - p.functions.renameClashingDocumentable().map(::pageForFunction) + - p.properties.mapNotNull(::pageForProperty) - } - return PackagePageNode( - name = p.name, - content = contentForPackage(p), - dri = setOf(p.dri), - documentables = listOf(p), - children = children - ) - } - - public open fun pageForEnumEntry(e: DEnumEntry): ClasslikePageNode = pageForEnumEntries(listOf(e)) - - public open fun pageForClasslike(c: Documentable): ClasslikePageNode = pageForClasslikes(listOf(c)) - - public open fun pageForEnumEntries(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 classlikes = documentables.flatMap { it.classlikes } - val functions = documentables.flatMap { it.filteredFunctions } - val props = documentables.flatMap { it.filteredProperties } - - val childrenPages = if (mergeImplicitExpectActualDeclarations) - functions.mergeClashingDocumentable().map(::pageForFunctions) + - props.mergeClashingDocumentable().map(::pageForProperties) - else - classlikes.renameClashingDocumentable().map(::pageForClasslike) + - functions.renameClashingDocumentable().map(::pageForFunction) + - props.renameClashingDocumentable().mapNotNull(::pageForProperty) - - return ClasslikePageNode( - documentables.first().nameAfterClash(), contentForClasslikesAndEntries(documentables), dri, documentables, - childrenPages - ) - } - - /** - * @param documentables a list of [DClasslike] and [DTypeAlias] with the same dri in different sourceSets - */ - public open fun pageForClasslikes(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 classlikes = documentables.filterIsInstance() - - val constructors = - if (classlikes.shouldDocumentConstructors()) { - classlikes.flatMap { (it as? WithConstructors)?.constructors ?: emptyList() } - } else { - emptyList() - } - - val nestedClasslikes = classlikes.flatMap { it.classlikes } - val functions = classlikes.flatMap { it.filteredFunctions } - val props = classlikes.flatMap { it.filteredProperties } - val entries = classlikes.flatMap { if (it is DEnum) it.entries else emptyList() } - - val childrenPages = constructors.map(::pageForFunction) + - if (mergeImplicitExpectActualDeclarations) - nestedClasslikes.mergeClashingDocumentable().map(::pageForClasslikes) + - functions.mergeClashingDocumentable().map(::pageForFunctions) + - props.mergeClashingDocumentable().map(::pageForProperties) + - entries.mergeClashingDocumentable().map(::pageForEnumEntries) - else - nestedClasslikes.renameClashingDocumentable().map(::pageForClasslike) + - functions.renameClashingDocumentable().map(::pageForFunction) + - props.renameClashingDocumentable().mapNotNull(::pageForProperty) + - entries.renameClashingDocumentable().map(::pageForEnumEntry) - - - return ClasslikePageNode( - documentables.first().nameAfterClash(), contentForClasslikesAndEntries(documentables), dri, documentables, - childrenPages - ) - } - - private fun T.toClashedName() where T : Documentable, T : WithExtraProperties = - (extra[ClashingDriIdentifier]?.value?.joinToString(", ", "[", "]") { it.displayName } ?: "") + name.orEmpty() - - private fun List.renameClashingDocumentable(): List = - groupBy { it.dri }.values.flatMap { elements -> - if (elements.size == 1) elements else elements.mapNotNull { element -> - 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() - - public open fun pageForFunction(f: DFunction): MemberPageNode = - MemberPageNode(f.nameAfterClash(), contentForFunction(f), setOf(f.dri), listOf(f)) - - public 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) - } - - public open fun pageForProperty(p: DProperty): MemberPageNode? = - MemberPageNode(p.nameAfterClash(), contentForProperty(p), setOf(p.dri), listOf(p)) - - public 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) - } - - private fun T.isInherited(): Boolean where T : Documentable, T : WithExtraProperties = - sourceSets.all { sourceSet -> extra[InheritedMember]?.isInherited(sourceSet) == true } - - private val WithScope.filteredFunctions: List - get() = functions.filterNot { it.isInherited() } - - private val WithScope.filteredProperties: List - get() = properties.filterNot { it.isInherited() } - - private fun Collection.splitPropsAndFuns(): Pair, List> { - val first = ArrayList() - val second = ArrayList() - for (element in this) { - when (element) { - is DProperty -> first.add(element) - is DFunction -> second.add(element) - else -> throw IllegalStateException("Expected only properties or functions") - } - } - return Pair(first, second) - } - - private fun Collection.splitInheritedExtension(dri: Set): Pair, List> where T : org.jetbrains.dokka.model.Callable = - partition { it.receiver?.dri !in dri } - - private fun Collection.splitInherited(): Pair, List> where T : Documentable, T : WithExtraProperties = - partition { it.isInherited() } - - protected open fun contentForModule(m: DModule): ContentGroup { - return contentBuilder.contentFor(m) { - group(kind = ContentKind.Cover) { - cover(m.name) - if (contentForDescription(m).isNotEmpty()) { - sourceSetDependentHint( - m.dri, - m.sourceSets.toSet(), - kind = ContentKind.SourceSetDependentHint, - styles = setOf(TextStyle.UnderCoverText) - ) { - +contentForDescription(m) - } - } - } - - block( - name = "Packages", - level = 2, - kind = ContentKind.Packages, - elements = m.packages, - sourceSets = m.sourceSets.toSet(), - needsAnchors = true, - headers = listOf( - headers("Name") - ) - ) { - val documentations = it.sourceSets.map { platform -> - it.descriptions[platform]?.also { it.root } - } - val haveSameContent = - documentations.all { it?.root == documentations.firstOrNull()?.root && it?.root != null } - - link(it.name, it.dri) - if (it.sourceSets.size == 1 || (documentations.isNotEmpty() && haveSameContent)) { - documentations.first()?.let { firstParagraphComment(kind = ContentKind.Comment, content = it.root) } - } - } - } - } - - protected open fun contentForPackage(p: DPackage): ContentGroup { - return contentBuilder.contentFor(p) { - group(kind = ContentKind.Cover) { - cover("Package-level declarations") - if (contentForDescription(p).isNotEmpty()) { - sourceSetDependentHint( - dri = p.dri, - sourcesetData = p.sourceSets.toSet(), - kind = ContentKind.SourceSetDependentHint, - styles = setOf(TextStyle.UnderCoverText) - ) { - +contentForDescription(p) - } - } - } - group(styles = setOf(ContentStyle.TabbedContent), extra = mainExtra) { - +contentForScope(p, p.dri, p.sourceSets) - } - } - } - - protected open fun contentForScopes( - scopes: List, - sourceSets: Set, - extensions: List = emptyList() - ): ContentGroup { - val types = scopes.flatMap { it.classlikes } + scopes.filterIsInstance().flatMap { it.typealiases } - return contentForScope( - @Suppress("UNCHECKED_CAST") - (scopes as List).dri, - sourceSets, - types, - scopes.flatMap { it.functions }, - scopes.flatMap { it.properties }, - extensions - ) - } - - protected open fun contentForScope( - s: WithScope, - dri: DRI, - sourceSets: Set, - extensions: List = emptyList() - ): ContentGroup { - val types = listOf( - s.classlikes, - (s as? DPackage)?.typealiases ?: emptyList() - ).flatten() - return contentForScope(setOf(dri), sourceSets, types, s.functions, s.properties, extensions) - } - - private fun contentForScope( - dri: Set, - sourceSets: Set, - types: List, - functions: List, - properties: List, - extensions: List - ) = contentBuilder.contentFor(dri, sourceSets) { - divergentBlock( - "Types", - types, - ContentKind.Classlikes - ) - val (extensionProps, extensionFuns) = extensions.splitPropsAndFuns() - if (separateInheritedMembers) { - val (inheritedFunctions, memberFunctions) = functions.splitInherited() - val (inheritedProperties, memberProperties) = properties.splitInherited() - - val (inheritedExtensionFunctions, extensionFunctions) = extensionFuns.splitInheritedExtension(dri) - val (inheritedExtensionProperties, extensionProperties) = extensionProps.splitInheritedExtension(dri) - propertiesBlock( - "Properties", memberProperties + extensionProperties - ) - propertiesBlock( - "Inherited properties", inheritedProperties + inheritedExtensionProperties - ) - functionsBlock("Functions", memberFunctions + extensionFunctions) - functionsBlock( - "Inherited functions", inheritedFunctions + inheritedExtensionFunctions - ) - } else { - propertiesBlock( - "Properties", properties + extensionProps - ) - functionsBlock("Functions", functions + extensionFuns) - } - } - - private fun Iterable.sorted() = - sortedWith(compareBy({ it.name }, { it.parameters.size }, { it.dri.toString() })) - - /** - * @param documentables a list of [DClasslike] and [DEnumEntry] and [DTypeAlias] with the same dri in different sourceSets - */ - protected open fun contentForClasslikesAndEntries(documentables: List): ContentGroup = - contentBuilder.contentFor(documentables.dri, documentables.sourceSets) { - val classlikes = documentables.filterIsInstance() - - @Suppress("UNCHECKED_CAST") - val extensions = (classlikes as List>).flatMap { - it.extra[CallableExtensions]?.extensions - ?.filterIsInstance().orEmpty() - } - .distinctBy { it.sourceSets to it.dri } // [Documentable] has expensive equals/hashCode at the moment, see #2620 - - // 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) - +contentForDescription(it) - } - } - } - val csEnum = classlikes.filterIsInstance() - val csWithConstructor = classlikes.filterIsInstance() - val scopes = documentables.filterIsInstance() - val constructorsToDocumented = csWithConstructor.flatMap { it.constructors } - - group( - styles = setOf(ContentStyle.TabbedContent), - sourceSets = mainSourcesetData + extensions.sourceSets, - extra = mainExtra - ) { - if (constructorsToDocumented.isNotEmpty() && documentables.shouldDocumentConstructors()) { - +contentForConstructors(constructorsToDocumented, classlikes.dri, classlikes.sourceSets) - } - if (csEnum.isNotEmpty()) { - +contentForEntries(csEnum.flatMap { it.entries }, csEnum.dri, csEnum.sourceSets) - } - +contentForScopes(scopes, documentables.sourceSets, extensions) - } - } - protected open fun contentForConstructors( - constructorsToDocumented: List, - dri: Set, - sourceSets: Set - ): ContentGroup { - return contentBuilder.contentFor(dri, sourceSets) { - multiBlock( - name = "Constructors", - level = 2, - kind = ContentKind.Constructors, - groupedElements = constructorsToDocumented.groupBy { it.name } - .map { (_, v) -> v.first().name to v }, - sourceSets = (constructorsToDocumented as List).sourceSets, - needsAnchors = true, - extra = PropertyContainer.empty() + TabbedContentTypeExtra( - BasicTabbedContentType.CONSTRUCTOR - ), - ) { key, ds -> - link(key, ds.first().dri, kind = ContentKind.Main, styles = setOf(ContentStyle.RowTitle)) - sourceSetDependentHint( - dri = ds.dri, - sourceSets = ds.sourceSets, - kind = ContentKind.SourceSetDependentHint, - styles = emptySet(), - extra = PropertyContainer.empty() - ) { - ds.forEach { - +buildSignature(it) - contentForBrief(it) - } - } - } - } - } - - protected open fun contentForEntries( - entries: List, - dri: Set, - sourceSets: Set - ): ContentGroup { - return contentBuilder.contentFor(dri, sourceSets) { - multiBlock( - name = "Entries", - level = 2, - kind = ContentKind.Classlikes, - groupedElements = entries.groupBy { it.name }.toList(), - sourceSets = entries.sourceSets, - needsSorting = false, - needsAnchors = true, - extra = mainExtra + TabbedContentTypeExtra(BasicTabbedContentType.ENTRY), - styles = emptySet() - ) { key, ds -> - link(key, ds.first().dri) - sourceSetDependentHint( - dri = ds.dri, - sourceSets = ds.sourceSets, - kind = ContentKind.SourceSetDependentHint, - extra = PropertyContainer.empty() - ) { - ds.forEach { - +buildSignature(it) - contentForBrief(it) - } - } - } - } - } - - - - protected open fun contentForDescription( - d: Documentable - ): List { - val sourceSets = d.sourceSets.toSet() - val tags = d.groupedTags - - return contentBuilder.contentFor(d) { - deprecatedSectionContent(d, sourceSets) - - descriptionSectionContent(d, sourceSets) - customTagSectionContent(d, sourceSets, customTagContentProviders) - unnamedTagSectionContent(d, sourceSets) { toHeaderString() } - - paramsSectionContent(tags) - seeAlsoSectionContent(tags) - throwsSectionContent(tags) - samplesSectionContent(tags) - - inheritorsSectionContent(d, logger) - }.children - } - - protected open fun DocumentableContentBuilder.contentForBrief( - documentable: Documentable - ) { - documentable.sourceSets.forEach { sourceSet -> - documentable.documentation[sourceSet]?.let { - /* - Get description or a tag that holds documentation. - This tag can be either property or constructor but constructor tags are handled already in analysis so we - only need to keep an eye on property - - We purposefully ignore all other tags as they should not be visible in brief - */ - it.firstMemberOfTypeOrNull() ?: it.firstMemberOfTypeOrNull() - .takeIf { documentable is DProperty } - }?.let { - group(sourceSets = setOf(sourceSet), kind = ContentKind.BriefComment) { - createBriefComment(documentable, sourceSet, it) - } - } - } - } - - private fun DocumentableContentBuilder.createBriefComment( - documentable: Documentable, - sourceSet: DokkaSourceSet, - tag: TagWrapper - ) { - val language = documentableAnalyzer.getLanguage(documentable, sourceSet) - when(language) { - DocumentableLanguage.JAVA -> firstSentenceComment(tag.root) - DocumentableLanguage.KOTLIN -> firstParagraphComment(tag.root) - else -> firstParagraphComment(tag.root) - } - } - - protected open fun contentForFunction(f: DFunction): ContentGroup = contentForMember(f) - - protected open fun contentForProperty(p: DProperty): ContentGroup = contentForMember(p) - - protected open fun contentForMember(d: Documentable): ContentGroup = contentForMembers(listOf(d)) - - protected open fun contentForMembers(doumentables: List): ContentGroup = - 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) - } - } - } - } - } - - private fun DocumentableContentBuilder.functionsBlock( - name: String, - list: Collection - ) { - divergentBlock( - name, - list.sorted(), - ContentKind.Functions, - extra = mainExtra - ) - } - - private fun DocumentableContentBuilder.propertiesBlock( - name: String, - list: Collection - ) { - divergentBlock( - name, - list, - ContentKind.Properties, - extra = mainExtra - ) - - } - private data class NameAndIsExtension(val name:String?, val isExtension: Boolean) - - private fun groupAndSortDivergentCollection(collection: Collection): List>> { - val groupKeyComparator: Comparator>> = - compareBy>, String?>( - nullsFirst(canonicalAlphabeticalOrder) - ) { it.key.name } - .thenBy { it.key.isExtension } - - return collection - .groupBy { - NameAndIsExtension( - it.name, - it.isExtension() - ) - } // This groupBy should probably use LocationProvider - // This hacks displaying actual typealias signatures along classlike ones - .mapValues { if (it.value.any { it is DClasslike }) it.value.filter { it !is DTypeAlias } else it.value } - .entries.sortedWith(groupKeyComparator) - } - - protected open fun DocumentableContentBuilder.divergentBlock( - name: String, - collection: Collection, - kind: ContentKind, - extra: PropertyContainer = mainExtra - ) { - if (collection.any()) { - val onlyExtensions = collection.all { it.isExtension() } - val groupExtra = when(kind) { - ContentKind.Functions -> extra + TabbedContentTypeExtra(if (onlyExtensions) BasicTabbedContentType.EXTENSION_FUNCTION else BasicTabbedContentType.FUNCTION) - ContentKind.Properties -> extra + TabbedContentTypeExtra(if (onlyExtensions) BasicTabbedContentType.EXTENSION_PROPERTY else BasicTabbedContentType.PROPERTY) - ContentKind.Classlikes -> extra + TabbedContentTypeExtra(BasicTabbedContentType.TYPE) - else -> extra - } - - group(extra = groupExtra) { - // be careful: groupExtra will be applied for children by default - header(2, name, kind = kind, extra = extra) { } - val isFunctions = collection.any { it is DFunction } - table(kind, extra = extra, styles = emptySet()) { - header { - group { text("Name") } - group { text("Summary") } - } - groupAndSortDivergentCollection(collection) - .forEach { (elementNameAndIsExtension, elements) -> // This groupBy should probably use LocationProvider - val elementName = elementNameAndIsExtension.name - val isExtension = elementNameAndIsExtension.isExtension - val rowExtra = - if (isExtension) extra + TabbedContentTypeExtra(if(isFunctions) BasicTabbedContentType.EXTENSION_FUNCTION else BasicTabbedContentType.EXTENSION_PROPERTY) else extra - val rowKind = if (isExtension) ContentKind.Extensions else kind - val sortedElements = sortDivergentElementsDeterministically(elements) - row( - dri = sortedElements.map { it.dri }.toSet(), - sourceSets = sortedElements.flatMap { it.sourceSets }.toSet(), - kind = rowKind, - styles = emptySet(), - extra = elementName?.let { name -> rowExtra + SymbolAnchorHint(name, kind) } ?: rowExtra - ) { - link( - text = elementName.orEmpty(), - address = sortedElements.first().dri, - kind = rowKind, - styles = setOf(ContentStyle.RowTitle), - sourceSets = sortedElements.sourceSets.toSet(), - extra = extra - ) - divergentGroup( - ContentDivergentGroup.GroupID(name), - sortedElements.map { it.dri }.toSet(), - kind = rowKind, - extra = extra - ) { - sortedElements.map { element -> - instance( - setOf(element.dri), - element.sourceSets.toSet(), - extra = PropertyContainer.withAll( - SymbolAnchorHint(element.name ?: "", rowKind) - ) - ) { - divergent(extra = PropertyContainer.empty()) { - group { - +buildSignature(element) - } - } - after( - extra = PropertyContainer.empty() - ) { - contentForBrief(element) - contentForCustomTagsBrief(element) - } - } - } - } - } - } - } - } - } - } - - /** - * Divergent elements, such as extensions for the same receiver, can have identical signatures - * if they are declared in different places. If such elements are shown on the same page together, - * they need to be rendered deterministically to have reproducible builds. - * - * For example, you can have three identical extensions, if they are declared as: - * 1) top-level in package A - * 2) top-level in package B - * 3) inside a companion object in package A/B - * - * @see divergentBlock - * - * @param elements can contain types (annotation/class/interface/object/typealias), functions and properties - * @return the original list if it has one or zero elements - */ - private fun sortDivergentElementsDeterministically(elements: List): List = - elements.takeIf { it.size > 1 } // the majority are single-element lists, but no real benchmarks done - ?.sortedWith(divergentDocumentableComparator) - ?: elements - - private fun DocumentableContentBuilder.contentForCustomTagsBrief(documentable: Documentable) { - val customTags = documentable.customTags - if (customTags.isEmpty()) return - - documentable.sourceSets.forEach { sourceSet -> - customTags.forEach { (_, sourceSetTag) -> - sourceSetTag[sourceSet]?.let { tag -> - customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> - with(provider) { - contentForBrief(sourceSet, tag) - } - } - } - } - } - } - - protected open fun TagWrapper.toHeaderString(): String = this.javaClass.toGenericString().split('.').last() -} - -internal val List.sourceSets: Set - get() = flatMap { it.sourceSets }.toSet() - -internal val List.dri: Set - get() = map { it.dri }.toSet() - -internal val Documentable.groupedTags: GroupedTags - get() = documentation.flatMap { (pd, doc) -> - doc.children.map { pd to it }.toList() - }.groupBy { it.second::class } - -internal val Documentable.descriptions: SourceSetDependent - get() = groupedTags.withTypeUnnamed() - -internal val Documentable.customTags: Map> - get() = groupedTags.withTypeNamed() - -/** - * @see DefaultPageCreator.sortDivergentElementsDeterministically for usage - */ -private val divergentDocumentableComparator = - compareBy(nullsLast()) { it.dri.packageName } - .thenBy(nullsFirst()) { it.dri.classNames } // nullsFirst for top level to be first - .thenBy( - nullsLast( - compareBy { it.params.size } - .thenBy { it.signature() } - ) - ) { it.dri.callable } - -@Suppress("UNCHECKED_CAST") -private fun T.nameAfterClash(): String = - ((this as? WithExtraProperties)?.extra?.get(DriClashAwareName)?.value ?: name).orEmpty() - -@Suppress("UNCHECKED_CAST") -internal inline fun GroupedTags.withTypeUnnamed(): SourceSetDependent = - (this[T::class] as List>?)?.toMap().orEmpty() - -@Suppress("UNCHECKED_CAST") -internal inline fun GroupedTags.withTypeNamed(): Map> = - (this[T::class] as List>?) - ?.groupByTo(linkedMapOf()) { it.second.name } - ?.mapValues { (_, v) -> v.toMap() } - .orEmpty() - -// Annotations might have constructors to substitute reflection invocations -// and for internal/compiler purposes, but they are not expected to be documented -// and instantiated directly under normal circumstances, so constructors should not be rendered. -internal fun List.shouldDocumentConstructors() = !this.any { it is DAnnotation } diff --git a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt deleted file mode 100644 index 0f51578f..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations -import org.jetbrains.dokka.base.transformers.documentables.isDeprecated -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.pages.ContentKind -import org.jetbrains.dokka.pages.ContentStyle -import org.jetbrains.dokka.pages.TextStyle - -/** - * Main header for [Deprecated] section - */ -private const val DEPRECATED_HEADER_LEVEL = 3 - -/** - * Header for a direct parameter of [Deprecated] annotation, - * such as [Deprecated.message] and [Deprecated.replaceWith] - */ -private const val DIRECT_PARAM_HEADER_LEVEL = 4 - -internal fun PageContentBuilder.DocumentableContentBuilder.deprecatedSectionContent( - documentable: Documentable, - platforms: Set -) { - val allAnnotations = documentable.annotations() - if (allAnnotations.isEmpty()) { - return - } - - platforms.forEach { platform -> - val platformAnnotations = allAnnotations[platform] ?: emptyList() - val deprecatedPlatformAnnotations = platformAnnotations.filter { it.isDeprecated() } - - if (deprecatedPlatformAnnotations.isNotEmpty()) { - group(kind = ContentKind.Deprecation, sourceSets = setOf(platform), styles = emptySet()) { - val kotlinAnnotation = deprecatedPlatformAnnotations.find { it.dri.packageName == "kotlin" } - val javaAnnotation = deprecatedPlatformAnnotations.find { it.dri.packageName == "java.lang" } - - // If both annotations are present, priority is given to Kotlin's annotation since it - // contains more useful information, and Java's annotation is probably there - // for interop with Java callers, so it should be OK to ignore it - if (kotlinAnnotation != null) { - createKotlinDeprecatedSectionContent(kotlinAnnotation, platformAnnotations) - } else if (javaAnnotation != null) { - createJavaDeprecatedSectionContent(javaAnnotation) - } - } - } - } -} - -/** - * @see [DeprecatedSinceKotlin] - */ -private fun findDeprecatedSinceKotlinAnnotation(annotations: List): Annotations.Annotation? { - return annotations.firstOrNull { - it.dri.packageName == "kotlin" && it.dri.classNames == "DeprecatedSinceKotlin" - } -} - -/** - * Section with details for Kotlin's [kotlin.Deprecated] annotation - */ -private fun DocumentableContentBuilder.createKotlinDeprecatedSectionContent( - deprecatedAnnotation: Annotations.Annotation, - allAnnotations: List -) { - val deprecatedSinceKotlinAnnotation = findDeprecatedSinceKotlinAnnotation(allAnnotations) - header( - level = DEPRECATED_HEADER_LEVEL, - text = createKotlinDeprecatedHeaderText(deprecatedAnnotation, deprecatedSinceKotlinAnnotation) - ) - - deprecatedSinceKotlinAnnotation?.let { - createDeprecatedSinceKotlinFootnoteContent(it) - } - - deprecatedAnnotation.takeStringParam("message")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text(it) - } - } - - createReplaceWithSectionContent(deprecatedAnnotation) -} - -private fun createKotlinDeprecatedHeaderText( - kotlinDeprecatedAnnotation: Annotations.Annotation, - deprecatedSinceKotlinAnnotation: Annotations.Annotation? -): String { - if (deprecatedSinceKotlinAnnotation != null) { - // In this case there's no single level, it's dynamic based on api version, - // so there should be a footnote with levels and their respective versions - return "Deprecated" - } - - val deprecationLevel = kotlinDeprecatedAnnotation.params["level"]?.let { (it as? EnumValue)?.enumName } - return when (deprecationLevel) { - "DeprecationLevel.ERROR" -> "Deprecated (with error)" - "DeprecationLevel.HIDDEN" -> "Deprecated (hidden)" - else -> "Deprecated" - } -} - -/** - * Footnote for [DeprecatedSinceKotlin] annotation used in stdlib - * - * Notice that values are empty by default, so it's not guaranteed that all three will be set - */ -private fun DocumentableContentBuilder.createDeprecatedSinceKotlinFootnoteContent( - deprecatedSinceKotlinAnnotation: Annotations.Annotation -) { - group(styles = setOf(ContentStyle.Footnote)) { - deprecatedSinceKotlinAnnotation.takeStringParam("warningSince")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text("Warning since $it") - } - } - deprecatedSinceKotlinAnnotation.takeStringParam("errorSince")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text("Error since $it") - } - } - deprecatedSinceKotlinAnnotation.takeStringParam("hiddenSince")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text("Hidden since $it") - } - } - } -} - -/** - * Section for [ReplaceWith] parameter of [kotlin.Deprecated] annotation - */ -private fun DocumentableContentBuilder.createReplaceWithSectionContent(kotlinDeprecatedAnnotation: Annotations.Annotation) { - val replaceWithAnnotation = (kotlinDeprecatedAnnotation.params["replaceWith"] as? AnnotationValue)?.annotation - ?: return - - header( - level = DIRECT_PARAM_HEADER_LEVEL, - text = "Replace with" - ) - - // Signature: vararg val imports: String - val imports = (replaceWithAnnotation.params["imports"] as? ArrayValue) - ?.value - ?.mapNotNull { (it as? StringValue)?.value } - ?: emptyList() - - if (imports.isNotEmpty()) { - codeBlock(language = "kotlin", styles = setOf(TextStyle.Monospace)) { - imports.forEach { - text("import $it") - breakLine() - } - } - } - - replaceWithAnnotation.takeStringParam("expression")?.removeSurrounding("`")?.let { - codeBlock(language = "kotlin", styles = setOf(TextStyle.Monospace)) { - text(it) - } - } -} - -/** - * Section with details for Java's [java.lang.Deprecated] annotation - */ -private fun DocumentableContentBuilder.createJavaDeprecatedSectionContent( - deprecatedAnnotation: Annotations.Annotation, -) { - val isForRemoval = deprecatedAnnotation.takeBooleanParam("forRemoval", default = false) - header( - level = DEPRECATED_HEADER_LEVEL, - text = if (isForRemoval) "Deprecated (for removal)" else "Deprecated" - ) - deprecatedAnnotation.takeStringParam("since")?.let { - group(styles = setOf(ContentStyle.Footnote)) { - text("Since version $it") - } - } -} - -private fun Annotations.Annotation.takeBooleanParam(name: String, default: Boolean): Boolean = - (this.params[name] as? BooleanValue)?.value ?: default - -private fun Annotations.Annotation.takeStringParam(name: String): String? = - (this.params[name] as? StringValue)?.takeIf { it.value.isNotEmpty() }?.value diff --git a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt b/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt deleted file mode 100644 index e2489260..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo -import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.PointingToDeclaration -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.SourceSetDependent -import org.jetbrains.dokka.model.WithScope -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.orEmpty -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.pages.ContentKind -import org.jetbrains.dokka.pages.ContentStyle -import org.jetbrains.dokka.pages.TextStyle -import org.jetbrains.dokka.utilities.DokkaLogger -import kotlin.reflect.KClass -import kotlin.reflect.full.isSubclassOf - -internal const val KDOC_TAG_HEADER_LEVEL = 4 - -private val unnamedTagsExceptions: Set> = - setOf(Property::class, Description::class, Constructor::class, Param::class, See::class) - -internal fun PageContentBuilder.DocumentableContentBuilder.descriptionSectionContent( - documentable: Documentable, - sourceSets: Set, -) { - val descriptions = documentable.descriptions - if (descriptions.any { it.value.root.children.isNotEmpty() }) { - sourceSets.forEach { sourceSet -> - descriptions[sourceSet]?.also { - group(sourceSets = setOf(sourceSet), styles = emptySet()) { - comment(it.root) - } - } - } - } -} - -/** - * Custom tags are tags which are not part of the [KDoc specification](https://kotlinlang.org/docs/kotlin-doc.html). For instance, a user-defined tag - * which is specific to the user's code base would be considered a custom tag. - * - * For details, see [CustomTagContentProvider] - */ -internal fun PageContentBuilder.DocumentableContentBuilder.customTagSectionContent( - documentable: Documentable, - sourceSets: Set, - customTagContentProviders: List, -) { - val customTags = documentable.customTags - if (customTags.isEmpty()) return - - sourceSets.forEach { sourceSet -> - customTags.forEach { (_, sourceSetTag) -> - sourceSetTag[sourceSet]?.let { tag -> - customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> - group(sourceSets = setOf(sourceSet), styles = setOf(ContentStyle.KDocTag)) { - with(provider) { - contentForDescription(sourceSet, tag) - } - } - } - } - } - } -} - -/** - * Tags in KDoc are used in form of "@tag name value". - * This function handles tags that have only value parameter without name. - * List of such tags: `@return`, `@author`, `@since`, `@receiver` - */ -internal fun PageContentBuilder.DocumentableContentBuilder.unnamedTagSectionContent( - documentable: Documentable, - sourceSets: Set, - toHeaderString: TagWrapper.() -> String, -) { - val unnamedTags = documentable.groupedTags - .filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in unnamedTagsExceptions } - .values.flatten().groupBy { it.first } - .mapValues { it.value.map { it.second } } - .takeIf { it.isNotEmpty() } ?: return - - sourceSets.forEach { sourceSet -> - unnamedTags[sourceSet]?.let { tags -> - if (tags.isNotEmpty()) { - tags.groupBy { it::class }.forEach { (_, sameCategoryTags) -> - group(sourceSets = setOf(sourceSet), styles = setOf(ContentStyle.KDocTag)) { - header( - level = KDOC_TAG_HEADER_LEVEL, - text = sameCategoryTags.first().toHeaderString(), - styles = setOf() - ) - sameCategoryTags.forEach { comment(it.root, styles = setOf()) } - } - } - } - } - } -} - - -internal fun PageContentBuilder.DocumentableContentBuilder.paramsSectionContent(tags: GroupedTags) { - val params = tags.withTypeNamed() - if (params.isEmpty()) return - - val availableSourceSets = params.availableSourceSets() - tableSectionContentBlock( - blockName = "Parameters", - kind = ContentKind.Parameters, - sourceSets = availableSourceSets - ) { - availableSourceSets.forEach { sourceSet -> - val possibleFallbacks = availableSourceSets.getPossibleFallback(sourceSet) - params.mapNotNull { (_, param) -> - (param[sourceSet] ?: param.fallback(possibleFallbacks))?.let { - row(sourceSets = setOf(sourceSet), kind = ContentKind.Parameters) { - text( - it.name, - kind = ContentKind.Parameters, - styles = mainStyles + setOf(ContentStyle.RowTitle, TextStyle.Underlined) - ) - if (it.isNotEmpty()) { - comment(it.root) - } - } - } - } - } - } -} - -internal fun PageContentBuilder.DocumentableContentBuilder.seeAlsoSectionContent(tags: GroupedTags) { - val seeAlsoTags = tags.withTypeNamed() - if (seeAlsoTags.isEmpty()) return - - val availableSourceSets = seeAlsoTags.availableSourceSets() - tableSectionContentBlock( - blockName = "See also", - kind = ContentKind.Comment, - sourceSets = availableSourceSets - ) { - availableSourceSets.forEach { sourceSet -> - val possibleFallbacks = availableSourceSets.getPossibleFallback(sourceSet) - seeAlsoTags.forEach { (_, see) -> - (see[sourceSet] ?: see.fallback(possibleFallbacks))?.let { seeTag -> - row( - sourceSets = setOf(sourceSet), - kind = ContentKind.Comment - ) { - seeTag.address?.let { dri -> - link( - text = seeTag.name.removePrefix("${dri.packageName}."), - address = dri, - kind = ContentKind.Comment, - styles = mainStyles + ContentStyle.RowTitle - ) - } ?: text( - text = seeTag.name, - kind = ContentKind.Comment, - styles = mainStyles + ContentStyle.RowTitle - ) - if (seeTag.isNotEmpty()) { - comment(seeTag.root) - } - } - } - } - } - } -} - -/** - * Used for multi-value tags (e.g. params) when values are missed on some platforms. - * It this case description is inherited from parent platform. - * E.g. if param hasn't description in JVM, the description is taken from common. - */ -private fun Set.getPossibleFallback(sourceSet: DokkaConfiguration.DokkaSourceSet) = - this.filter { it.sourceSetID in sourceSet.dependentSourceSets } - -private fun Map.fallback(sourceSets: List): V? = - sourceSets.firstOrNull { it in this.keys }.let { this[it] } - -internal fun PageContentBuilder.DocumentableContentBuilder.throwsSectionContent(tags: GroupedTags) { - val throwsTags = tags.withTypeNamed() - if (throwsTags.isEmpty()) return - - val availableSourceSets = throwsTags.availableSourceSets() - tableSectionContentBlock( - blockName = "Throws", - kind = ContentKind.Main, - sourceSets = availableSourceSets - ) { - throwsTags.forEach { (throwsName, throwsPerSourceSet) -> - throwsPerSourceSet.forEach { (sourceSet, throws) -> - row(sourceSets = setOf(sourceSet)) { - group(styles = mainStyles + ContentStyle.RowTitle) { - throws.exceptionAddress?.let { - val className = it.takeIf { it.target is PointingToDeclaration }?.classNames - link(text = className ?: throwsName, address = it) - } ?: text(throwsName) - } - if (throws.isNotEmpty()) { - comment(throws.root) - } - } - } - } - } -} - -private fun TagWrapper.isNotEmpty() = this.children.isNotEmpty() - -internal fun PageContentBuilder.DocumentableContentBuilder.samplesSectionContent(tags: GroupedTags) { - val samples = tags.withTypeNamed() - if (samples.isEmpty()) return - - val availableSourceSets = samples.availableSourceSets() - - header(KDOC_TAG_HEADER_LEVEL, "Samples", kind = ContentKind.Sample, sourceSets = availableSourceSets) - availableSourceSets.forEach { sourceSet -> - group( - sourceSets = setOf(sourceSet), - kind = ContentKind.Sample, - styles = setOf(TextStyle.Monospace, ContentStyle.RunnableSample), - ) { - samples.filter { it.value.isEmpty() || sourceSet in it.value } - .forEach { text(text = it.key, sourceSets = setOf(sourceSet)) } - } - } -} - -internal fun PageContentBuilder.DocumentableContentBuilder.inheritorsSectionContent( - documentable: Documentable, - logger: DokkaLogger, -) { - val inheritors = if (documentable is WithScope) documentable.inheritors() else return - if (inheritors.values.none()) return - - // split content section for the case: - // parent is in the shared source set (without expect-actual) and inheritor is in the platform code - if (documentable.isDefinedInSharedSourceSetOnly(inheritors.keys.toSet())) - sharedSourceSetOnlyInheritorsSectionContent(inheritors, logger) - else - multiplatformInheritorsSectionContent(documentable, inheritors, logger) -} - -private fun WithScope.inheritors(): SourceSetDependent> { - @Suppress("UNCHECKED_CAST") - val withExtra = this as? WithExtraProperties - - return withExtra - ?.let { it.extra[InheritorsInfo] } - ?.let { inheritors -> inheritors.value.filter { it.value.isNotEmpty() } } - .orEmpty() -} - -/** - * Detect that documentable is located only in the shared code without expect-actuals - * Value of `analysisPlatform` will be [Platform.common] in cases if a source set shared between 2 different platforms. - * But if it shared between 2 same platforms (e.g. jvm("awt") and jvm("android")) - * then the source set will be still marked as jvm platform. - * - * So, we also try to check if any of inheritors source sets depends on current documentable source set. - * that will mean that the source set is shared. - */ -private fun Documentable.isDefinedInSharedSourceSetOnly(inheritorsSourceSets: Set) = - sourceSets.size == 1 && - (sourceSets.first().analysisPlatform == Platform.common - || sourceSets.first().hasDependentSourceSet(inheritorsSourceSets)) - -private fun DokkaConfiguration.DokkaSourceSet.hasDependentSourceSet( - sourceSets: Set, -) = - sourceSets.any { sourceSet -> sourceSet.dependentSourceSets.any { it == this.sourceSetID } } - -private fun PageContentBuilder.DocumentableContentBuilder.multiplatformInheritorsSectionContent( - documentable: Documentable, - inheritors: Map>, - logger: DokkaLogger, -) { - // intersect is used for removing duplication in case of merged classlikes from different platforms - val availableSourceSets = inheritors.keys.toSet().intersect(documentable.sourceSets) - - tableSectionContentBlock( - blockName = "Inheritors", - kind = ContentKind.Inheritors, - sourceSets = availableSourceSets - ) { - availableSourceSets.forEach { sourceSet -> - inheritors[sourceSet]?.forEach { classlike: DRI -> - inheritorRow(classlike, logger, sourceSet) - } - } - } -} - -private fun PageContentBuilder.DocumentableContentBuilder.sharedSourceSetOnlyInheritorsSectionContent( - inheritors: Map>, - logger: DokkaLogger, -) { - val uniqueInheritors = inheritors.values.flatten().toSet() - tableSectionContentBlock( - blockName = "Inheritors", - kind = ContentKind.Inheritors, - ) { - uniqueInheritors.forEach { classlike -> - inheritorRow(classlike, logger) - } - } -} - -private fun PageContentBuilder.TableBuilder.inheritorRow( - classlike: DRI, logger: DokkaLogger, sourceSet: DokkaConfiguration.DokkaSourceSet? = null, -) = row { - link( - text = classlike.friendlyClassName() - ?: classlike.toString().also { logger.warn("No class name found for DRI $classlike") }, - address = classlike, - sourceSets = sourceSet?.let { setOf(it) } ?: mainSourcesetData - ) -} - -private fun PageContentBuilder.DocumentableContentBuilder.tableSectionContentBlock( - blockName: String, - kind: ContentKind, - sourceSets: Set = mainSourcesetData, - body: PageContentBuilder.TableBuilder.() -> Unit, -) { - header(KDOC_TAG_HEADER_LEVEL, text = blockName, kind = kind, sourceSets = sourceSets) - table( - kind = kind, - sourceSets = sourceSets, - ) { - body() - } -} - -private fun DRI.friendlyClassName() = classNames?.substringAfterLast(".") - -private fun Map>.availableSourceSets() = values.flatMap { it.keys }.toSet() diff --git a/plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt b/plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt deleted file mode 100644 index 362bb9b9..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.properties.ExtraProperty - -public data class DriClashAwareName(val value: String?): ExtraProperty { - public companion object : ExtraProperty.Key - override val key: ExtraProperty.Key = Companion -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt deleted file mode 100644 index 4ddda674..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ /dev/null @@ -1,781 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint -import org.jetbrains.dokka.base.signatures.SignatureProvider -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.SourceSetDependent -import org.jetbrains.dokka.model.doc.DocTag -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.model.properties.plus -import org.jetbrains.dokka.model.toDisplaySourceSets -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.utilities.DokkaLogger - -@DslMarker -public annotation class ContentBuilderMarker - -public open class PageContentBuilder( - public val commentsConverter: Comm