diff options
Diffstat (limited to 'plugins')
19 files changed, 882 insertions, 307 deletions
diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index b17b4052..40ee6813 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -68,25 +68,29 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final class org/jetbrains/dokka/base/DokkaBaseConfiguration : org/jetbrains/dokka/plugability/ConfigurableBlock { public static final field Companion Lorg/jetbrains/dokka/base/DokkaBaseConfiguration$Companion; + public static final field mergeImplicitExpectActualDeclarationsDefault Z public static final field separateInheritedMembersDefault Z public fun <init> ()V - public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;)V - public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;Z)V + public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/util/List; public final fun component2 ()Ljava/util/List; public final fun component3 ()Z public final fun component4 ()Ljava/lang/String; - public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration; + public final fun component5 ()Z + public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;Z)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZILjava/lang/Object;)Lorg/jetbrains/dokka/base/DokkaBaseConfiguration; public fun equals (Ljava/lang/Object;)Z public final fun getCustomAssets ()Ljava/util/List; public final fun getCustomStyleSheets ()Ljava/util/List; public final fun getFooterMessage ()Ljava/lang/String; + public final fun getMergeImplicitExpectActualDeclarations ()Z public final fun getSeparateInheritedMembers ()Z public fun hashCode ()I public final fun setCustomAssets (Ljava/util/List;)V public final fun setCustomStyleSheets (Ljava/util/List;)V public final fun setFooterMessage (Ljava/lang/String;)V + public final fun setMergeImplicitExpectActualDeclarations (Z)V public final fun setSeparateInheritedMembers (Z)V public fun toString ()Ljava/lang/String; } @@ -1342,28 +1346,39 @@ public class org/jetbrains/dokka/base/translators/documentables/DefaultPageCreat public fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;)V public synthetic fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V protected fun contentForBrief (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/Documentable;)V - protected fun contentForClasslike (Lorg/jetbrains/dokka/model/DClasslike;)Lorg/jetbrains/dokka/pages/ContentGroup; + protected fun contentForClasslikesAndEntries (Ljava/util/List;)Lorg/jetbrains/dokka/pages/ContentGroup; + protected fun contentForComments (Ljava/util/List;Z)Ljava/util/List; + protected fun contentForComments (Lorg/jetbrains/dokka/links/DRI;Ljava/util/Set;Ljava/util/Map;Z)Ljava/util/List; protected fun contentForComments (Lorg/jetbrains/dokka/model/Documentable;Z)Ljava/util/List; + public static synthetic fun contentForComments$default (Lorg/jetbrains/dokka/base/translators/documentables/DefaultPageCreator;Ljava/util/List;ZILjava/lang/Object;)Ljava/util/List; + public static synthetic fun contentForComments$default (Lorg/jetbrains/dokka/base/translators/documentables/DefaultPageCreator;Lorg/jetbrains/dokka/links/DRI;Ljava/util/Set;Ljava/util/Map;ZILjava/lang/Object;)Ljava/util/List; public static synthetic fun contentForComments$default (Lorg/jetbrains/dokka/base/translators/documentables/DefaultPageCreator;Lorg/jetbrains/dokka/model/Documentable;ZILjava/lang/Object;)Ljava/util/List; protected fun contentForDescription (Lorg/jetbrains/dokka/model/Documentable;)Ljava/util/List; - protected fun contentForEnumEntry (Lorg/jetbrains/dokka/model/DEnumEntry;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForFunction (Lorg/jetbrains/dokka/model/DFunction;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForMember (Lorg/jetbrains/dokka/model/Documentable;)Lorg/jetbrains/dokka/pages/ContentGroup; + protected fun contentForMembers (Ljava/util/List;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForModule (Lorg/jetbrains/dokka/model/DModule;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForPackage (Lorg/jetbrains/dokka/model/DPackage;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForProperty (Lorg/jetbrains/dokka/model/DProperty;)Lorg/jetbrains/dokka/pages/ContentGroup; + protected fun contentForScope (Ljava/util/Set;Ljava/util/Set;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/Map;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForScope (Lorg/jetbrains/dokka/model/WithScope;Lorg/jetbrains/dokka/links/DRI;Ljava/util/Set;)Lorg/jetbrains/dokka/pages/ContentGroup; + protected fun contentForScopes (Ljava/util/List;Ljava/util/Set;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun divergentBlock (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;Ljava/util/Collection;Lorg/jetbrains/dokka/pages/ContentKind;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V public static synthetic fun divergentBlock$default (Lorg/jetbrains/dokka/base/translators/documentables/DefaultPageCreator;Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;Ljava/util/Collection;Lorg/jetbrains/dokka/pages/ContentKind;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)V protected fun getContentBuilder ()Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder; public final fun getCustomTagContentProviders ()Ljava/util/List; public final fun getLogger ()Lorg/jetbrains/dokka/utilities/DokkaLogger; + protected final fun getMergeImplicitExpectActualDeclarations ()Z protected final fun getSeparateInheritedMembers ()Z public fun pageForClasslike (Lorg/jetbrains/dokka/model/DClasslike;)Lorg/jetbrains/dokka/pages/ClasslikePageNode; + public fun pageForClasslikes (Ljava/util/List;)Lorg/jetbrains/dokka/pages/ClasslikePageNode; + public fun pageForEnumEntries (Ljava/util/List;)Lorg/jetbrains/dokka/pages/ClasslikePageNode; public fun pageForEnumEntry (Lorg/jetbrains/dokka/model/DEnumEntry;)Lorg/jetbrains/dokka/pages/ClasslikePageNode; public fun pageForFunction (Lorg/jetbrains/dokka/model/DFunction;)Lorg/jetbrains/dokka/pages/MemberPageNode; + public fun pageForFunctions (Ljava/util/List;)Lorg/jetbrains/dokka/pages/MemberPageNode; public fun pageForModule (Lorg/jetbrains/dokka/model/DModule;)Lorg/jetbrains/dokka/pages/ModulePageNode; public fun pageForPackage (Lorg/jetbrains/dokka/model/DPackage;)Lorg/jetbrains/dokka/pages/PackagePageNode; + public fun pageForProperties (Ljava/util/List;)Lorg/jetbrains/dokka/pages/MemberPageNode; public fun pageForProperty (Lorg/jetbrains/dokka/model/DProperty;)Lorg/jetbrains/dokka/pages/MemberPageNode; protected fun toHeaderString (Lorg/jetbrains/dokka/model/doc/TagWrapper;)Ljava/lang/String; } @@ -1462,6 +1477,8 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild public static synthetic fun linkNode$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/pages/DCI;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)Lorg/jetbrains/dokka/pages/ContentDRILink; public final fun list (Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function2;)V public static synthetic fun list$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public final fun multiBlock (Ljava/lang/String;ILorg/jetbrains/dokka/pages/Kind;Ljava/lang/Iterable;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZZLjava/util/List;ZLkotlin/jvm/functions/Function3;)V + public static synthetic fun multiBlock$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;ILorg/jetbrains/dokka/pages/Kind;Ljava/lang/Iterable;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZZLjava/util/List;ZLkotlin/jvm/functions/Function3;ILjava/lang/Object;)V public final fun operator (Ljava/lang/String;)V public final fun orderedList (Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)V public static synthetic fun orderedList$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V diff --git a/plugins/base/base-test-utils/api/base-test-utils.api b/plugins/base/base-test-utils/api/base-test-utils.api index b6ec84a1..a0b535f2 100644 --- a/plugins/base/base-test-utils/api/base-test-utils.api +++ b/plugins/base/base-test-utils/api/base-test-utils.api @@ -62,8 +62,8 @@ public final class org/jetbrains/dokka/base/testApi/testRunner/BaseTestMethods : } public final class renderers/RawTestPage : org/jetbrains/dokka/pages/RootPageNode, org/jetbrains/dokka/pages/ContentPage { - public fun <init> (Lorg/jetbrains/dokka/pages/ContentNode;Ljava/lang/String;Ljava/util/Set;Lorg/jetbrains/dokka/model/Documentable;Ljava/util/List;Ljava/util/List;)V - public synthetic fun <init> (Lorg/jetbrains/dokka/pages/ContentNode;Ljava/lang/String;Ljava/util/Set;Lorg/jetbrains/dokka/model/Documentable;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Lorg/jetbrains/dokka/pages/ContentNode;Ljava/lang/String;Ljava/util/Set;Ljava/util/List;Ljava/util/List;)V + public synthetic fun <init> (Lorg/jetbrains/dokka/pages/ContentNode;Ljava/lang/String;Ljava/util/Set;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getChildren ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; diff --git a/plugins/base/base-test-utils/src/main/kotlin/renderers/TestPage.kt b/plugins/base/base-test-utils/src/main/kotlin/renderers/TestPage.kt index 67ba2dd3..4066c7c6 100644 --- a/plugins/base/base-test-utils/src/main/kotlin/renderers/TestPage.kt +++ b/plugins/base/base-test-utils/src/main/kotlin/renderers/TestPage.kt @@ -1,15 +1,14 @@ package renderers import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.doc.DocTag import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.DokkaConsoleLogger -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder -import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter fun testPage(callback: PageContentBuilder.DocumentableContentBuilder.() -> Unit): RawTestPage { val content = PageContentBuilder( @@ -29,7 +28,6 @@ class RawTestPage( override val content: ContentNode, override val name: String = "testPage", override val dri: Set<DRI> = setOf(DRI.topLevel), - override val documentable: Documentable? = null, override val embeddedResources: List<String> = emptyList(), override val children: List<PageNode> = emptyList(), ): RootPageNode(), ContentPage { diff --git a/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt b/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt index 21757d70..8ea8818d 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<File> = defaultCustomStyleSheets, var customAssets: List<File> = defaultCustomAssets, var separateInheritedMembers: Boolean = separateInheritedMembersDefault, - var footerMessage: String = defaultFooterMessage + var footerMessage: String = defaultFooterMessage, + var mergeImplicitExpectActualDeclarations: Boolean = mergeImplicitExpectActualDeclarationsDefault ) : ConfigurableBlock { companion object { val defaultFooterMessage = "© ${Year.now().value} Copyright" val defaultCustomStyleSheets: List<File> = emptyList() val defaultCustomAssets: List<File> = emptyList() const val separateInheritedMembersDefault: Boolean = false + const val mergeImplicitExpectActualDeclarationsDefault: Boolean = false } }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index 43526dc3..3297d09f 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -1,14 +1,14 @@ package org.jetbrains.dokka.base.renderers.html -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.base.renderers.sourceSets -import org.jetbrains.dokka.base.templating.AddToSearch import org.jetbrains.dokka.base.templating.AddToSourcesetDependencies import org.jetbrains.dokka.base.templating.toJsonString -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.DEnumEntry +import org.jetbrains.dokka.model.DFunction +import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.configuration @@ -30,19 +30,18 @@ abstract class NavigationDataProvider { when { this !is ClasslikePageNode -> children.filterIsInstance<ContentPage>().map { visit(it) } - documentable is DEnum -> - children.filter { it is ContentPage && it.documentable is DEnumEntry }.map { visit(it as ContentPage) } + documentables.any { it is DEnum } -> + children.filter { it is WithDocumentables && it.documentables.any { it is DEnumEntry } } + .map { visit(it as ContentPage) } else -> emptyList() }.sortedBy { it.name.toLowerCase() } /** - * Parenthesis is applied in 2 cases: + * Parenthesis is applied in 1 case: * - page only contains functions (therefore documentable from this page is [DFunction]) - * - page merges only functions (documentable is null because [SameMethodNamePageMergerStrategy][org.jetbrains.dokka.base.transformers.pages.merger.SameMethodNamePageMergerStrategy] - * removes it from page) */ private val ContentPage.displayableName: String - get() = if (documentable is DFunction || (documentable == null && dri.all { it.callable != null })) { + get() = if (this is WithDocumentables && documentables.all { it is DFunction }) { "$name()" } else { name 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 2fb70fc8..6c12c719 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt @@ -23,7 +23,7 @@ class SameMethodNamePageMergerStrategy(val logger: DokkaLogger) : PageMergerStra children = members.flatMap { it.children }.distinct(), content = squashDivergentInstances(members).withSourceSets(members.allSourceSets()), embeddedResources = members.flatMap { it.embeddedResources }.distinct(), - documentable = null + documentables = members.flatMap { it.documentables } ) return (pages - members) + listOf(merged) @@ -37,9 +37,9 @@ class SameMethodNamePageMergerStrategy(val logger: DokkaLogger) : PageMergerStra .reduce { acc, node -> acc.mapTransform<ContentDivergentGroup, ContentNode> { 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/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt index 3ff7a77d..db133bb8 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt @@ -35,14 +35,18 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { "<script src=\"https://unpkg.com/kotlin-playground@1\"></script>" return input.transformContentPagesTree { page -> - page.documentable?.documentation?.entries?.fold(page) { acc, entry -> - entry.value.children.filterIsInstance<Sample>().fold(acc) { acc, sample -> + val samples = (page as? WithDocumentables)?.documentables?.flatMap { + it.documentation.entries.flatMap { entry -> + entry.value.children.filterIsInstance<Sample>().map { entry.key to it } + } + } + + samples?.fold(page as ContentPage) { acc, (sampleSourceSet, sample) -> acc.modified( - content = acc.content.addSample(page, entry.key, sample.name, analysis), + content = acc.content.addSample(page, sampleSourceSet, sample.name, analysis), embeddedResources = acc.embeddedResources + kotlinPlaygroundScript ) - } - } ?: page + } ?: page } } 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 f66ff222..93305055 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt @@ -1,15 +1,15 @@ package org.jetbrains.dokka.base.transformers.pages.sourcelinks -import com.intellij.psi.PsiElement import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder -import org.jetbrains.dokka.model.DocumentableSource import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.analysis.DescriptorDocumentableSource import org.jetbrains.dokka.analysis.PsiDocumentableSource import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.model.DocumentableSource import org.jetbrains.dokka.model.WithSources import org.jetbrains.dokka.model.toDisplaySourceSets import org.jetbrains.dokka.pages.* @@ -32,8 +32,9 @@ class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { override fun invoke(input: RootPageNode) = input.transformContentPagesTree { node -> - when (val documentable = node.documentable) { - is WithSources -> resolveSources(documentable) + when (node) { + is WithDocumentables -> + node.documentables.filterIsInstance<WithSources>().flatMap { resolveSources(it) } .takeIf { it.isNotEmpty() } ?.let { node.addSourcesContent(it) } ?: node @@ -65,23 +66,26 @@ class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { private fun PageContentBuilder.buildSourcesContent( node: ContentPage, sources: List<Pair<DokkaSourceSet, String>> - ) = contentFor( - node.dri.first(), - node.documentable!!.sourceSets.toSet() - ) { - header(2, "Sources", kind = ContentKind.Source) - +ContentTable( - header = emptyList(), - children = sources.map { - buildGroup(node.dri, setOf(it.first), kind = ContentKind.Source, extra = mainExtra + SymbolAnchorHint(it.second, ContentKind.Source)) { - link("${it.first.displayName} source", it.second) - } - }, - dci = DCI(node.dri, ContentKind.Source), - sourceSets = node.documentable!!.sourceSets.toDisplaySourceSets(), - style = emptySet(), - extra = mainExtra + SimpleAttr.header("Sources") - ) + ): ContentGroup { + val documentables = (node as? WithDocumentables)?.documentables.orEmpty() + return contentFor( + node.dri, + documentables.flatMap { it.sourceSets }.toSet() + ) { + header(2, "Sources", kind = ContentKind.Source) + +ContentTable( + header = emptyList(), + children = sources.map { + buildGroup(node.dri, setOf(it.first), kind = ContentKind.Source, extra = mainExtra + SymbolAnchorHint(it.second, ContentKind.Source)) { + link("${it.first.displayName} source", it.second) + } + }, + dci = DCI(node.dri, ContentKind.Source), + sourceSets = documentables.flatMap { it.sourceSets }.toDisplaySourceSets(), + style = emptySet(), + extra = mainExtra + SimpleAttr.header("Sources") + ) + } } private fun DocumentableSource.toLink(sourceLink: SourceLink): String { diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index e2aca6f9..c5136c27 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -1,9 +1,14 @@ 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.transformers.pages.tags.CustomTagContentProvider import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* @@ -15,11 +20,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<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>> @@ -35,66 +35,139 @@ open class DefaultPageCreator( ) { protected open val contentBuilder = PageContentBuilder(commentsToContentConverter, signatureProvider, logger) + protected val mergeImplicitExpectActualDeclarations = + configuration?.mergeImplicitExpectActualDeclarations + ?: DokkaBaseConfiguration.mergeImplicitExpectActualDeclarationsDefault + protected val separateInheritedMembers = configuration?.separateInheritedMembers ?: DokkaBaseConfiguration.separateInheritedMembersDefault - open fun pageForModule(m: DModule) = - ModulePageNode(m.name.ifEmpty { "<root>" }, contentForModule(m), m, m.packages.map(::pageForPackage)) + open fun pageForModule(m: DModule): ModulePageNode = + ModulePageNode(m.name.ifEmpty { "<root>" }, contentForModule(m), listOf(m), m.packages.map(::pageForPackage)) 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) + p.name, contentForPackage(p), setOf(p.dri), listOf(p), + if (mergeImplicitExpectActualDeclarations) + p.classlikes.mergeClashingDocumentable().map(::pageForClasslikes) + + 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 = - ClasslikePageNode( - e.nameAfterClash(), contentForEnumEntry(e), setOf(e.dri), e, - e.classlikes.renameClashingDocumentable().map(::pageForClasslike) + - e.filteredFunctions.renameClashingDocumentable().map(::pageForFunction) + - e.filteredProperties.renameClashingDocumentable().mapNotNull(::pageForProperty) - ) + open fun pageForEnumEntry(e: DEnumEntry): ClasslikePageNode = pageForEnumEntries(listOf(e)) - open fun pageForClasslike(c: DClasslike): ClasslikePageNode { - val constructors = if (c is WithConstructors) c.constructors else emptyList() + open fun pageForClasslike(c: DClasslike): ClasslikePageNode = pageForClasslikes(listOf(c)) + + open fun pageForEnumEntries(documentables: List<DEnumEntry>): 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( - c.nameAfterClash(), contentForClasslike(c), setOf(c.dri), c, - constructors.map(::pageForFunction) + - c.classlikes.renameClashingDocumentable().map(::pageForClasslike) + - c.filteredFunctions.renameClashingDocumentable().map(::pageForFunction) + - c.filteredProperties.renameClashingDocumentable().mapNotNull(::pageForProperty) + - if (c is DEnum) c.entries.map(::pageForEnumEntry) else emptyList() + documentables.first().nameAfterClash(), contentForClasslikesAndEntries(documentables), dri, documentables, + childrenPages + ) + } + + open fun pageForClasslikes(documentables: List<DClasslike>): 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() } + + val classlikes = documentables.flatMap { it.classlikes } + val functions = documentables.flatMap { it.filteredFunctions } + val props = documentables.flatMap { it.filteredProperties } + val entries = documentables.flatMap { if (it is DEnum) it.entries else emptyList() } + + val childrenPages = constructors.map(::pageForFunction) + + if (mergeImplicitExpectActualDeclarations) + classlikes.mergeClashingDocumentable().map(::pageForClasslikes) + + functions.mergeClashingDocumentable().map(::pageForFunctions) + + props.mergeClashingDocumentable().map(::pageForProperties) + + entries.mergeClashingDocumentable().map(::pageForEnumEntries) + 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, + childrenPages ) } private fun <T> T.toClashedName() where T : Documentable, T : WithExtraProperties<T> = (extra[ClashingDriIdentifier]?.value?.joinToString(", ", "[", "]") { it.displayName } ?: "") + name.orEmpty() - @Suppress("UNCHECKED_CAST") private fun <T : Documentable> List<T>.renameClashingDocumentable(): List<T> = 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() } } - open fun pageForFunction(f: DFunction) = MemberPageNode(f.nameAfterClash(), contentForFunction(f), setOf(f.dri), f) + @Suppress("UNCHECKED_CAST") + private fun <T : Documentable> 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 <T : Documentable> List<T>.mergeClashingDocumentable(): List<List<T>> = + groupBy { it.dri }.values.toList() + + open fun pageForFunction(f: DFunction) = + MemberPageNode(f.nameAfterClash(), contentForFunction(f), setOf(f.dri), listOf(f)) + + open fun pageForFunctions(fs: List<DFunction>): 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) + } open fun pageForProperty(p: DProperty): MemberPageNode? = - MemberPageNode(p.nameAfterClash(), contentForProperty(p), setOf(p.dri), p) + MemberPageNode(p.nameAfterClash(), contentForProperty(p), setOf(p.dri), listOf(p)) + + open fun pageForProperties(ps: List<DProperty>): 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> T.isInherited(): Boolean where T : Documentable, T : WithExtraProperties<T> = sourceSets.all { sourceSet -> extra[InheritedMember]?.isInherited(sourceSet) == true } @@ -168,148 +241,193 @@ open class DefaultPageCreator( } } + protected open fun contentForScopes( + scopes: List<WithScope>, + sourceSets: Set<DokkaSourceSet> + ): ContentGroup { + val types = scopes.flatMap { it.classlikes } + scopes.filterIsInstance<DPackage>().flatMap { it.typealiases } + val inheritors = scopes.fold(mutableMapOf<DokkaSourceSet, List<DRI>>()) { acc, scope -> + val inheritorsForScope = + scope.safeAs<WithExtraProperties<Documentable>>()?.let { it.extra[InheritorsInfo] }?.let { inheritors -> + inheritors.value.filter { it.value.isNotEmpty() } + }.orEmpty() + inheritorsForScope.forEach { (k, v) -> + acc.compute(k) { _, value -> value?.plus(v) ?: v } + } + acc + } + + return contentForScope( + @Suppress("UNCHECKED_CAST") + (scopes as List<Documentable>).dri, + sourceSets, + types, + scopes.flatMap { it.functions }, + scopes.flatMap { it.properties }, + inheritors + ) + } + protected open fun contentForScope( s: WithScope, dri: DRI, sourceSets: Set<DokkaSourceSet> - ) = contentBuilder.contentFor(s as Documentable) { + ): ContentGroup { val types = listOf( s.classlikes, (s as? DPackage)?.typealiases ?: emptyList() ).flatten() + val inheritors = + s.safeAs<WithExtraProperties<Documentable>>()?.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<DRI>, + sourceSets: Set<DokkaSourceSet>, + types: List<Documentable>, + functions: List<DFunction>, + properties: List<DProperty>, + inheritors: SourceSetDependent<List<DRI>> + ) = 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<WithExtraProperties<Documentable>>()?.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") + ) } } private fun Iterable<DFunction>.sorted() = sortedWith(compareBy({ it.name }, { it.parameters.size }, { it.dri.toString() })) - protected open fun contentForEnumEntry(e: DEnumEntry) = contentBuilder.contentFor(e) { - group(kind = ContentKind.Cover) { - cover(e.name) - sourceSetDependentHint(e.dri, e.sourceSets.toSet()) { - +buildSignature(e) - +contentForDescription(e) - } - } - group(styles = setOf(ContentStyle.TabbedContent)) { - +contentForComments(e) - +contentForScope(e, e.dri, e.sourceSets) - } - } - - protected open fun contentForClasslike(c: DClasslike) = contentBuilder.contentFor(c) { - @Suppress("UNCHECKED_CAST") - val extensions = (c as WithExtraProperties<DClasslike>) - .extra[CallableExtensions]?.extensions - ?.filterIsInstance<Documentable>().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<Documentable>): ContentGroup = + contentBuilder.contentFor(documentables.dri, documentables.sourceSets) { + val classlikes = documentables.filterIsInstance<DClasslike>() + + @Suppress("UNCHECKED_CAST") + val extensions = (classlikes as List<WithExtraProperties<DClasslike>>).flatMap { + it.extra[CallableExtensions]?.extensions + ?.filterIsInstance<Documentable>().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<ContentNode>() + 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() - ) { - +buildSignature(it) - contentForBrief(it) + + group(styles = setOf(ContentStyle.TabbedContent), sourceSets = mainSourcesetData + extensions.sourceSets) { + +contentForComments(documentables) + val csWithConstructor = classlikes.filterIsInstance<WithConstructors>() + if (csWithConstructor.isNotEmpty()) { + val constructorsToDocumented = csWithConstructor.flatMap { it.constructors } + multiBlock( + "Constructors", + 2, + ContentKind.Constructors, + constructorsToDocumented.groupBy { it.parameters.map { it.dri } } + .map { (_, v) -> v.first().name to v }, + @Suppress("UNCHECKED_CAST") + (csWithConstructor as List<Documentable>).sourceSets, + needsAnchors = true, + extra = PropertyContainer.empty<ContentNode>() + SimpleAttr.header("Constructors") + ) { key, ds -> + link(key, ds.first().dri, kind = ContentKind.Main, styles = setOf(ContentStyle.RowTitle)) + sourceSetDependentHint( + ds.dri, + ds.sourceSets, + kind = ContentKind.SourceSetDependentHint, + styles = emptySet(), + extra = PropertyContainer.empty<ContentNode>() + ) { + ds.forEach { + +buildSignature(it) + contentForBrief(it) + } + } } } - } - +contentForScope(c, c.dri, c.sourceSets) + val csEnum = classlikes.filterIsInstance<DEnum>() + if (csEnum.isNotEmpty()) { + multiBlock( + "Entries", + 2, + ContentKind.Classlikes, + csEnum.flatMap { it.entries }.groupBy { it.name }.toList(), + csEnum.sourceSets, + needsSorting = false, + needsAnchors = true, + extra = mainExtra + SimpleAttr.header("Entries"), + styles = emptySet() + ) { key, ds -> + link(key, ds.first().dri) + sourceSetDependentHint( + ds.dri, + ds.sourceSets, + kind = ContentKind.SourceSetDependentHint, + extra = PropertyContainer.empty<ContentNode>() + ) { + ds.forEach { + +buildSignature(it) + contentForBrief(it) + } + } + } + } + +contentForScopes(documentables.filterIsInstance<WithScope>(), documentables.sourceSets) - divergentBlock( - "Extensions", - extensions, - ContentKind.Extensions, - extra = mainExtra + SimpleAttr.header("Extensions") - ) + divergentBlock( + "Extensions", + extensions, + ContentKind.Extensions, + extra = mainExtra + SimpleAttr.header("Extensions") + ) + } } - } @Suppress("UNCHECKED_CAST") private inline fun <reified T : TagWrapper> GroupedTags.withTypeUnnamed(): SourceSetDependent<T> = @@ -347,7 +465,7 @@ open class DefaultPageCreator( if (customTags.isNotEmpty()) { group(styles = setOf(TextStyle.Block)) { platforms.forEach { platform -> - customTags.forEach { (tagName, sourceSetTag) -> + customTags.forEach { (_, sourceSetTag) -> sourceSetTag[platform]?.let { tag -> customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> with(provider) { @@ -379,8 +497,8 @@ open class DefaultPageCreator( }.children } - private fun Documentable.getPossibleFallbackSourcesets(sourceSet: DokkaSourceSet) = - this.sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } + private fun Set<DokkaSourceSet>.getPossibleFallbackSourcesets(sourceSet: DokkaSourceSet) = + this.filter { it.sourceSetID in sourceSet.dependentSourceSets } private fun <V> Map<DokkaSourceSet, V>.fallback(sourceSets: List<DokkaSourceSet>): V? = sourceSets.firstOrNull { it in this.keys }.let { this[it] } @@ -388,8 +506,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<Documentable>, + isPlatformHintedContent: Boolean = true + ) = contentForComments(d.first().dri, d.sourceSets, d.groupedTags, isPlatformHintedContent) + + protected open fun contentForComments( + dri: DRI, + sourceSets: Set<DokkaSourceSet>, + tags: GroupedTags, + isPlatformHintedContent: Boolean = true ): List<ContentNode> { - val tags = d.groupedTags fun DocumentableContentBuilder.buildContent( platforms: Set<DokkaSourceSet>, @@ -417,7 +546,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 +580,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 +666,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 @@ -558,7 +686,8 @@ open class DefaultPageCreator( We purposefully ignore all other tags as they should not be visible in brief */ - it.firstMemberOfTypeOrNull<Description>() ?: it.firstMemberOfTypeOrNull<Property>().takeIf { documentable is DProperty } + it.firstMemberOfTypeOrNull<Description>() ?: it.firstMemberOfTypeOrNull<Property>() + .takeIf { documentable is DProperty } }?.let { group(sourceSets = setOf(sourceSet), kind = ContentKind.BriefComment) { if (documentable.hasSeparatePage) createBriefComment(documentable, sourceSet, it) @@ -568,9 +697,13 @@ open class DefaultPageCreator( } } - private fun DocumentableContentBuilder.createBriefComment(documentable: Documentable, sourceSet: DokkaSourceSet, tag: TagWrapper){ + private fun DocumentableContentBuilder.createBriefComment( + documentable: Documentable, + sourceSet: DokkaSourceSet, + tag: TagWrapper + ) { (documentable as? WithSources)?.documentableLanguage(sourceSet)?.let { - when(it){ + when (it) { DocumentableLanguage.KOTLIN -> firstParagraphComment(tag.root) DocumentableLanguage.JAVA -> firstSentenceComment(tag.root) } @@ -581,22 +714,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<Documentable>) = + 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<DFunction>) = divergentBlock( name, @@ -610,22 +748,24 @@ open class DefaultPageCreator( list: Collection<DProperty>, sourceSets: Set<DokkaSourceSet> ) { - 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) + } } } } @@ -699,7 +839,7 @@ open class DefaultPageCreator( if (customTags.isEmpty()) return documentable.sourceSets.forEach { sourceSet -> - customTags.forEach { (tagName, sourceSetTag) -> + customTags.forEach { (_, sourceSetTag) -> sourceSetTag[sourceSet]?.let { tag -> customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> with(provider) { @@ -716,9 +856,19 @@ open class DefaultPageCreator( private val List<Documentable>.sourceSets: Set<DokkaSourceSet> get() = flatMap { it.sourceSets }.toSet() + private val List<Documentable>.dri: Set<DRI> + get() = map { it.dri }.toSet() + private val Documentable.groupedTags: GroupedTags get() = documentation.flatMap { (pd, doc) -> - doc.children.asSequence().map { pd to it }.toList() + doc.children.map { pd to it }.toList() + }.groupBy { it.second::class } + + private val List<Documentable>.groupedTags: GroupedTags + get() = this.flatMap { + it.documentation.flatMap { (pd, doc) -> + doc.children.map { pd to it }.toList() + } }.groupBy { it.second::class } private val Documentable.descriptions: SourceSetDependent<Description> @@ -730,6 +880,7 @@ open class DefaultPageCreator( private val Documentable.hasSeparatePage: Boolean get() = this !is DTypeAlias + @Suppress("UNCHECKED_CAST") private fun <T : Documentable> T.nameAfterClash(): String = ((this as? WithExtraProperties<out Documentable>)?.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 8c3fcd84..7b6fbc7a 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 <T : Pair<String, List<Documentable>>> multiBlock( + name: String, + level: Int, + kind: Kind = ContentKind.Main, + groupedElements: Iterable<T>, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + renderWhenEmpty: Boolean = false, + needsSorting: Boolean = true, + headers: List<ContentGroup> = emptyList(), + needsAnchors: Boolean = false, + operation: DocumentableContentBuilder.(String, List<Documentable>) -> Unit + ) { + if (renderWhenEmpty || groupedElements.any()) { + header(level, name, kind = kind) { } + contents += ContentTable( + header = headers, + children = groupedElements + .let { + if (needsSorting) + it.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.first }) + else it + } + .map { + val newExtra = if (needsAnchors) extra + SymbolAnchorHint(it.first, kind) else extra + val documentables = it.second + buildGroup(documentables.map { it.dri }.toSet(), documentables.flatMap { it.sourceSets }.toSet(), kind, styles, newExtra) { + operation(it.first, documentables) + } + }, + dci = DCI(mainDRI, kind), + sourceSets = sourceSets.toDisplaySourceSets(), + style = styles, + extra = extra + ) + } + } + fun <T> list( elements: List<T>, prefix: String = "", diff --git a/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt b/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt index 7d8a169b..50f9e357 100644 --- a/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt +++ b/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt @@ -1,11 +1,11 @@ package content.functions -import org.junit.Assert.assertEquals import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.model.DClass import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.pages.* +import org.junit.Assert.assertEquals import org.junit.jupiter.api.Test import kotlin.test.assertNull @@ -60,7 +60,7 @@ class ContentForBriefTest : BaseAbstractTest() { testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable @@ -84,7 +84,7 @@ class ContentForBriefTest : BaseAbstractTest() { testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable @@ -108,7 +108,7 @@ class ContentForBriefTest : BaseAbstractTest() { testInline(codeWithDocumentedParameter, testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable @@ -319,7 +319,7 @@ class ContentForBriefTest : BaseAbstractTest() { } private fun RootPageNode.singleFunctionDescription(className: String): ContentGroup { - val classPage = dfs { it.name == className && (it as ContentPage).documentable is DClass } as ContentPage + val classPage = dfs { it.name == className && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val functionsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Functions } as ContentTable diff --git a/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt b/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt index 832fa6f6..8da3be54 100644 --- a/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt +++ b/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt @@ -32,7 +32,7 @@ class ContentForConstructors : BaseAbstractTest() { """.trimIndent(), testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable diff --git a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt index 8c96eab3..59406e1e 100644 --- a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt @@ -53,8 +53,7 @@ class DokkaLocationProviderTest : BaseAbstractTest() { ModulePageNode( name = name, children = packages.pages, - content = stubContentNode, - documentable = null + content = stubContentNode ) ) } @@ -69,7 +68,6 @@ class DokkaLocationProviderTest : BaseAbstractTest() { name = name, children = packages.pages, content = stubContentNode, - documentable = null, dri = emptySet() ) ) @@ -84,7 +82,6 @@ class DokkaLocationProviderTest : BaseAbstractTest() { name = name, children = emptyList(), content = stubContentNode, - documentable = null, dri = emptySet() ) ) diff --git a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt new file mode 100644 index 00000000..ad3b5d38 --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt @@ -0,0 +1,352 @@ +package transformers + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.PluginConfigurationImpl +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.model.childrenOfType +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.model.firstChildOfType +import org.jetbrains.dokka.pages.* +import org.jetbrains.kotlin.utils.addIfNotNull +import org.junit.jupiter.api.Test +import utils.assertNotNull +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { + + @Suppress("UNUSED_VARIABLE") + private fun configuration(switchOn: Boolean) = dokkaConfiguration { + sourceSets { + val common = sourceSet { + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt") + } + val js = sourceSet { + name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(common.value.sourceSetID) + sourceRoots = listOf("src/jsMain/kotlin/pageMerger/Test.kt") + } + val jvm = sourceSet { + name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt") + } + } + pluginsConfigurations.addIfNotNull( + PluginConfigurationImpl( + DokkaBase::class.qualifiedName!!, + DokkaConfiguration.SerializationFormat.JSON, + """{ "mergeImplicitExpectActualDeclarations": $switchOn }""", + ) + ) + } + + private fun ClasslikePageNode.findSectionWithName(name: String) : ContentNode? { + var sectionHeader: ContentHeader? = null + return content.dfs { node -> + node.children.filterIsInstance<ContentHeader>().any { header -> + header.children.firstOrNull { it is ContentText && it.text == name }?.also { sectionHeader = header } != null + } + }?.children?.dropWhile { child -> child != sectionHeader }?.drop(1)?.firstOrNull() + } + + @Test + fun `should merge fun`() { + testInline( + """ + + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun method1(): String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun method1(): Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") + val method1 = functions.children.singleOrNull().assertNotNull("method1") + + assertEquals( + 2, + method1.firstChildOfType<ContentDivergentGroup>().childrenOfType<ContentDivergentInstance>().size, + "Incorrect number of divergent instances found" + ) + + val methodPage = root.dfs { it.name == "method1" } as? MemberPageNode + assertNotNull(methodPage, "Tested method not found!") + + val divergentGroup = methodPage.content.dfs { it is ContentDivergentGroup } as? ContentDivergentGroup + + assertEquals( + 2, + divergentGroup?.childrenOfType<ContentDivergentInstance>()?.size, + "Incorrect number of divergent instances found in method page" + ) + } + } + } + + @Test + fun `should merge method and prop`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun method1(): String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val props = classPage.findSectionWithName("Properties").assertNotNull("Properties") + val prop1 = props.children.singleOrNull().assertNotNull("prop1") + + val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") + val method1 = functions.children.singleOrNull().assertNotNull("method1") + } + } + } + + @Test + fun `should merge prop`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val props = classPage.findSectionWithName("Properties").assertNotNull("Properties") + val prop1 = props.children.singleOrNull().assertNotNull("prop1") + + assertEquals( + 2, + prop1.firstChildOfType<PlatformHintedContent>().inner.children.size, + "Incorrect number of divergent instances found" + ) + + val propPage = root.dfs { it.name == "prop1" } as? MemberPageNode + assertNotNull(propPage, "Tested method not found!") + + val divergentGroup = propPage.content.dfs { it is ContentDivergentGroup } as? ContentDivergentGroup + + assertEquals( + 2, + divergentGroup?.childrenOfType<ContentDivergentInstance>()?.size, + "Incorrect number of divergent instances found in method page" + ) + } + } + } + + @Test + fun `should merge enum and class`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |enum class classA { + | ENTRY + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val entries = classPage.findSectionWithName("Entries").assertNotNull("Entries") + val entry = entries.children.singleOrNull().assertNotNull("ENTRY") + + val props = classPage.findSectionWithName("Properties").assertNotNull("Properties") + assertEquals( + 3, + props.children.size, + "Incorrect number of properties found in method page" + ) + } + } + } + + fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() } + + @Test + fun `should merge enum entries`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |enum class classA { + | SMTH; + | fun method1(): Int + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |enum class classA { + | SMTH; + | fun method1(): Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "SMTH" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") + val method1 = functions.children.singleOrNull().assertNotNull("method1") + + assertEquals( + 2, + method1.firstChildOfType<ContentDivergentGroup>().childrenOfType<ContentDivergentInstance>().size, + "Incorrect number of divergent instances found" + ) + } + } + } + + /** + * There is a case when a property and fun from different source sets + * have the same name so pages have the same urls respectively. + */ + @Test + fun `should no merge prop and method with the same name`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun merged():String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val merged:String + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val allChildren = root.childrenRec().filterIsInstance<MemberPageNode>() + + assertEquals( + 1, + allChildren.filter { it.name == "merged" }.size, + "Incorrect number of fun pages" + ) + } + } + } + + @Test + fun `should always merge constructor`() { + testInline( + """ + |/src/commonMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |expect class classA(a: Int) + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |actual class classA(a: Int) + """.trimMargin(), + configuration(false), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val constructors = classPage.findSectionWithName("Constructors").assertNotNull("Constructors") + + assertEquals( + 1, + constructors.children.size, + "Incorrect number of constructors" + ) + + val platformHinted = constructors.dfs { it is PlatformHintedContent } as? PlatformHintedContent + + assertEquals( + 2, + platformHinted?.sourceSets?.size, + "Incorrect number of source sets" + ) + } + } + } +}
\ No newline at end of file diff --git a/plugins/javadoc/api/javadoc.api b/plugins/javadoc/api/javadoc.api index bfea687a..0eabce5c 100644 --- a/plugins/javadoc/api/javadoc.api +++ b/plugins/javadoc/api/javadoc.api @@ -56,6 +56,7 @@ public final class org/jetbrains/dokka/javadoc/pages/AllClassesPage : org/jetbra public final fun getClasses ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public fun getEmbeddedResources ()Ljava/util/List; public fun getName ()Ljava/lang/String; @@ -90,6 +91,7 @@ public final class org/jetbrains/dokka/javadoc/pages/DeprecatedPage : org/jetbra public fun getChildren ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public final fun getElements ()Ljava/util/Map; public fun getEmbeddedResources ()Ljava/util/List; @@ -158,6 +160,7 @@ public final class org/jetbrains/dokka/javadoc/pages/IndexPage : org/jetbrains/d public fun getChildren ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public final fun getElements ()Ljava/util/List; public fun getEmbeddedResources ()Ljava/util/List; @@ -176,8 +179,8 @@ public final class org/jetbrains/dokka/javadoc/pages/JavaContentGroupBuilder { } public final class org/jetbrains/dokka/javadoc/pages/JavadocClasslikePageNode : org/jetbrains/dokka/javadoc/pages/JavadocPageNode, org/jetbrains/dokka/javadoc/pages/NavigableJavadocNode, org/jetbrains/dokka/javadoc/pages/WithBrief, org/jetbrains/dokka/javadoc/pages/WithJavadocExtra, org/jetbrains/dokka/javadoc/pages/WithNavigable, org/jetbrains/dokka/pages/ClasslikePage { - public fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Lorg/jetbrains/dokka/javadoc/pages/JavadocSignatureContentNode;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lorg/jetbrains/dokka/model/Documentable;Ljava/util/List;Ljava/util/List;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V - public synthetic fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Lorg/jetbrains/dokka/javadoc/pages/JavadocSignatureContentNode;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lorg/jetbrains/dokka/model/Documentable;Ljava/util/List;Ljava/util/List;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Lorg/jetbrains/dokka/javadoc/pages/JavadocSignatureContentNode;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V + public synthetic fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Lorg/jetbrains/dokka/javadoc/pages/JavadocSignatureContentNode;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getAllNavigables ()Ljava/util/List; public final fun getAnchorables ()Ljava/util/List; public fun getBrief ()Ljava/util/List; @@ -189,6 +192,7 @@ public final class org/jetbrains/dokka/javadoc/pages/JavadocClasslikePageNode : public fun getDRI ()Lorg/jetbrains/dokka/links/DRI; public final fun getDescription ()Ljava/util/List; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public fun getEmbeddedResources ()Ljava/util/List; public final fun getEntries ()Ljava/util/List; @@ -353,6 +357,7 @@ public final class org/jetbrains/dokka/javadoc/pages/JavadocModulePageNode : org public synthetic fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDRI ()Lorg/jetbrains/dokka/links/DRI; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public fun getEmbeddedResources ()Ljava/util/List; public fun getExtra ()Lorg/jetbrains/dokka/model/properties/PropertyContainer; @@ -367,14 +372,15 @@ public final class org/jetbrains/dokka/javadoc/pages/JavadocModulePageNode : org } public final class org/jetbrains/dokka/javadoc/pages/JavadocPackagePageNode : org/jetbrains/dokka/javadoc/pages/JavadocPageNode, org/jetbrains/dokka/javadoc/pages/NavigableJavadocNode, org/jetbrains/dokka/javadoc/pages/WithNavigable, org/jetbrains/dokka/pages/PackagePage { - public fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Lorg/jetbrains/dokka/model/Documentable;Ljava/util/List;Ljava/util/List;)V - public synthetic fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Lorg/jetbrains/dokka/model/Documentable;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Ljava/util/List;Ljava/util/List;Ljava/util/List;)V + public synthetic fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode;Ljava/util/Set;Ljava/util/List;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getAllNavigables ()Ljava/util/List; public fun getChildren ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/javadoc/pages/JavadocContentNode; public synthetic fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDRI ()Lorg/jetbrains/dokka/links/DRI; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public fun getEmbeddedResources ()Ljava/util/List; public fun getId ()Ljava/lang/String; @@ -383,7 +389,11 @@ public final class org/jetbrains/dokka/javadoc/pages/JavadocPackagePageNode : or public fun modified (Ljava/lang/String;Lorg/jetbrains/dokka/pages/ContentNode;Ljava/util/Set;Ljava/util/List;Ljava/util/List;)Lorg/jetbrains/dokka/pages/ContentPage; } -public abstract interface class org/jetbrains/dokka/javadoc/pages/JavadocPageNode : org/jetbrains/dokka/pages/ContentPage { +public abstract interface class org/jetbrains/dokka/javadoc/pages/JavadocPageNode : org/jetbrains/dokka/pages/ContentPage, org/jetbrains/dokka/pages/WithDocumentables { +} + +public final class org/jetbrains/dokka/javadoc/pages/JavadocPageNode$DefaultImpls { + public static fun getDocumentable (Lorg/jetbrains/dokka/javadoc/pages/JavadocPageNode;)Lorg/jetbrains/dokka/model/Documentable; } public final class org/jetbrains/dokka/javadoc/pages/JavadocParameterNode : org/jetbrains/dokka/javadoc/pages/AnchorableJavadocNode, org/jetbrains/dokka/javadoc/pages/WithJavadocExtra { @@ -573,11 +583,13 @@ public final class org/jetbrains/dokka/javadoc/pages/TreeViewInstaller : org/jet } public final class org/jetbrains/dokka/javadoc/pages/TreeViewPage : org/jetbrains/dokka/javadoc/pages/JavadocPageNode { - public fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/pages/PageNode;)V + public fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Lorg/jetbrains/dokka/pages/PageNode;)V + public synthetic fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Lorg/jetbrains/dokka/pages/PageNode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getChildren ()Ljava/util/List; public final fun getClasses ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; public fun getDocumentable ()Lorg/jetbrains/dokka/model/Documentable; + public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public fun getEmbeddedResources ()Ljava/util/List; public final fun getKind ()Ljava/lang/String; diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt index 96bcec70..61d45ae1 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt @@ -33,7 +33,7 @@ open class JavadocPageCreator(context: DokkaContext) { ) fun pageForPackage(p: DPackage) = - JavadocPackagePageNode(p.name, contentForPackage(p), setOf(p.dri), p, + JavadocPackagePageNode(p.name, contentForPackage(p), setOf(p.dri), listOf(p), p.classlikes.mapNotNull { pageForClasslike(it) } ) @@ -68,7 +68,7 @@ open class JavadocPageCreator(context: DokkaContext) { PropertyContainer.withAll(it.indexesInDocumentation()) ) }, - documentable = c, + documentables = listOf(c), children = children, extra = ((c as? WithExtraProperties<Documentable>)?.extra ?: PropertyContainer.empty()) + c.indexesInDocumentation() diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt index 572c5dec..6c7691cd 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType -interface JavadocPageNode : ContentPage +interface JavadocPageNode : ContentPage, WithDocumentables interface WithJavadocExtra<T : Documentable> : WithExtraProperties<T> { override fun withNewExtras(newExtras: PropertyContainer<T>): T = @@ -43,7 +43,7 @@ class JavadocModulePageNode( JavadocPageNode, ModulePage { - override val documentable: Documentable? = null + override val documentables: List<Documentable> = emptyList() override val embeddedResources: List<String> = emptyList() override fun modified(name: String, children: List<PageNode>): RootPageNode = JavadocModulePageNode(name, content, children, dri, extra) @@ -66,7 +66,7 @@ class JavadocPackagePageNode( override val content: JavadocContentNode, override val dri: Set<DRI>, - override val documentable: Documentable? = null, + override val documentables: List<Documentable> = emptyList(), override val children: List<PageNode> = emptyList(), override val embeddedResources: List<String> = listOf() ) : JavadocPageNode, @@ -91,7 +91,7 @@ class JavadocPackagePageNode( name, content, dri, - documentable, + documentables, children, embeddedResources ) @@ -107,7 +107,7 @@ class JavadocPackagePageNode( name, content as JavadocContentNode, dri, - documentable, + documentables, children, embeddedResources ) @@ -182,7 +182,7 @@ class JavadocClasslikePageNode( val classlikes: List<JavadocClasslikePageNode>, val properties: List<JavadocPropertyNode>, override val brief: List<ContentNode>, - override val documentable: Documentable? = null, + override val documentables: List<Documentable> = emptyList(), override val children: List<PageNode> = emptyList(), override val embeddedResources: List<String> = listOf(), override val extra: PropertyContainer<DClasslike> = PropertyContainer.empty(), @@ -194,7 +194,7 @@ class JavadocClasslikePageNode( fun getAnchorables(): List<AnchorableJavadocNode> = constructors + methods + entries + properties - val kind: String? = documentable?.kind() + val kind: String? = documentables.firstOrNull()?.kind() val packageName = dri.first().packageName override fun getId(): String = name @@ -215,7 +215,7 @@ class JavadocClasslikePageNode( classlikes, properties, brief, - documentable, + documentables, children, embeddedResources, extra @@ -240,7 +240,7 @@ class JavadocClasslikePageNode( classlikes, properties, brief, - documentable, + documentables, children, embeddedResources, extra @@ -254,7 +254,7 @@ class AllClassesPage(val classes: List<JavadocClasslikePageNode>) : JavadocPageN override val name: String = "All Classes" override val dri: Set<DRI> = setOf(DRI.topLevel) - override val documentable: Documentable? = null + override val documentables: List<Documentable> = emptyList() override val embeddedResources: List<String> = emptyList() override val content: ContentNode = @@ -285,7 +285,7 @@ class DeprecatedPage( ) : JavadocPageNode { override val name: String = "deprecated" override val dri: Set<DRI> = setOf(DRI.topLevel) - override val documentable: Documentable? = null + override val documentables: List<Documentable> = emptyList() override val children: List<PageNode> = emptyList() override val embeddedResources: List<String> = listOf() @@ -339,7 +339,7 @@ class IndexPage( ) : JavadocPageNode { override val name: String = "index-$id" override val dri: Set<DRI> = setOf(DRI.topLevel) - override val documentable: Documentable? = null + override val documentables: List<Documentable> = emptyList() override val children: List<PageNode> = emptyList() override val embeddedResources: List<String> = listOf() val title: String = "${keys[id - 1]}-index" @@ -370,7 +370,7 @@ class TreeViewPage( val packages: List<JavadocPackagePageNode>?, val classes: List<JavadocClasslikePageNode>?, override val dri: Set<DRI>, - override val documentable: Documentable?, + override val documentables: List<Documentable> = emptyList(), val root: PageNode ) : JavadocPageNode { init { @@ -378,7 +378,7 @@ class TreeViewPage( assert(packages != null || classes != null) } - private val documentables = root.children.filterIsInstance<ContentPage>().flatMap { node -> + private val childrenDocumentables = root.children.filterIsInstance<WithDocumentables>().flatMap { node -> getDocumentableEntries(node) }.groupBy({ it.first }) { it.second }.map { (l, r) -> l to r.first() }.toMap() @@ -389,12 +389,12 @@ class TreeViewPage( override val children: List<PageNode> = emptyList() - val title = when (documentable) { + val title = when (documentables.firstOrNull()) { is DPackage -> "$name Class Hierarchy" else -> "All packages" } - val kind = when (documentable) { + val kind = when (documentables.firstOrNull()) { is DPackage -> "package" else -> "main" } @@ -411,7 +411,7 @@ class TreeViewPage( packages = children.filterIsInstance<JavadocPackagePageNode>().takeIf { it.isNotEmpty() }, classes = children.filterIsInstance<JavadocClasslikePageNode>().takeIf { it.isNotEmpty() }, dri = dri, - documentable = documentable, + documentables, root = root ) @@ -421,7 +421,7 @@ class TreeViewPage( packages = children.filterIsInstance<JavadocPackagePageNode>().takeIf { it.isNotEmpty() }, classes = children.filterIsInstance<JavadocClasslikePageNode>().takeIf { it.isNotEmpty() }, dri = dri, - documentable = documentable, + documentables, root = root ) @@ -473,23 +473,25 @@ class TreeViewPage( listOf(psi to l) + l.flatMap { gatherPsiClasses(it) } } - val psiInheritanceTree = documentables.flatMap { (_, v) -> (v as? WithSources)?.sources?.values.orEmpty() } - .filterIsInstance<PsiDocumentableSource>().mapNotNull { it.psi as? PsiClass }.flatMap(::gatherPsiClasses) - .flatMap { entry -> entry.second.map { it to entry.first } } - .let { - it + it.map { it.second to null } - } - .groupBy({ it.first }) { it.second } - .map { it.key to it.value.filterNotNull().distinct() } - .map { (k, v) -> - InheritanceNode( - DRI.from(k), - v.map { InheritanceNode(DRI.from(it)) }, - k.supers.filter { it.isInterface }.map { DRI.from(it) }, - k.isInterface - ) - - } + val psiInheritanceTree = + childrenDocumentables.flatMap { (_, v) -> (v as? WithSources)?.sources?.values.orEmpty() } + .filterIsInstance<PsiDocumentableSource>().mapNotNull { it.psi as? PsiClass } + .flatMap(::gatherPsiClasses) + .flatMap { entry -> entry.second.map { it to entry.first } } + .let { + it + it.map { it.second to null } + } + .groupBy({ it.first }) { it.second } + .map { it.key to it.value.filterNotNull().distinct() } + .map { (k, v) -> + InheritanceNode( + DRI.from(k), + v.map { InheritanceNode(DRI.from(it)) }, + k.supers.filter { it.isInterface }.map { DRI.from(it) }, + k.isInterface + ) + + } val descriptorInheritanceTree = descriptorMap.flatMap { (_, v) -> v.typeConstructor.supertypes @@ -523,16 +525,17 @@ class TreeViewPage( } private fun generateInterfaceGraph() { - documentables.values.filterIsInstance<DInterface>() + childrenDocumentables.values.filterIsInstance<DInterface>() } - private fun getDocumentableEntries(node: ContentPage): List<Pair<DRI, Documentable>> = - listOfNotNull(node.documentable?.let { it.dri to it }) + - node.children.filterIsInstance<ContentPage>().flatMap(::getDocumentableEntries) + private fun getDocumentableEntries(node: WithDocumentables): List<Pair<DRI, Documentable>> = + node.documentables.map { it.dri to it } + + (node as? ContentPage)?.children?.filterIsInstance<WithDocumentables>() + ?.flatMap(::getDocumentableEntries).orEmpty() private fun getDescriptorMap(): Map<DRI, ClassDescriptor> { val map: MutableMap<DRI, ClassDescriptor> = mutableMapOf() - documentables + childrenDocumentables .mapNotNull { (k, v) -> v.descriptorForPlatform()?.let { k to it }?.also { (k, v) -> map[k] = v } }.map { it.second }.forEach { gatherSupertypes(it, map) } diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt index c04c7538..bef4108c 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt @@ -4,12 +4,10 @@ import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.base.transformers.documentables.deprecatedAnnotation import org.jetbrains.dokka.base.transformers.documentables.isDeprecated import org.jetbrains.dokka.base.transformers.documentables.isException -import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.BooleanValue -import org.jetbrains.dokka.model.WithSupertypes +import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.transformers.pages.PageTransformer -import kotlin.collections.HashMap object ResourcesInstaller : PageTransformer { override fun invoke(input: RootPageNode): RootPageNode = input.modified( @@ -37,7 +35,7 @@ object TreeViewInstaller : PageTransformer { packages = node.children<JavadocPackagePageNode>().map { installPackageTreeNode(it, root) }, classes = null, dri = node.dri, - documentable = node.documentable, + documentables = node.documentables, root = root ) @@ -55,7 +53,7 @@ object TreeViewInstaller : PageTransformer { packages = null, classes = node.children.filterIsInstance<JavadocClasslikePageNode>(), dri = node.dri, - documentable = node.documentable, + documentables = node.documentables, root = root ) diff --git a/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt b/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt index 89c13202..669238ff 100644 --- a/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt +++ b/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt @@ -9,6 +9,7 @@ import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.Doc import org.jetbrains.dokka.model.doc.CustomTagWrapper import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.pages.WithDocumentables import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -35,12 +36,10 @@ object MathjaxTransformer : PageTransformer { } private val ContentPage.isNeedingMathjax - get() = documentable?.documentation?.values - ?.flatMap { it.children } - .orEmpty() - .any { (it as? CustomTagWrapper)?.name == ANNOTATION } + get() = (this as WithDocumentables).documentables.any { it.documentation.values + .flatMap { it.children } + .any { (it as? CustomTagWrapper)?.name == ANNOTATION } } } - object MathjaxTagContentProvider : CustomTagContentProvider { override fun isApplicable(customTag: CustomTagWrapper) = customTag.name == ANNOTATION |