aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt106
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt4
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt20
-rw-r--r--plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt87
4 files changed, 164 insertions, 53 deletions
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
index 371ab12d..6e288c01 100644
--- a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
+++ b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
@@ -3,6 +3,8 @@ package org.jetbrains.dokka.base.transformers.documentables
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.DokkaSourceSetID
import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.ExtraProperty
+import org.jetbrains.dokka.model.properties.MergeStrategy
import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.model.properties.mergeExtras
import org.jetbrains.dokka.plugability.DokkaContext
@@ -36,14 +38,14 @@ internal class DefaultDocumentableMerger(val context: DokkaContext) : Documentab
allModules.groupBy { it.sourceSets.single().sourceSetID }
//this returns representation of graph where directed edges are leading from module to modules that depend on it
- val graph: Map<ModuleOfDifferentTranslators?, List<ModuleOfDifferentTranslators>> = modulesMap.flatMap { (_, module) ->
- module.first().sourceSets.single().dependentSourceSets.map { sourceSet ->
- modulesMap[sourceSet] to module
+ val graph: Map<ModuleOfDifferentTranslators, List<ModuleOfDifferentTranslators>> =
+ modulesMap.flatMap { (_, module) ->
+ module.first().sourceSets.single().dependentSourceSets.map { sourceSet ->
+ modulesMap[sourceSet]!! to module
+ }
+ }.groupingBy { it.first }.fold({ _, value -> listOf(value.second) }) { _, accumulator, value ->
+ accumulator + listOf(value.second)
}
- }.groupBy { it.first }.entries
- .map { it.key to it.value.map { it.second } }
- .toMap()
-
val visited = modulesMap.map { it.value to false }.toMap().toMutableMap()
val topologicalSortedList: MutableList<ModuleOfDifferentTranslators> = mutableListOf()
@@ -82,37 +84,64 @@ internal class DefaultDocumentableMerger(val context: DokkaContext) : Documentab
fun T.isExpectActual(): Boolean =
this.safeAs<WithExtraProperties<T>>().let { it != null && it.extra[IsExpectActual] != null }
- fun Set<DokkaConfiguration.DokkaSourceSet>.parentSourceSet(): String = singleOrNull {
- it.dependentSourceSets.all { it !in this.map { it.sourceSetID } }
- }?.displayName
- ?: "unresolved".also { context.logger.error("Ill-defined dependency between sourceSets") }
-
- fun mergeClashingElements(elements: List<T>): List<T> = elements.groupBy { it.name }.values.flatMap {
- if(it.size > 1) it.map {
- when(it) {
- is DClass -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- is DObject -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- is DAnnotation -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- is DInterface -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- is DEnum -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- is DFunction -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- is DProperty -> it.copy(name = "${it.name}(${it.sourceSets.parentSourceSet()})")
- else -> it
- }
- } as List<T> else it
- }
+ fun mergeClashingElements(elements: List<Pair<T, Set<DokkaConfiguration.DokkaSourceSet>>>): List<T> =
+ elements.groupBy { it.first.name }.values.flatMap { listOfDocumentableToSSIds ->
+ listOfDocumentableToSSIds.map { (documentable, sourceSets) ->
+ when (documentable) {
+ is DClass -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ is DObject -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ is DAnnotation -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ is DInterface -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ is DEnum -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ is DFunction -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ is DProperty -> documentable.copy(
+ extra = documentable.extra + ClashingDriIdentifier(
+ sourceSets + (documentable.extra[ClashingDriIdentifier]?.value ?: emptySet())
+ )
+ )
+ else -> documentable
+ }
+ } as List<T>
+ }
+
fun analyzeExpectActual(sameDriElements: List<T>): List<T> {
val (expects, actuals) = sameDriElements.partition { it.expectPresentInSet != null }
- val groupedByOwnExpect = expects.map { expect ->
- listOf(expect) + actuals.filter { actual ->
+ val groupedByOwnExpectWithActualSourceSetIds = expects.map { expect ->
+ val actualsForGivenExpect = actuals.filter { actual ->
dependencyInfo[actual.sourceSets.single()]
?.contains(expect.expectPresentInSet!!)
?: throw IllegalStateException("Cannot resolve expect/actual relation for ${actual.name}")
}
+ (listOf(expect) + actualsForGivenExpect) to actualsForGivenExpect.flatMap { it.sourceSets }.toSet()
}
- val reducedToOneDocumentable = groupedByOwnExpect.map { it.reduce(reducer) }
- val uniqueNamedDocumentables = reducedToOneDocumentable.let(::mergeClashingElements)
+ val reducedToOneDocumentableWithActualSourceSetIds =
+ groupedByOwnExpectWithActualSourceSetIds.map { it.first.reduce(reducer) to it.second }
+ val uniqueNamedDocumentables = reducedToOneDocumentableWithActualSourceSetIds.let(::mergeClashingElements)
return uniqueNamedDocumentables
}
@@ -120,8 +149,9 @@ internal class DefaultDocumentableMerger(val context: DokkaContext) : Documentab
return elements.partition {
it.isExpectActual()
}.let { (expectActuals, notExpectActuals) ->
- notExpectActuals.groupBy { it.dri }.values.flatMap(::mergeClashingElements) +
- expectActuals.groupBy { it.dri }.values.flatMap(::analyzeExpectActual)
+ notExpectActuals.map { it to it.sourceSets }
+ .groupBy { it.first.dri }.values.flatMap(::mergeClashingElements) +
+ expectActuals.groupBy { it.dri }.values.flatMap(::analyzeExpectActual)
}
}
@@ -277,3 +307,15 @@ internal class DefaultDocumentableMerger(val context: DokkaContext) : Documentab
}
private typealias ModuleOfDifferentTranslators = List<DModule>
+
+data class ClashingDriIdentifier(val value: Set<DokkaConfiguration.DokkaSourceSet>) : ExtraProperty<Documentable> {
+ companion object : ExtraProperty.Key<Documentable, ClashingDriIdentifier> {
+ override fun mergeStrategyFor(
+ left: ClashingDriIdentifier,
+ right: ClashingDriIdentifier
+ ): MergeStrategy<Documentable> =
+ MergeStrategy.Replace(ClashingDriIdentifier(left.value + right.value))
+ }
+
+ override val key: ExtraProperty.Key<Documentable, *> = ClashingDriIdentifier
+}
diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt
index df0c27ee..8698e84b 100644
--- a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt
+++ b/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt
@@ -1,10 +1,14 @@
package org.jetbrains.dokka.base.transformers.pages.merger
+import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.PageNode
import org.jetbrains.dokka.utilities.DokkaLogger
class FallbackPageMergerStrategy(private val logger: DokkaLogger) : PageMergerStrategy {
override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> {
+ pages.map {
+ (it as? ContentPage)
+ }
val renderedPath = path.joinToString(separator = "/")
if (pages.size != 1) logger.warn("For $renderedPath: expected 1 page, but got ${pages.size}")
return listOf(pages.first())
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
index 615412f4..55e59b77 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
@@ -16,6 +16,7 @@ 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.transformers.documentables.ClashingDriIdentifier
private typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>>
@@ -34,14 +35,14 @@ open class DefaultPageCreator(
open fun pageForPackage(p: DPackage): PackagePageNode = PackagePageNode(
p.name, contentForPackage(p), setOf(p.dri), p,
- p.classlikes.map(::pageForClasslike) +
+ p.classlikes.renameClashingClasslikes().map(::pageForClasslike) +
p.functions.map(::pageForFunction)
)
open fun pageForEnumEntry(e: DEnumEntry): ClasslikePageNode =
ClasslikePageNode(
e.name, contentForEnumEntry(e), setOf(e.dri), e,
- e.classlikes.map(::pageForClasslike) +
+ e.classlikes.renameClashingClasslikes().map(::pageForClasslike) +
e.filteredFunctions.map(::pageForFunction)
)
@@ -51,12 +52,25 @@ open class DefaultPageCreator(
return ClasslikePageNode(
c.name.orEmpty(), contentForClasslike(c), setOf(c.dri), c,
constructors.map(::pageForFunction) +
- c.classlikes.map(::pageForClasslike) +
+ c.classlikes.renameClashingClasslikes().map(::pageForClasslike) +
c.filteredFunctions.map(::pageForFunction) +
if (c is DEnum) c.entries.map(::pageForEnumEntry) else emptyList()
)
}
+ private fun List<DClasslike>.renameClashingClasslikes(): List<DClasslike> = groupBy { it.dri }.values.flatMap { classlikes ->
+ if (classlikes.size == 1) classlikes else classlikes.map { classlike ->
+ fun ClashingDriIdentifier?.toName() = this?.value?.joinToString(", ", "(", ")") { it.displayName } ?: ""
+ when(classlike) {
+ is DClass -> classlike.copy(name = classlike.name + classlike.extra[ClashingDriIdentifier]?.toName())
+ is DObject -> classlike.copy(name = classlike.name.orEmpty() + classlike.extra[ClashingDriIdentifier]?.toName())
+ is DAnnotation -> classlike.copy(name = classlike.name + classlike.extra[ClashingDriIdentifier]?.toName())
+ is DInterface -> classlike.copy(name = classlike.name + classlike.extra[ClashingDriIdentifier]?.toName())
+ is DEnum -> classlike.copy(name = classlike.name + classlike.extra[ClashingDriIdentifier]?.toName())
+ }
+ }
+ }
+
open fun pageForFunction(f: DFunction) = MemberPageNode(f.name, contentForFunction(f), setOf(f.dri), f)
open fun pageForTypeAlias(t: DTypeAlias) = MemberPageNode(t.name, contentForTypeAlias(t), setOf(t.dri), t)
diff --git a/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt b/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt
index 090196b3..624db485 100644
--- a/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt
+++ b/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt
@@ -12,7 +12,7 @@ class ExpectActualsTest : AbstractCoreTest() {
fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() }
@Test
- fun `two same named expect actual classes`() {
+ fun `three same named expect actual classes`() {
val configuration = dokkaConfiguration {
sourceSets {
@@ -31,12 +31,20 @@ class ExpectActualsTest : AbstractCoreTest() {
sourceRoots = listOf("src/commonJMain/kotlin/pageMerger/Test.kt")
dependentSourceSets = setOf(common.sourceSetID)
}
- val commonN = sourceSet {
+ val commonN1 = sourceSet {
moduleName = "example"
- name = "commonN"
- displayName = "commonN"
+ name = "commonN1"
+ displayName = "commonN1"
analysisPlatform = "common"
- sourceRoots = listOf("src/commonNMain/kotlin/pageMerger/Test.kt")
+ sourceRoots = listOf("src/commonN1Main/kotlin/pageMerger/Test.kt")
+ dependentSourceSets = setOf(common.sourceSetID)
+ }
+ val commonN2 = sourceSet {
+ moduleName = "example"
+ name = "commonN2"
+ displayName = "commonN2"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonN2Main/kotlin/pageMerger/Test.kt")
dependentSourceSets = setOf(common.sourceSetID)
}
val js = sourceSet {
@@ -60,7 +68,7 @@ class ExpectActualsTest : AbstractCoreTest() {
name = "linuxX64"
displayName = "linuxX64"
analysisPlatform = "native"
- dependentSourceSets = setOf(commonN.sourceSetID)
+ dependentSourceSets = setOf(commonN1.sourceSetID)
sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
}
val mingwX64 = sourceSet {
@@ -68,9 +76,25 @@ class ExpectActualsTest : AbstractCoreTest() {
name = "mingwX64"
displayName = "mingwX64"
analysisPlatform = "native"
- dependentSourceSets = setOf(commonN.sourceSetID)
+ dependentSourceSets = setOf(commonN1.sourceSetID)
sourceRoots = listOf("src/mingwX64Main/kotlin/pageMerger/Test.kt")
}
+ val iosArm64 = sourceSet {
+ moduleName = "example"
+ name = "iosArm64"
+ displayName = "iosArm64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(commonN2.sourceSetID)
+ sourceRoots = listOf("src/iosArm64Main/kotlin/pageMerger/Test.kt")
+ }
+ val iosX64 = sourceSet {
+ moduleName = "example"
+ name = "iosX64"
+ displayName = "iosX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(commonN2.sourceSetID)
+ sourceRoots = listOf("src/iosX64Main/kotlin/pageMerger/Test.kt")
+ }
}
}
@@ -84,7 +108,12 @@ class ExpectActualsTest : AbstractCoreTest() {
|
|expect class A
|
- |/src/commonNMain/kotlin/pageMerger/Test.kt
+ |/src/commonN1Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |expect class A
+ |
+ |/src/commonN2Main/kotlin/pageMerger/Test.kt
|package pageMerger
|
|expect class A
@@ -99,7 +128,7 @@ class ExpectActualsTest : AbstractCoreTest() {
|
|actual class A
|
- |/src/linuxX64/kotlin/pageMerger/Test.kt
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
|package pageMerger
|
|actual class A
@@ -109,22 +138,44 @@ class ExpectActualsTest : AbstractCoreTest() {
|
|actual class A
|
+ |/src/iosArm64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual class A
+ |
+ |/src/iosX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual class A
+ |
""".trimMargin(),
configuration
) {
pagesTransformationStage = {
- println(it)
val allChildren = it.childrenRec().filterIsInstance<ClasslikePageNode>()
- val jvmClass = allChildren.filter { it.name == "DoNotMerge(jvm)" }
- val jsClass = allChildren.filter { it.name == "DoNotMerge(js)" }
- val noClass = allChildren.filter { it.name == "DoNotMerge" }
- assertTrue(jvmClass.size == 1) { "There can be only one DoNotMerge(jvm) page" }
- assertTrue(jvmClass.first().documentable?.sourceSets?.single()?.analysisPlatform?.key == "jvm") { "DoNotMerge(jvm) should have only jvm sources" }
+ val commonJ = allChildren.filter { it.name == "A(js, jvm)" }
+ val commonN1 = allChildren.filter { it.name == "A(linuxX64, mingwX64)" }
+ val commonN2 = allChildren.filter { it.name == "A(iosArm64, iosX64)" }
+ val noClass = allChildren.filter { it.name == "A" }
+ assertTrue(commonJ.size == 1) { "There can be only one A(js, jvm) page" }
+ assertTrue(
+ commonJ.first().documentable?.sourceSets?.map { it.displayName }
+ ?.containsAll(listOf("commonJ", "js", "jvm")) ?: false
+ ) { "A(js, jvm)should have commonJ, js, jvm sources" }
+
+ assertTrue(commonN1.size == 1) { "There can be only one A(linuxX64, mingwX64) page" }
+ assertTrue(
+ commonN1.first().documentable?.sourceSets?.map { it.displayName }
+ ?.containsAll(listOf("commonN1", "linuxX64", "mingwX64")) ?: false
+ ) { "A(linuxX64, mingwX64) should have commonN1, linuxX64, mingwX64 sources" }
- assertTrue(jsClass.size == 1) { "There can be only one DoNotMerge(js) page" }
- assertTrue(jsClass.first().documentable?.sourceSets?.single()?.analysisPlatform?.key == "js") { "DoNotMerge(js) should have only js sources" }
+ assertTrue(commonN2.size == 1) { "There can be only one A(iosArm64, iosX64) page" }
+ assertTrue(
+ commonN2.first().documentable?.sourceSets?.map { it.displayName }
+ ?.containsAll(listOf("commonN2", "iosArm64", "iosX64")) ?: false
+ ) { "A(iosArm64, iosX64) should have commonN2, iosArm64, iosX64 sources" }
- assertTrue(noClass.isEmpty()) { "There can't be any DoNotMerge page" }
+ assertTrue(noClass.isEmpty()) { "There can't be any A page" }
}
}
}