aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/base/api/base.api8
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt385
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt346
-rw-r--r--plugins/base/src/main/resources/dokka/styles/style.css7
-rw-r--r--plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt434
-rw-r--r--plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt494
-rw-r--r--plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt309
-rw-r--r--plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt192
-rw-r--r--plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt468
-rw-r--r--plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt3
-rw-r--r--plugins/base/src/test/kotlin/utils/contentUtils.kt54
-rw-r--r--plugins/base/src/test/resources/content/samples/samples.kt5
12 files changed, 2110 insertions, 595 deletions
diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api
index 3e74e37b..5ba42101 100644
--- a/plugins/base/api/base.api
+++ b/plugins/base/api/base.api
@@ -1450,12 +1450,6 @@ public class org/jetbrains/dokka/base/translators/documentables/DefaultPageCreat
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 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 contentForFunction (Lorg/jetbrains/dokka/model/DFunction;)Lorg/jetbrains/dokka/pages/ContentGroup;
protected fun contentForMember (Lorg/jetbrains/dokka/model/Documentable;)Lorg/jetbrains/dokka/pages/ContentGroup;
@@ -1463,7 +1457,7 @@ public class org/jetbrains/dokka/base/translators/documentables/DefaultPageCreat
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 (Ljava/util/Set;Ljava/util/Set;Ljava/util/List;Ljava/util/List;Ljava/util/List;)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
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
index 9d45f7a3..266d6e94 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
@@ -6,28 +6,19 @@ 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.links.PointingToDeclaration
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.kotlin.utils.addToStdlib.safeAs
import kotlin.reflect.KClass
-import kotlin.reflect.full.isSubclassOf
-internal const val KDOC_TAG_HEADER_LEVEL = 4
-
-private typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>>
-
-private val specialTags: Set<KClass<out TagWrapper>> =
- setOf(Property::class, Description::class, Constructor::class, Param::class, See::class)
+internal typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>>
open class DefaultPageCreator(
configuration: DokkaBaseConfiguration?,
@@ -203,7 +194,6 @@ open class DefaultPageCreator(
}
}
}
- +contentForComments(m)
block(
"Packages",
@@ -244,7 +234,6 @@ open class DefaultPageCreator(
}
}
group(styles = setOf(ContentStyle.TabbedContent)) {
- +contentForComments(p)
+contentForScope(p, p.dri, p.sourceSets)
}
}
@@ -254,25 +243,13 @@ open class DefaultPageCreator(
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
+ scopes.flatMap { it.properties }
)
}
@@ -285,12 +262,7 @@ open class DefaultPageCreator(
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)
+ return contentForScope(setOf(dri), sourceSets, types, s.functions, s.properties)
}
protected open fun contentForScope(
@@ -298,8 +270,7 @@ open class DefaultPageCreator(
sourceSets: Set<DokkaSourceSet>,
types: List<Documentable>,
functions: List<DFunction>,
- properties: List<DProperty>,
- inheritors: SourceSetDependent<List<DRI>>
+ properties: List<DProperty>
) = contentBuilder.contentFor(dri, sourceSets) {
divergentBlock("Types", types, ContentKind.Classlikes, extra = mainExtra + SimpleAttr.header("Types"))
if (separateInheritedMembers) {
@@ -313,31 +284,6 @@ open class DefaultPageCreator(
functionsBlock("Functions", functions)
propertiesBlock("Properties", properties, sourceSets)
}
- 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() =
@@ -367,9 +313,7 @@ open class DefaultPageCreator(
}
}
}
-
group(styles = setOf(ContentStyle.TabbedContent), sourceSets = mainSourcesetData + extensions.sourceSets) {
- +contentForComments(documentables)
val csWithConstructor = classlikes.filterIsInstance<WithConstructors>()
if (csWithConstructor.isNotEmpty() && documentables.shouldRenderConstructors()) {
val constructorsToDocumented = csWithConstructor.flatMap { it.constructors }
@@ -442,272 +386,28 @@ open class DefaultPageCreator(
// and instantiated directly under normal circumstances, so constructors should not be rendered.
private fun List<Documentable>.shouldRenderConstructors() = !this.any { it is DAnnotation }
- @Suppress("UNCHECKED_CAST")
- private inline fun <reified T : TagWrapper> GroupedTags.withTypeUnnamed(): SourceSetDependent<T> =
- (this[T::class] as List<Pair<DokkaSourceSet, T>>?)?.toMap().orEmpty()
-
- @Suppress("UNCHECKED_CAST")
- private inline fun <reified T : NamedTagWrapper> GroupedTags.withTypeNamed(): Map<String, SourceSetDependent<T>> =
- (this[T::class] as List<Pair<DokkaSourceSet, T>>?)
- ?.groupByTo(linkedMapOf()) { it.second.name }
- ?.mapValues { (_, v) -> v.toMap() }
- .orEmpty()
-
- private inline fun <reified T : TagWrapper> GroupedTags.isNotEmptyForTag(): Boolean =
- this[T::class]?.isNotEmpty() ?: false
-
protected open fun contentForDescription(
d: Documentable
): List<ContentNode> {
- val tags: GroupedTags = d.groupedTags
- val platforms = d.sourceSets.toSet()
-
- return contentBuilder.contentFor(d, styles = setOf(TextStyle.Block)) {
- deprecatedSectionContent(d, platforms)
-
- val descriptions = d.descriptions
- if (descriptions.any { it.value.root.children.isNotEmpty() }) {
- platforms.forEach { platform ->
- descriptions[platform]?.also {
- group(sourceSets = setOf(platform), styles = emptySet()) {
- comment(it.root)
- }
- }
- }
- }
-
- val customTags = d.customTags
- if (customTags.isNotEmpty()) {
- platforms.forEach { platform ->
- customTags.forEach { (_, sourceSetTag) ->
- sourceSetTag[platform]?.let { tag ->
- customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider ->
- group(sourceSets = setOf(platform), styles = setOf(ContentStyle.KDocTag)) {
- with(provider) {
- contentForDescription(platform, tag)
- }
- }
- }
- }
- }
- }
- }
-
- val unnamedTags = tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags }
- .values.flatten().groupBy { it.first }.mapValues { it.value.map { it.second } }
- if (unnamedTags.isNotEmpty()) {
- platforms.forEach { platform ->
- unnamedTags[platform]?.let { tags ->
- if (tags.isNotEmpty()) {
- tags.groupBy { it::class }.forEach { (_, sameCategoryTags) ->
- group(sourceSets = setOf(platform), styles = setOf(ContentStyle.KDocTag)) {
- header(
- level = KDOC_TAG_HEADER_LEVEL,
- text = sameCategoryTags.first().toHeaderString(),
- styles = setOf()
- )
- sameCategoryTags.forEach { comment(it.root, styles = setOf()) }
- }
- }
- }
- }
- }
- }
- }.children
- }
-
- 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] }
-
- 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> {
-
- fun DocumentableContentBuilder.buildContent(
- platforms: Set<DokkaSourceSet>,
- contentBuilder: DocumentableContentBuilder.() -> Unit
- ) = if (isPlatformHintedContent)
- sourceSetDependentHint(
- sourceSets = platforms,
- kind = ContentKind.SourceSetDependentHint,
- block = contentBuilder
- )
- else
- contentBuilder()
-
- fun DocumentableContentBuilder.contentForParams() {
- if (tags.isNotEmptyForTag<Param>()) {
- val params = tags.withTypeNamed<Param>()
- val availablePlatforms = params.values.flatMap { it.keys }.toSet()
-
- header(KDOC_TAG_HEADER_LEVEL, "Parameters", kind = ContentKind.Parameters, sourceSets = availablePlatforms)
- group(
- extra = mainExtra + SimpleAttr.header("Parameters"),
- styles = setOf(ContentStyle.WithExtraAttributes),
- sourceSets = availablePlatforms
- ) {
- buildContent(availablePlatforms) {
- table(kind = ContentKind.Parameters, sourceSets = availablePlatforms) {
- availablePlatforms.forEach { platform ->
- val possibleFallbacks = sourceSets.getPossibleFallbackSourcesets(platform)
- params.mapNotNull { (_, param) ->
- (param[platform] ?: param.fallback(possibleFallbacks))?.let {
- row(sourceSets = setOf(platform), kind = ContentKind.Parameters) {
- text(
- it.name,
- kind = ContentKind.Parameters,
- styles = mainStyles + setOf(ContentStyle.RowTitle, TextStyle.Underlined)
- )
- if (it.isNotEmpty()) {
- comment(it.root)
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
+ val sourceSets = d.sourceSets.toSet()
+ val tags = d.groupedTags
- fun DocumentableContentBuilder.contentForSeeAlso() {
- if (tags.isNotEmptyForTag<See>()) {
- val seeAlsoTags = tags.withTypeNamed<See>()
- val availablePlatforms = seeAlsoTags.values.flatMap { it.keys }.toSet()
+ return contentBuilder.contentFor(d) {
+ deprecatedSectionContent(d, sourceSets)
- header(KDOC_TAG_HEADER_LEVEL, "See also", kind = ContentKind.Comment, sourceSets = availablePlatforms)
- group(
- extra = mainExtra + SimpleAttr.header("See also"),
- styles = setOf(ContentStyle.WithExtraAttributes),
- sourceSets = availablePlatforms
- ) {
- buildContent(availablePlatforms) {
- table(kind = ContentKind.Sample) {
- availablePlatforms.forEach { platform ->
- val possibleFallbacks = sourceSets.getPossibleFallbackSourcesets(platform)
- seeAlsoTags.forEach { (_, see) ->
- (see[platform] ?: see.fallback(possibleFallbacks))?.let { seeTag ->
- row(
- sourceSets = setOf(platform),
- kind = ContentKind.Comment,
- styles = this@group.mainStyles,
- ) {
- 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)
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
+ descriptionSectionContent(d, sourceSets)
+ customTagSectionContent(d, sourceSets, customTagContentProviders)
+ unnamedTagSectionContent(d, sourceSets) { toHeaderString() }
- fun DocumentableContentBuilder.contentForThrows() {
- val throws = tags.withTypeNamed<Throws>()
- if (throws.isNotEmpty()) {
- val availablePlatforms = throws.values.flatMap { it.keys }.toSet()
-
- header(KDOC_TAG_HEADER_LEVEL, "Throws", sourceSets = availablePlatforms)
- buildContent(availablePlatforms) {
- availablePlatforms.forEach { sourceset ->
- table(
- kind = ContentKind.Main,
- sourceSets = setOf(sourceset),
- extra = mainExtra + SimpleAttr.header("Throws")
- ) {
- throws.entries.forEach { entry ->
- entry.value[sourceset]?.let { 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 ?: entry.key, address = it)
- } ?: text(entry.key)
- }
- if (throws.isNotEmpty()) {
- comment(throws.root)
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- fun DocumentableContentBuilder.contentForSamples() {
- val samples = tags.withTypeNamed<Sample>()
- if (samples.isNotEmpty()) {
- val availablePlatforms = samples.values.flatMap { it.keys }.toSet()
- header(KDOC_TAG_HEADER_LEVEL, "Samples", kind = ContentKind.Sample, sourceSets = availablePlatforms)
- group(
- extra = mainExtra + SimpleAttr.header("Samples"),
- styles = emptySet(),
- sourceSets = availablePlatforms
- ) {
- buildContent(availablePlatforms) {
- availablePlatforms.map { platformData ->
- val content = samples.filter { it.value.isEmpty() || platformData in it.value }
- group(
- sourceSets = setOf(platformData),
- kind = ContentKind.Sample,
- styles = setOf(TextStyle.Monospace, ContentStyle.RunnableSample)
- ) {
- content.forEach {
- text(it.key)
- }
- }
- }
- }
- }
- }
- }
+ paramsSectionContent(tags)
+ seeAlsoSectionContent(tags)
+ throwsSectionContent(tags)
+ samplesSectionContent(tags)
- return contentBuilder.contentFor(dri, sourceSets) {
- if (tags.isNotEmpty()) {
- contentForSamples()
- contentForSeeAlso()
- contentForParams()
- contentForThrows()
- }
+ inheritorsSectionContent(d, logger)
}.children
}
- private fun TagWrapper.isNotEmpty() = this.children.isNotEmpty()
-
protected open fun DocumentableContentBuilder.contentForBrief(documentable: Documentable) {
documentable.sourceSets.forEach { sourceSet ->
documentable.documentation[sourceSet]?.let {
@@ -761,7 +461,6 @@ open class DefaultPageCreator(
}
after {
+contentForDescription(d)
- +contentForComments(d, isPlatformHintedContent = false)
}
}
}
@@ -890,35 +589,39 @@ open class DefaultPageCreator(
}
protected open fun TagWrapper.toHeaderString() = this.javaClass.toGenericString().split('.').last()
+}
- private val List<Documentable>.sourceSets: Set<DokkaSourceSet>
- get() = flatMap { it.sourceSets }.toSet()
+internal val List<Documentable>.sourceSets: Set<DokkaSourceSet>
+ get() = flatMap { it.sourceSets }.toSet()
- private val List<Documentable>.dri: Set<DRI>
- get() = map { it.dri }.toSet()
+internal val List<Documentable>.dri: Set<DRI>
+ get() = map { it.dri }.toSet()
- private val Documentable.groupedTags: GroupedTags
- get() = documentation.flatMap { (pd, doc) ->
- doc.children.map { pd to it }.toList()
- }.groupBy { it.second::class }
+internal val Documentable.groupedTags: GroupedTags
+ get() = documentation.flatMap { (pd, doc) ->
+ 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 }
+internal val Documentable.descriptions: SourceSetDependent<Description>
+ get() = groupedTags.withTypeUnnamed()
- private val Documentable.descriptions: SourceSetDependent<Description>
- get() = groupedTags.withTypeUnnamed()
+internal val Documentable.customTags: Map<String, SourceSetDependent<CustomTagWrapper>>
+ get() = groupedTags.withTypeNamed()
- private val Documentable.customTags: Map<String, SourceSetDependent<CustomTagWrapper>>
- get() = groupedTags.withTypeNamed()
+private val Documentable.hasSeparatePage: Boolean
+ get() = this !is DTypeAlias
- 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()
- @Suppress("UNCHECKED_CAST")
- private fun <T : Documentable> T.nameAfterClash(): String =
- ((this as? WithExtraProperties<out Documentable>)?.extra?.get(DriClashAwareName)?.value ?: name).orEmpty()
-}
+@Suppress("UNCHECKED_CAST")
+internal inline fun <reified T : TagWrapper> GroupedTags.withTypeUnnamed(): SourceSetDependent<T> =
+ (this[T::class] as List<Pair<DokkaSourceSet, T>>?)?.toMap().orEmpty()
+
+@Suppress("UNCHECKED_CAST")
+internal inline fun <reified T : NamedTagWrapper> GroupedTags.withTypeNamed(): Map<String, SourceSetDependent<T>> =
+ (this[T::class] as List<Pair<DokkaSourceSet, T>>?)
+ ?.groupByTo(linkedMapOf()) { it.second.name }
+ ?.mapValues { (_, v) -> v.toMap() }
+ .orEmpty() \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt b/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt
new file mode 100644
index 00000000..1720b1d0
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt
@@ -0,0 +1,346 @@
+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.SimpleAttr
+import org.jetbrains.dokka.pages.TextStyle
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.kotlin.utils.addToStdlib.safeAs
+import kotlin.reflect.KClass
+import kotlin.reflect.full.isSubclassOf
+
+internal const val KDOC_TAG_HEADER_LEVEL = 4
+
+private val unnamedTagsExceptions: Set<KClass<out TagWrapper>> =
+ setOf(Property::class, Description::class, Constructor::class, Param::class, See::class)
+
+internal fun PageContentBuilder.DocumentableContentBuilder.descriptionSectionContent(
+ documentable: Documentable,
+ sourceSets: Set<DokkaConfiguration.DokkaSourceSet>,
+) {
+ 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<DokkaConfiguration.DokkaSourceSet>,
+ customTagContentProviders: List<CustomTagContentProvider>,
+) {
+ 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<DokkaConfiguration.DokkaSourceSet>,
+ 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<Param>()
+ 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<See>()
+ 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<DokkaConfiguration.DokkaSourceSet>.getPossibleFallback(sourceSet: DokkaConfiguration.DokkaSourceSet) =
+ this.filter { it.sourceSetID in sourceSet.dependentSourceSets }
+
+private fun <V> Map<DokkaConfiguration.DokkaSourceSet, V>.fallback(sourceSets: List<DokkaConfiguration.DokkaSourceSet>): V? =
+ sourceSets.firstOrNull { it in this.keys }.let { this[it] }
+
+internal fun PageContentBuilder.DocumentableContentBuilder.throwsSectionContent(tags: GroupedTags) {
+ val throwsTags = tags.withTypeNamed<Throws>()
+ 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<Sample>()
+ 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),
+ extra = mainExtra + SimpleAttr.header("Samples")
+ ) {
+ 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() = safeAs<WithExtraProperties<Documentable>>()
+ ?.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<DokkaConfiguration.DokkaSourceSet>) =
+ sourceSets.size == 1 &&
+ (sourceSets.first().analysisPlatform == Platform.common
+ || sourceSets.first().hasDependentSourceSet(inheritorsSourceSets))
+
+private fun DokkaConfiguration.DokkaSourceSet.hasDependentSourceSet(
+ sourceSets: Set<DokkaConfiguration.DokkaSourceSet>,
+) =
+ sourceSets.any { sourceSet -> sourceSet.dependentSourceSets.any { it == this.sourceSetID } }
+
+private fun PageContentBuilder.DocumentableContentBuilder.multiplatformInheritorsSectionContent(
+ documentable: Documentable,
+ inheritors: Map<DokkaConfiguration.DokkaSourceSet, List<DRI>>,
+ 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<DokkaConfiguration.DokkaSourceSet, List<DRI>>,
+ 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<DokkaConfiguration.DokkaSourceSet> = mainSourcesetData,
+ body: PageContentBuilder.TableBuilder.() -> Unit,
+) {
+ header(KDOC_TAG_HEADER_LEVEL, text = blockName, kind = kind, sourceSets = sourceSets)
+ table(
+ kind = kind,
+ sourceSets = sourceSets,
+ extra = mainExtra + SimpleAttr.header(blockName)
+ ) {
+ body()
+ }
+}
+
+private fun DRI.friendlyClassName() = classNames?.substringAfterLast(".")
+
+private fun <T> Map<String, SourceSetDependent<T>>.availableSourceSets() = values.flatMap { it.keys }.toSet()
+
+
diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css
index 431db3b8..5242a76d 100644
--- a/plugins/base/src/main/resources/dokka/styles/style.css
+++ b/plugins/base/src/main/resources/dokka/styles/style.css
@@ -946,9 +946,10 @@ td.content {
display: none;
}
-/*Work around an issue: https://github.com/JetBrains/kotlin-playground/issues/91*/
-.platform-hinted[data-togglable="Samples"] > .content:not([data-active]),
-.tabs-section-body > *[data-togglable="Samples"]:not([data-active]) {
+/* Work around an issue: https://github.com/JetBrains/kotlin-playground/issues/91
+Applies for main description blocks with platform tabs.
+Just in case of possible performance degradation it excluding tabs with briefs on classlike page */
+#content > div:not(.tabbedcontent) .sourceset-dependent-content:not([data-active]) {
display: block !important;
visibility: hidden;
height: 0;
diff --git a/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt b/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
new file mode 100644
index 00000000..f59ba529
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
@@ -0,0 +1,434 @@
+package content.exceptions
+
+import matchers.content.*
+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.DisplaySourceSet
+import org.jetbrains.kotlin.utils.addIfNotNull
+import org.junit.jupiter.api.Test
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.findTestType
+import kotlin.test.assertEquals
+
+class ContentForExceptions : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ pluginsConfigurations.addIfNotNull(
+ PluginConfigurationImpl(
+ DokkaBase::class.qualifiedName!!,
+ DokkaConfiguration.SerializationFormat.JSON,
+ """{ "mergeImplicitExpectActualDeclarations": true }""",
+ )
+ )
+ }
+
+ @Test
+ fun `function with navigatable thrown exception`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ |* @throws Exception
+ |*/
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ link { +"Exception" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with non-navigatable thrown exception`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ |* @throws UnavailableException
+ |*/
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"UnavailableException"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatofrm class with throws`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws CommonException
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws JvmException
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws LinuxException
+ |*/
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"CommonException"
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "common",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ group {
+ group {
+ +"JvmException"
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ group {
+ group {
+ +"LinuxException"
+ }
+ check {
+ sourceSets.assertSourceSet("linuxX64")
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatofrm class with throws in few platforms`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws CommonException
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws JvmException
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"CommonException"
+ }
+ check {
+ sourceSets.assertSourceSet("common")
+ }
+ }
+ group {
+ group {
+ +"JvmException"
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ check {
+ assertEquals(2, sourceSets.size)
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `throws in merged functions`() {
+ testInline(
+ """
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws LinuxException
+ |*/
+ |fun function() {
+ | println()
+ |}
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws JvmException
+ |*/
+ |fun function() {
+ | println()
+ |}
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"JvmException"
+ }
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"LinuxException"
+ }
+ }
+ }
+ }
+ check {
+ sourceSets.assertSourceSet("linuxX64")
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) {
+ assertEquals(1, this.size)
+ assertEquals(expectedName, this.first().name)
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt b/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
new file mode 100644
index 00000000..09c927bd
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
@@ -0,0 +1,494 @@
+package content.inheritors
+
+import matchers.content.*
+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.kotlin.utils.addIfNotNull
+import org.junit.jupiter.api.Test
+import utils.classSignature
+import utils.findTestType
+import kotlin.test.assertEquals
+
+class ContentForInheritorsTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ pluginsConfigurations.addIfNotNull(
+ PluginConfigurationImpl(
+ DokkaBase::class.qualifiedName!!,
+ DokkaConfiguration.SerializationFormat.JSON,
+ """{ "mergeImplicitExpectActualDeclarations": true }""",
+ )
+ )
+ }
+
+
+ //Case from skiko library
+ private val mppTestConfigurationSharedAsPlatform = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ val jvm = sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "android"
+ displayName = "android"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(jvm.value.sourceSetID)
+ sourceRoots = listOf("src/androidMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "awt"
+ displayName = "awt"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(jvm.value.sourceSetID)
+ sourceRoots = listOf("src/awtMain/kotlin/pageMerger/Test.kt")
+ }
+
+ }
+ }
+
+ @Test
+ fun `class with one inheritor has table in description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class Parent
+ |
+ |class Foo : Parent()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Parent"
+ )
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Foo" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `interface with few inheritors has table in description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |interface Parent
+ |
+ |class Foo : Parent()
+ |class Bar : Parent()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"interface "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Foo" }
+ }
+ group {
+ link { +"Bar" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `inherit from one of multiplatoforms actuals`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |class Child: Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "linuxX64",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `inherit from class in common code`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |class Child : Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "common",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `inheritors from merged classes`() {
+ testInline(
+ """
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class LChild : Parent()
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class JChild : Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"JChild" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "jvm",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"LChild" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "linuxX64",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `merged inheritors from merged classes`() {
+ testInline(
+ """
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class Child : Parent()
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class Child : Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "jvm",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "linuxX64",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `parent in shared source set that analyse as platform`() {
+ testInline(
+ """
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |interface Parent
+ |
+ |/src/androidMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |class Child : Parent
+ |
+ |/src/awtMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |class AwtChild : Parent
+ |class Child : Parent
+ |
+ """.trimMargin(),
+ mppTestConfigurationSharedAsPlatform
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"interface "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ group {
+ link { +"AwtChild" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "jvm",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
index 3531f148..742c801f 100644
--- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
+++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
@@ -2,12 +2,12 @@ package content.params
import matchers.content.*
import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.model.DFunction
import org.jetbrains.dokka.model.dfs
import org.jetbrains.dokka.model.doc.DocumentationNode
import org.jetbrains.dokka.model.doc.Param
import org.jetbrains.dokka.model.doc.Text
-import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.pages.*
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.junit.jupiter.api.Test
@@ -37,8 +37,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -76,8 +75,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -122,8 +120,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -175,8 +172,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ val classPage = module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -219,8 +215,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ val classPage = module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -274,7 +269,7 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -327,7 +322,7 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -375,7 +370,7 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -436,7 +431,10 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val functionPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" }.children.single { it.name == "sample" } as ContentPage
+ module.findTestType(
+ "sample",
+ "DocGenProcessor"
+ ).children.single { it.name == "sample" } as ContentPage
functionPage.content.assertNode {
group {
header(1) { +"sample" }
@@ -492,8 +490,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val functionPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "sample" } as ContentPage
+ val functionPage = module.findTestType("sample", "sample")
functionPage.content.assertNode {
group {
header(1) { +"sample" }
@@ -564,7 +561,7 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val functionPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "sample" } as ContentPage
+ module.findTestType("sample", "sample")
functionPage.content.assertNode {
group {
header(1) { +"sample" }
@@ -616,7 +613,10 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val functionPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" }.children.single { it.name == "sample" } as ContentPage
+ module.findTestType(
+ "sample",
+ "DocGenProcessor"
+ ).children.single { it.name == "sample" } as ContentPage
functionPage.content.assertNode {
group {
header(1) { +"sample" }
@@ -693,7 +693,7 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -742,7 +742,10 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val functionPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" }.children.single { it.name == "sample" } as ContentPage
+ module.findTestType(
+ "sample",
+ "DocGenProcessor"
+ ).children.single { it.name == "sample" } as ContentPage
functionPage.content.assertNode {
group {
header(1) { +"sample" }
@@ -763,14 +766,12 @@ class ContentForParamsTest : BaseAbstractTest() {
}
}
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"testParam"
- comment {
- +"Sample description for test param that has a type of "
- link { +"String" }
- }
+ table {
+ group {
+ +"testParam"
+ comment {
+ +"Sample description for test param that has a type of "
+ link { +"String" }
}
}
}
@@ -801,8 +802,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val functionPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "sample" } as ContentPage
+ val functionPage = module.findTestType("sample", "sample")
functionPage.content.assertNode {
group {
header(1) { +"sample" }
@@ -855,8 +855,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ val classPage = module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -921,7 +920,7 @@ class ContentForParamsTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val classPage =
- module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage
+ module.findTestType("sample", "DocGenProcessor")
classPage.content.assertNode {
group {
header { +"DocGenProcessor" }
@@ -982,8 +981,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1029,8 +1027,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1051,21 +1048,69 @@ class ContentForParamsTest : BaseAbstractTest() {
after {
group { pWrapped("comment to function") }
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"abc"
- check {
- val textStyles = children.single { it is ContentText }.style
- assertContains(textStyles, TextStyle.Underlined)
- }
- group { group { +"comment to param" } }
+ table {
+ group {
+ +"abc"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
}
+ group { group { +"comment to param" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `single parameter in class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to class
+ | * @param abc comment to param
+ | */
+ |class Foo(abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ group {
+ pWrapped("comment to class")
+ }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"abc"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
}
+ group { group { +"comment to param" } }
}
}
}
}
+ skipAllNotMatching()
}
}
}
@@ -1089,8 +1134,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1108,32 +1152,30 @@ class ContentForParamsTest : BaseAbstractTest() {
after {
group { group { group { +"comment to function" } } }
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"first"
- check {
- val textStyles = children.single { it is ContentText }.style
- assertContains(textStyles, TextStyle.Underlined)
- }
- group { group { +"comment to first param" } }
+ table {
+ group {
+ +"first"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
}
- group {
- +"second"
- check {
- val textStyles = children.single { it is ContentText }.style
- assertContains(textStyles, TextStyle.Underlined)
- }
- group { group { +"comment to second param" } }
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"second"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
}
- group {
- +"third"
- check {
- val textStyles = children.single { it is ContentText }.style
- assertContains(textStyles, TextStyle.Underlined)
- }
- group { group { +"comment to third param" } }
+ group { group { +"comment to second param" } }
+ }
+ group {
+ +"third"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
}
+ group { group { +"comment to third param" } }
}
}
}
@@ -1163,8 +1205,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1182,22 +1223,21 @@ class ContentForParamsTest : BaseAbstractTest() {
after {
group { group { group { +"comment to function" } } }
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"c"
- group { group { +"comment to c param" } }
- }
- group {
- +"b"
- group { group { +"comment to b param" } }
- }
- group {
- +"a"
- group { group { +"comment to a param" } }
- }
+ table {
+ group {
+ +"c"
+ group { group { +"comment to c param" } }
+ }
+ group {
+ +"b"
+ group { group { +"comment to b param" } }
+ }
+ group {
+ +"a"
+ group { group { +"comment to a param" } }
}
}
+
}
}
}
@@ -1223,8 +1263,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1241,20 +1280,18 @@ class ContentForParamsTest : BaseAbstractTest() {
}
after {
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"first"
- group { group { +"comment to first param" } }
- }
- group {
- +"second"
- group { group { +"comment to second param" } }
- }
- group {
- +"third"
- group { group { +"comment to third param" } }
- }
+ table {
+ group {
+ +"first"
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"second"
+ group { group { +"comment to second param" } }
+ }
+ group {
+ +"third"
+ group { group { +"comment to third param" } }
}
}
}
@@ -1282,8 +1319,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1309,14 +1345,13 @@ class ContentForParamsTest : BaseAbstractTest() {
pWrapped("comment to receiver")
}
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"abc"
- group { group { +"comment to param" } }
- }
+ table {
+ group {
+ +"abc"
+ group { group { +"comment to param" } }
}
}
+
}
}
}
@@ -1342,8 +1377,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1361,16 +1395,14 @@ class ContentForParamsTest : BaseAbstractTest() {
after {
group { group { group { +"comment to function" } } }
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"first"
- group { group { +"comment to first param" } }
- }
- group {
- +"third"
- group { group { +"comment to third param" } }
- }
+ table {
+ group {
+ +"first"
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"third"
+ group { group { +"comment to third param" } }
}
}
}
@@ -1401,8 +1433,7 @@ class ContentForParamsTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -1423,20 +1454,18 @@ class ContentForParamsTest : BaseAbstractTest() {
unnamedTag("Since") { comment { +"0.11" } }
header(4) { +"Parameters" }
- group {
- table {
- group {
- +"first"
- group { group { +"comment to first param" } }
- }
- group {
- +"second"
- group { group { +"comment to second param" } }
- }
- group {
- +"third"
- group { group { +"comment to third param" } }
- }
+ table {
+ group {
+ +"first"
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"second"
+ group { group { +"comment to second param" } }
+ }
+ group {
+ +"third"
+ group { group { +"comment to third param" } }
}
}
}
diff --git a/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt b/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
new file mode 100644
index 00000000..37009e46
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
@@ -0,0 +1,192 @@
+package content.samples
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DisplaySourceSet
+import org.junit.jupiter.api.Test
+import utils.classSignature
+import utils.findTestType
+import java.nio.file.Paths
+import kotlin.test.assertEquals
+
+class ContentForSamplesTest : BaseAbstractTest() {
+ private val testDataDir = getTestDataDir("content/samples").toAbsolutePath()
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `samples block is rendered in the description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ | /**
+ | * @sample [test.sampleForClassDescription]
+ | */
+ |class Foo
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo"
+ )
+ header(4) { +"Samples" }
+ group {
+ codeBlock {
+ +"""|
+ |fun main() {
+ | //sampleStart
+ | print("Hello")
+ | //sampleEnd
+ |}""".trimMargin()
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatofrm class with samples in few platforms`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @sample [test.sampleForClassDescription]
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @sample unresolved
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Samples" }
+ group {
+ codeBlock {
+ +"""|
+ |fun main() {
+ | //sampleStart
+ | print("Hello")
+ | //sampleEnd
+ |}""".trimMargin()
+ }
+ check {
+ sourceSets.assertSourceSet("common")
+ }
+ }
+ group {
+ +"unresolved"
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+}
+
+
+private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) {
+ assertEquals(1, this.size)
+ assertEquals(expectedName, this.first().name)
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
index 5dee546f..79c1e1ad 100644
--- a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
+++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
@@ -1,14 +1,9 @@
package content.seealso
import matchers.content.*
-import org.jetbrains.dokka.pages.ContentDRILink
-import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
-import org.jetbrains.dokka.links.Callable
-import org.jetbrains.dokka.links.DRI
-import org.jetbrains.dokka.links.JavaClassReference
-import org.jetbrains.dokka.model.doc.See
-import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
+import org.jetbrains.dokka.model.DisplaySourceSet
+import org.jetbrains.dokka.pages.ContentDRILink
import org.junit.jupiter.api.Test
import utils.*
import kotlin.test.assertEquals
@@ -23,6 +18,32 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
}
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ }
+
@Test
fun `undocumented function`() {
testInline(
@@ -36,8 +57,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -77,8 +97,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -98,12 +117,10 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
- group {
- //DRI should be "test//abc/#/-1/"
- link { +"abc" }
- }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
}
}
}
@@ -115,6 +132,128 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
@Test
+ fun `undocumented seealso without reference for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |class Foo()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo"
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ +"abc"
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso with reference to parameter for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |class Foo(abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ +"abc"
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso with reference to property for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |class Foo(val abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "val abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ link { +"Foo.abc" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
fun `documented seealso`() {
testInline(
"""
@@ -129,8 +268,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -150,14 +288,12 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
group {
- //DRI should be "test//abc/#/-1/"
- link { +"abc" }
- group {
- group { +"Comment to abc" }
- }
+ group { +"Comment to abc" }
}
}
}
@@ -170,6 +306,50 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
@Test
+ fun `documented seealso with reference to property for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc
+ | */
+ |class Foo(val abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "val abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ link { +"Foo.abc" }
+ group {
+ group { +"Comment to abc" }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
fun `should use fully qualified name for unresolved link`() {
testInline(
"""
@@ -184,8 +364,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -205,13 +384,11 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ +"com.example.NonExistingClass"
group {
- +"com.example.NonExistingClass"
- group {
- group { +"description for non-existing" }
- }
+ group { +"description for non-existing" }
}
}
}
@@ -238,8 +415,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -259,21 +435,20 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
- group {
- link {
- check {
- assertEquals(
- "kotlin.collections/Collection///PointingToDeclaration/",
- (this as ContentDRILink).address.toString()
- )
- }
- +"Collection"
+ table {
+ group {
+ link {
+ check {
+ assertEquals(
+ "kotlin.collections/Collection///PointingToDeclaration/",
+ (this as ContentDRILink).address.toString()
+ )
}
+ +"Collection"
}
}
}
+
}
}
}
@@ -297,8 +472,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -318,16 +492,15 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
group {
- //DRI should be "test//abc/#/-1/"
- link { +"Collection" }
- group {
- group { +"Comment to stdliblink" }
- }
+ group { +"Comment to stdliblink" }
}
}
+
}
}
}
@@ -355,8 +528,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -380,17 +552,16 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
unnamedTag("Since") { comment { +"0.11" } }
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
group {
- //DRI should be "test//abc/#/-1/"
- link { +"Collection" }
- group {
- group { +"Comment to stdliblink" }
- }
+ group { +"Comment to stdliblink" }
}
}
}
+
}
}
}
@@ -415,8 +586,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -436,17 +606,16 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
group {
- //DRI should be "test//abc/#/-1/"
- link { +"abc" }
- group {
- group { +"Comment to abc2" }
- }
+ group { +"Comment to abc2" }
}
}
}
+
}
}
}
@@ -471,8 +640,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
""".trimIndent(), testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "test" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("test", "function")
page.content.assertNode {
group {
header(1) { +"function" }
@@ -492,21 +660,19 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
group {
- //DRI should be "test//abc/#/-1/"
- link { +"abc" }
- group {
- group { +"Comment to abc1" }
- }
- }
- group {
- //DRI should be "test//abc/#/-1/"
- link { +"Collection" }
- group { group { +"Comment to collection" } }
+ group { +"Comment to abc1" }
}
}
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group { group { +"Comment to collection" } }
+ }
}
}
}
@@ -543,8 +709,7 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
testConfiguration
) {
pagesTransformationStage = { module ->
- val page = module.children.single { it.name == "com.example" }
- .children.single { it.name == "function" } as ContentPage
+ val page = module.findTestType("com.example", "function")
page.content.assertNode {
group {
@@ -564,19 +729,17 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
after {
header(4) { +"See also" }
- group {
- table {
+ table {
+ group {
+ link { +"CollectionExtensions.property" }
group {
- link { +"CollectionExtensions.property" }
- group {
- group { +"static property" }
- }
+ group { +"static property" }
}
+ }
+ group {
+ link { +"CollectionExtensions.emptyList" }
group {
- link { +"CollectionExtensions.emptyList" }
- group {
- group { +"static emptyList" }
- }
+ group { +"static emptyList" }
}
}
}
@@ -587,4 +750,109 @@ class ContentForSeeAlsoTest : BaseAbstractTest() {
}
}
}
+
+ @Test
+ fun `multiplatform class with seealso in few platforms`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @see Unit
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |val x = 0
+ |/**
+ |* @see x resolved
+ |* @see y unresolved
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) {
+ +"See also"
+ check {
+ assertEquals(2, sourceSets.size)
+ }
+ }
+ table {
+ group {
+ link { +"Unit" }
+ check {
+ sourceSets.assertSourceSet("common")
+ }
+ }
+ group {
+ link { +"Unit" }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ group {
+ link { +"x" }
+ group { group { +"resolved" } }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ group {
+ +"y"
+ group { group { +"unresolved" } }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+
+ check {
+ assertEquals(2, sourceSets.size)
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
}
+
+private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) {
+ assertEquals(1, this.size)
+ assertEquals(expectedName, this.first().name)
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
index fadc8e83..7dfe1e1d 100644
--- a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
+++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
@@ -1,13 +1,13 @@
package linkableContent
import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer
import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer
import org.jetbrains.dokka.model.WithGenerics
import org.jetbrains.dokka.model.dfs
import org.jetbrains.dokka.model.doc.Text
import org.jetbrains.dokka.pages.*
-import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.junit.jupiter.api.Assertions
@@ -200,7 +200,6 @@ class LinkableContentTest : BaseAbstractTest() {
.cast<ContentDivergentGroup>().children.single()
.cast<ContentDivergentInstance>().after
.cast<ContentGroup>().children.last()
- .cast<ContentGroup>().children.last()
.cast<ContentGroup>().children.single()
.cast<ContentCodeBlock>().children.single().cast<ContentText>().text
Assertions.assertEquals(
diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt
index 9f024c5b..d38af3f4 100644
--- a/plugins/base/src/test/kotlin/utils/contentUtils.kt
+++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt
@@ -1,9 +1,9 @@
package utils
import matchers.content.*
-import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.pages.ContentGroup
-import kotlin.text.Typography.nbsp
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.RootPageNode
//TODO: Try to unify those functions after update to 1.4
fun ContentMatcherBuilder<*>.functionSignature(
@@ -68,6 +68,53 @@ fun ContentMatcherBuilder<*>.bareSignature(
}
}
+fun ContentMatcherBuilder<*>.classSignature(
+ annotations: Map<String, Set<String>>,
+ visibility: String,
+ modifier: String,
+ keywords: Set<String>,
+ name: String,
+ vararg params: Pair<String, ParamAttributes>,
+ parent: String? = null
+) = group {
+ annotations.entries.forEach {
+ group {
+ unwrapAnnotation(it)
+ }
+ }
+ if (visibility.isNotBlank()) +"$visibility "
+ if (modifier.isNotBlank()) +"$modifier "
+ +("${keywords.joinToString("") { "$it " }}class ")
+ link { +name }
+ if (params.isNotEmpty()) {
+ +"("
+ group {
+ params.forEachIndexed { id, (n, t) ->
+ group {
+ t.annotations.forEach {
+ unwrapAnnotation(it)
+ }
+ t.keywords.forEach {
+ +it
+ }
+
+ +"$n: "
+ group { link { +(t.type) } }
+ if (id != params.lastIndex)
+ +", "
+ }
+ }
+ }
+ +")"
+ }
+ if (parent != null) {
+ +(" : ")
+ link {
+ +(parent)
+ }
+ }
+}
+
fun ContentMatcherBuilder<*>.functionSignatureWithReceiver(
annotations: Map<String, Set<String>>,
visibility: String?,
@@ -272,3 +319,6 @@ data class ParamAttributes(
val keywords: Set<String>,
val type: String
)
+
+fun RootPageNode.findTestType(packageName: String, name: String) =
+ children.single { it.name == packageName }.children.single { it.name == name } as ContentPage \ No newline at end of file
diff --git a/plugins/base/src/test/resources/content/samples/samples.kt b/plugins/base/src/test/resources/content/samples/samples.kt
new file mode 100644
index 00000000..dcd38f40
--- /dev/null
+++ b/plugins/base/src/test/resources/content/samples/samples.kt
@@ -0,0 +1,5 @@
+package test
+
+fun sampleForClassDescription() {
+ print("Hello")
+} \ No newline at end of file