From a1f8efc30b4421ce371b02b747bbeac24fafd7ba Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 25 Jun 2020 14:22:51 +0200 Subject: Changed way of merging documentables to avoid exception on merging documentables of different types. Minor javadoc fixes. Changed constructor names. Add handling same name pages clash for different platforms --- plugins/base/src/main/kotlin/DokkaBase.kt | 2 +- .../resolvers/local/DefaultLocationProvider.kt | 1 + .../resolvers/local/DokkaLocationProvider.kt | 40 +- .../documentables/DefaultDocumentableMerger.kt | 410 ++++++++++++--------- .../DefaultDescriptorToDocumentableTranslator.kt | 45 ++- .../documentables/DefaultPageCreator.kt | 2 +- .../test/kotlin/expectActuals/ExpectActualsTest.kt | 131 +++++++ .../kotlin/linkableContent/LinkableContentTest.kt | 91 +++-- .../test/kotlin/pageMerger/PageNodeMergerTest.kt | 66 ++++ .../kotlin/signatures/DivergentSignatureTest.kt | 135 ++----- .../jvmMain/kotlin/example/Clock.kt | 4 + 11 files changed, 606 insertions(+), 321 deletions(-) create mode 100644 plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt (limited to 'plugins/base') diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 44a532f8..fcea6be9 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -54,7 +54,7 @@ class DokkaBase : DokkaPlugin() { } val documentableMerger by extending { - CoreExtensions.documentableMerger with DefaultDocumentableMerger + CoreExtensions.documentableMerger providing ::DefaultDocumentableMerger } val deprecatedDocumentableFilter by extending { diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt index c25aa543..e085af1a 100644 --- a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt @@ -4,6 +4,7 @@ import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DisplaySourceSet import org.jetbrains.dokka.pages.RootPageNode diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt index 096104cc..0269d7a6 100644 --- a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.base.resolvers.local +import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.PointingToDeclaration @@ -26,35 +27,46 @@ open class DokkaLocationProvider( pageGraphRoot.children.forEach { registerPath(it, emptyList()) } } - protected open val pagesIndex: Map = + protected val pagesIndex: Map, ContentPage> = pageGraphRoot.withDescendants().filterIsInstance() - .flatMap { it.dri.map { dri -> dri to it } } + .flatMap { page -> + page.dri.flatMap { dri -> + page.sourceSets().map { sourceSet -> (dri to sourceSet) to page } + } + } .groupingBy { it.first } - .aggregate { dri, _, (_, page), first -> - if (first) page else throw AssertionError("Multiple pages associated with dri: $dri") + .aggregate { key, _, (_, page), first -> + if (first) page else throw AssertionError("Multiple pages associated with key: ${key.first}/${key.second}") } - protected open val anchorsIndex: Map = + protected val anchorsIndex: Map, ContentPage> = pageGraphRoot.withDescendants().filterIsInstance() .flatMap { page -> page.content.withDescendants() .filter { it.extra[SymbolAnchorHint] != null } .mapNotNull { it.dci.dri.singleOrNull() } .distinct() - .map { it to page } + .flatMap { dri -> + page.sourceSets().map { sourceSet -> + (dri to sourceSet) to page + } + } }.toMap() override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean) = pathTo(node, context) + if (!skipExtension) extension else "" - override fun resolve(dri: DRI, sourceSets: Set, context: PageNode?) = - getLocalLocation(dri, context) - ?: getLocalLocation(dri.copy(target = PointingToDeclaration), context) - // Not found in PageGraph, that means it's an external link - ?: getExternalLocation(dri, sourceSets) - ?: getExternalLocation(dri.copy(target = PointingToDeclaration), sourceSets) - - private fun getLocalLocation(dri: DRI, context: PageNode?): String? = + override fun resolve(dri: DRI, sourceSets: Set, context: PageNode?): String? = + sourceSets.mapNotNull { sourceSet -> + val driWithSourceSet = Pair(dri, sourceSet) + getLocalLocation(driWithSourceSet, context) + ?: getLocalLocation(driWithSourceSet.copy(first = dri.copy(target = PointingToDeclaration)), context) + // Not found in PageGraph, that means it's an external link + ?: getExternalLocation(dri, sourceSets) + ?: getExternalLocation(dri.copy(target = PointingToDeclaration), sourceSets) + }.distinct().singleOrNull() + + private fun getLocalLocation(dri: Pair, context: PageNode?): String? = pagesIndex[dri]?.let { resolve(it, context) } ?: anchorsIndex[dri]?.let { resolve(it, context) + "#$dri" } diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt index 17ead667..d556c9cb 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt @@ -1,195 +1,269 @@ 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.WithExtraProperties import org.jetbrains.dokka.model.properties.mergeExtras import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.documentation.DocumentableMerger import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult +import org.jetbrains.kotlin.utils.addToStdlib.safeAs -internal object DefaultDocumentableMerger : DocumentableMerger { +internal class DefaultDocumentableMerger(context: DokkaContext) : DocumentableMerger { + private val dependencyInfo = context.getDependencyInfo() - override fun invoke(modules: Collection, context: DokkaContext): DModule { - return modules.reduce { left, right -> + override fun invoke(modules: Collection): DModule { + + return topologicalSort(modules).reduce { left, right -> val list = listOf(left, right) DModule( name = modules.map { it.name }.distinct().joinToString("|"), packages = merge( - list.flatMap { it.packages }, - DPackage::mergeWith - ), + list.flatMap { it.packages } + ) { pck1, pck2 -> pck1.mergeWith(pck2) }, documentation = list.map { it.documentation }.flatMap { it.entries }.associate { (k, v) -> k to v }, expectPresentInSet = list.firstNotNullResult { it.expectPresentInSet }, sourceSets = list.flatMap { it.sourceSets }.toSet() ).mergeExtras(left, right) } } -} -private fun merge(elements: List, reducer: (T, T) -> T): List = - elements.groupingBy { it.dri } - .reduce { _, left, right -> reducer(left, right) } - .values.toList() + private fun topologicalSort(allModules: Collection): List { -private fun mergeExpectActual( - elements: List, - reducer: (T, T) -> T -): List where T : Documentable, T : WithExpectActual { + val modulesMap: Map = + allModules.groupBy { it.sourceSets.single().sourceSetID } - fun analyzeExpectActual(sameDriElements: List) = sameDriElements.reduce(reducer) + //this returns representation of graph where directed edges are leading from module to modules that depend on it + val graph: Map> = modulesMap.flatMap { (_, module) -> + module.first().sourceSets.single().dependentSourceSets.map { sourceSet -> + modulesMap[sourceSet] to module + } + }.groupBy { it.first }.entries + .map { it.key to it.value.map { it.second } } + .toMap() - return elements.groupBy { it.dri }.values.map(::analyzeExpectActual) -} -fun DPackage.mergeWith(other: DPackage): DPackage = copy( - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - typealiases = merge(typealiases + other.typealiases, DTypeAlias::mergeWith), - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DFunction.mergeWith(other: DFunction): DFunction = copy( - parameters = merge(this.parameters + other.parameters, DParameter::mergeWith), - receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - modifier = modifier + other.modifier, - sourceSets = sourceSets + other.sourceSets, - generics = merge(generics + other.generics, DTypeParameter::mergeWith) -).mergeExtras(this, other) - -fun DProperty.mergeWith(other: DProperty): DProperty = copy( - receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - modifier = modifier + other.modifier, - sourceSets = sourceSets + other.sourceSets, - getter = getter?.let { g -> other.getter?.let { g.mergeWith(it) } ?: g } ?: other.getter, - setter = setter?.let { s -> other.setter?.let { s.mergeWith(it) } ?: s } ?: other.setter, - generics = merge(generics + other.generics, DTypeParameter::mergeWith) -).mergeExtras(this, other) - -fun DClasslike.mergeWith(other: DClasslike): DClasslike = when { - this is DClass && other is DClass -> mergeWith(other) - this is DEnum && other is DEnum -> mergeWith(other) - this is DInterface && other is DInterface -> mergeWith(other) - this is DObject && other is DObject -> mergeWith(other) - this is DAnnotation && other is DAnnotation -> mergeWith(other) - else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be mergesd with ${other::class.qualifiedName} ${other.name}") + val visited = modulesMap.map { it.value to false }.toMap().toMutableMap() + val topologicalSortedList: MutableList = mutableListOf() + + fun dfs(module: ModuleOfDifferentTranslators) { + visited[module] = true + graph[module]?.forEach { if (!visited[it]!!) dfs(it) } + topologicalSortedList.add(0, module) + } + modulesMap.values.forEach { if (!visited[it]!!) dfs(it) } + + return topologicalSortedList.flatten() + } + + private fun DokkaContext.getDependencyInfo() + : Map> { + + fun getDependencies(sourceSet: DokkaConfiguration.DokkaSourceSet): List = + listOf(sourceSet) + configuration.sourceSets.filter { + it.sourceSetID in sourceSet.dependentSourceSets + }.flatMap { getDependencies(it) } + + return configuration.sourceSets.map { it to getDependencies(it) }.toMap() + } + + private fun merge(elements: List, reducer: (T, T) -> T): List = + elements.groupingBy { it.dri } + .reduce { _, left, right -> reducer(left, right) } + .values.toList() + + private fun mergeExpectActual( + elements: List, + reducer: (T, T) -> T + ): List where T : Documentable, T : WithExpectActual { + + fun analyzeExpectActual(sameDriElements: List) = sameDriElements + .partition { it.expectPresentInSet != null } + .let { (expects, actuals) -> + expects.map { expect -> + listOf(expect) + actuals.filter { actual -> + dependencyInfo[actual.sourceSets.single()] + ?.contains(expect.expectPresentInSet!!) + ?: throw IllegalStateException("Cannot resolve expect/actual relation for ${actual.name}") + } + }.map { it.reduce(reducer) } + } + + fun T.isExpectActual(): Boolean = + this.safeAs>().let { it != null && it.extra[IsExpectActual] != null } + + fun mergeClashingElements(elements: List) = if(elements.size <= 1) elements else elements.map { + when(it) { + is DClass -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + is DObject -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + is DAnnotation -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + is DInterface -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + is DEnum -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + is DFunction -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + is DProperty -> it.copy(name = "${it.name}(${it.sourceSets.single().analysisPlatform.key})") + else -> elements + } + } as List + + return elements.partition { + it.isExpectActual() + }.let { (expectActuals, notExpectActuals) -> + notExpectActuals.groupBy { it.dri }.values.flatMap(::mergeClashingElements) + + expectActuals.groupBy { it.dri }.values.flatMap(::analyzeExpectActual) + } + } + + fun DPackage.mergeWith(other: DPackage): DPackage = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + typealiases = merge(typealiases + other.typealiases) { ta1, ta2 -> ta1.mergeWith(ta2) }, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DFunction.mergeWith(other: DFunction): DFunction = copy( + parameters = merge(this.parameters + other.parameters) { p1, p2 -> p1.mergeWith(p2) }, + receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + modifier = modifier + other.modifier, + sourceSets = sourceSets + other.sourceSets, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + ).mergeExtras(this, other) + + fun DProperty.mergeWith(other: DProperty): DProperty = copy( + receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + modifier = modifier + other.modifier, + sourceSets = sourceSets + other.sourceSets, + getter = getter?.let { g -> other.getter?.let { g.mergeWith(it) } ?: g } ?: other.getter, + setter = setter?.let { s -> other.setter?.let { s.mergeWith(it) } ?: s } ?: other.setter, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + ).mergeExtras(this, other) + + fun DClasslike.mergeWith(other: DClasslike): DClasslike = when { + this is DClass && other is DClass -> mergeWith(other) + this is DEnum && other is DEnum -> mergeWith(other) + this is DInterface && other is DInterface -> mergeWith(other) + this is DObject && other is DObject -> mergeWith(other) + this is DAnnotation && other is DAnnotation -> mergeWith(other) + else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be merged with ${other::class.qualifiedName} ${other.name}") + } + + fun DClass.mergeWith(other: DClass): DClass = copy( + constructors = mergeExpectActual( + constructors + other.constructors + ) { f1, f2 -> f1.mergeWith(f2) }, + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + modifier = modifier + other.modifier, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DEnum.mergeWith(other: DEnum): DEnum = copy( + entries = merge(entries + other.entries) { ee1, ee2 -> ee1.mergeWith(ee2) }, + constructors = mergeExpectActual( + constructors + other.constructors + ) { f1, f2 -> f1.mergeWith(f2) }, + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DEnumEntry.mergeWith(other: DEnumEntry): DEnumEntry = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DObject.mergeWith(other: DObject): DObject = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DInterface.mergeWith(other: DInterface): DInterface = copy( + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) }, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DAnnotation.mergeWith(other: DAnnotation): DAnnotation = copy( + constructors = mergeExpectActual( + constructors + other.constructors + ) { f1, f2 -> f1.mergeWith(f2) }, + functions = mergeExpectActual(functions + other.functions) { f1, f2 -> f1.mergeWith(f2) }, + properties = mergeExpectActual(properties + other.properties) { p1, p2 -> p1.mergeWith(p2) }, + classlikes = mergeExpectActual(classlikes + other.classlikes) { c1, c2 -> c1.mergeWith(c2) }, + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources + other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets, + generics = merge(generics + other.generics) { tp1, tp2 -> tp1.mergeWith(tp2) } + ).mergeExtras(this, other) + + fun DParameter.mergeWith(other: DParameter): DParameter = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DTypeParameter.mergeWith(other: DTypeParameter): DTypeParameter = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) + + fun DTypeAlias.mergeWith(other: DTypeAlias): DTypeAlias = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + underlyingType = underlyingType + other.underlyingType, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets + ).mergeExtras(this, other) } -fun DClass.mergeWith(other: DClass): DClass = copy( - constructors = mergeExpectActual( - constructors + other.constructors, - DFunction::mergeWith - ), - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - generics = merge(generics + other.generics, DTypeParameter::mergeWith), - modifier = modifier + other.modifier, - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DEnum.mergeWith(other: DEnum): DEnum = copy( - entries = merge(entries + other.entries, DEnumEntry::mergeWith), - constructors = mergeExpectActual( - constructors + other.constructors, - DFunction::mergeWith - ), - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DEnumEntry.mergeWith(other: DEnumEntry): DEnumEntry = copy( - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DObject.mergeWith(other: DObject): DObject = copy( - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DInterface.mergeWith(other: DInterface): DInterface = copy( - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - generics = merge(generics + other.generics, DTypeParameter::mergeWith), - supertypes = supertypes + other.supertypes, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DAnnotation.mergeWith(other: DAnnotation): DAnnotation = copy( - constructors = mergeExpectActual( - constructors + other.constructors, - DFunction::mergeWith - ), - functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), - properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), - classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), - companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sources = sources + other.sources, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets, - generics = merge(generics + other.generics, DTypeParameter::mergeWith) -).mergeExtras(this, other) - -fun DParameter.mergeWith(other: DParameter): DParameter = copy( - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DTypeParameter.mergeWith(other: DTypeParameter): DTypeParameter = copy( - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) - -fun DTypeAlias.mergeWith(other: DTypeAlias): DTypeAlias = copy( - documentation = documentation + other.documentation, - expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, - underlyingType = underlyingType + other.underlyingType, - visibility = visibility + other.visibility, - sourceSets = sourceSets + other.sourceSets -).mergeExtras(this, other) +private typealias ModuleOfDifferentTranslators = List diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index 8e5a1927..57d4f151 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -12,6 +12,7 @@ import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.Nullable import org.jetbrains.dokka.model.TypeConstructor import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.properties.ExtraProperty import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator @@ -140,6 +141,7 @@ private class DokkaDescriptorVisitor( val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect + val isActual = descriptor.isActual val info = descriptor.resolveClassDescriptionData() return DInterface( @@ -156,11 +158,11 @@ private class DokkaDescriptorVisitor( generics = descriptor.declaredTypeParameters.map { it.toVariantTypeParameter() }, companion = descriptor.companion(driWithPlatform), sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } @@ -168,6 +170,7 @@ private class DokkaDescriptorVisitor( val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect + val isActual = descriptor.isActual val info = descriptor.resolveClassDescriptionData() @@ -183,11 +186,11 @@ private class DokkaDescriptorVisitor( supertypes = info.supertypes.toSourceSetDependent(), documentation = info.docs, sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } @@ -195,6 +198,7 @@ private class DokkaDescriptorVisitor( val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect + val isActual = descriptor.isActual val info = descriptor.resolveClassDescriptionData() return DEnum( @@ -212,11 +216,11 @@ private class DokkaDescriptorVisitor( documentation = info.docs, companion = descriptor.companion(driWithPlatform), sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } @@ -245,6 +249,8 @@ private class DokkaDescriptorVisitor( fun annotationDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DAnnotation { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope + val isExpect = descriptor.isExpect + val isActual = descriptor.isActual return DAnnotation( dri = driWithPlatform.dri, @@ -253,12 +259,12 @@ private class DokkaDescriptorVisitor( classlikes = scope.classlikes(driWithPlatform), functions = scope.functions(driWithPlatform), properties = scope.properties(driWithPlatform), - expectPresentInSet = null, + expectPresentInSet = sourceSet.takeIf { isExpect }, sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ), + ).let { if (isExpect || isActual) it + IsExpectActual else it }, companion = descriptor.companionObjectDescriptor?.let { objectDescriptor(it, driWithPlatform) }, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), generics = descriptor.declaredTypeParameters.map { it.toVariantTypeParameter() }, @@ -271,6 +277,7 @@ private class DokkaDescriptorVisitor( val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect + val isActual = descriptor.isActual val info = descriptor.resolveClassDescriptionData() val actual = descriptor.createSources() @@ -296,17 +303,18 @@ private class DokkaDescriptorVisitor( modifier = descriptor.modifier().toSourceSetDependent(), companion = descriptor.companion(driWithPlatform), sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): DProperty { val dri = parent.dri.copy(callable = Callable.from(descriptor)) val isExpect = descriptor.isExpect + val isActual = descriptor.isActual val actual = descriptor.createSources() return DProperty( @@ -333,7 +341,7 @@ private class DokkaDescriptorVisitor( (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations() - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } @@ -346,6 +354,7 @@ private class DokkaDescriptorVisitor( override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): DFunction { val (dri, inheritedFrom) = descriptor.createDRI() val isExpect = descriptor.isExpect + val isActual = descriptor.isActual val actual = descriptor.createSources() return DFunction( @@ -367,11 +376,11 @@ private class DokkaDescriptorVisitor( modifier = descriptor.modifier().toSourceSetDependent(), type = descriptor.returnType!!.toBound(), sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( InheritedFunction(inheritedFrom.toSourceSetDependent()), descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } @@ -380,6 +389,7 @@ private class DokkaDescriptorVisitor( val dri = parent.dri.copy(callable = Callable.from(descriptor, name)) val actual = descriptor.createSources() val isExpect = descriptor.isExpect + val isActual = descriptor.isActual return DFunction( dri = dri, @@ -418,7 +428,7 @@ private class DokkaDescriptorVisitor( if (descriptor.isPrimary) { it + PrimaryConstructorExtra } else it - } + }.let { if (isExpect || isActual) it + IsExpectActual else it } ) } @@ -443,6 +453,7 @@ private class DokkaDescriptorVisitor( val dri = parent.copy(callable = Callable.from(descriptor)) val isGetter = descriptor is PropertyGetterDescriptor val isExpect = descriptor.isExpect + val isActual = descriptor.isActual fun PropertyDescriptor.asParameter(parent: DRI) = DParameter( @@ -490,10 +501,10 @@ private class DokkaDescriptorVisitor( }, sources = descriptor.createSources(), sourceSets = setOf(sourceSet), - extra = PropertyContainer.withAll( + extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ) + ).let { if (isExpect || isActual) it + IsExpectActual else it } ) } diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 3cc77f96..615412f4 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -490,7 +490,7 @@ open class DefaultPageCreator( header(2, name, kind = kind) table(kind, extra = extra, styles = emptySet()) { collection - .groupBy { it.name } + .groupBy { it.name } // This groupBy should probably use LocationProvider // This hacks displaying actual typealias signatures along classlike ones .mapValues { if (it.value.any { it is DClasslike }) it.value.filter { it !is DTypeAlias } else it.value } .toSortedMap(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it }) diff --git a/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt b/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt new file mode 100644 index 00000000..090196b3 --- /dev/null +++ b/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt @@ -0,0 +1,131 @@ +package expectActuals + +import org.jetbrains.dokka.pages.ClasslikePageNode +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.assertTrue + + +class ExpectActualsTest : AbstractCoreTest() { + + fun PageNode.childrenRec(): List = listOf(this) + children.flatMap { it.childrenRec() } + + @Test + fun `two same named expect actual classes`() { + + val configuration = dokkaConfiguration { + sourceSets { + val common = sourceSet { + moduleName = "example" + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt") + } + val commonJ = sourceSet { + moduleName = "example" + name = "commonJ" + displayName = "commonJ" + analysisPlatform = "common" + sourceRoots = listOf("src/commonJMain/kotlin/pageMerger/Test.kt") + dependentSourceSets = setOf(common.sourceSetID) + } + val commonN = sourceSet { + moduleName = "example" + name = "commonN" + displayName = "commonN" + analysisPlatform = "common" + sourceRoots = listOf("src/commonNMain/kotlin/pageMerger/Test.kt") + dependentSourceSets = setOf(common.sourceSetID) + } + val js = sourceSet { + moduleName = "example" + name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(commonJ.sourceSetID) + sourceRoots = listOf("src/jsMain/kotlin/pageMerger/Test.kt") + } + val jvm = sourceSet { + moduleName = "example" + name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + dependentSourceSets = setOf(commonJ.sourceSetID) + sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt") + } + val linuxX64 = sourceSet { + moduleName = "example" + name = "linuxX64" + displayName = "linuxX64" + analysisPlatform = "native" + dependentSourceSets = setOf(commonN.sourceSetID) + sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt") + } + val mingwX64 = sourceSet { + moduleName = "example" + name = "mingwX64" + displayName = "mingwX64" + analysisPlatform = "native" + dependentSourceSets = setOf(commonN.sourceSetID) + sourceRoots = listOf("src/mingwX64Main/kotlin/pageMerger/Test.kt") + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |/src/commonJMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |expect class A + | + |/src/commonNMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |expect class A + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |actual class A + | + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |actual class A + | + |/src/linuxX64/kotlin/pageMerger/Test.kt + |package pageMerger + | + |actual class A + | + |/src/mingwX64Main/kotlin/pageMerger/Test.kt + |package pageMerger + | + |actual class A + | + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + println(it) + val allChildren = it.childrenRec().filterIsInstance() + 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" } + + 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(noClass.isEmpty()) { "There can't be any DoNotMerge page" } + } + } + } +} \ 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 25400ca5..f8eba2fe 100644 --- a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt +++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt @@ -28,20 +28,33 @@ class LinkableContentTest : AbstractCoreTest() { val configuration = dokkaConfiguration { moduleName = "example" sourceSets { - sourceSet { - analysisPlatform = "js" - sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } + val common = sourceSet { + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf(Paths.get("$testDataDir/commonMain/kotlin").toString()) + } + val jvmAndJsSecondCommonMain = sourceSet { + name = "jvmAndJsSecondCommonMain" + displayName = "jvmAndJsSecondCommonMain" + analysisPlatform = "common" + dependentSourceSets = setOf(common.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmAndJsSecondCommonMain/kotlin").toString()) + } + val js = sourceSet { name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jsMain/kotlin").toString()) includes = listOf(Paths.get("$includesDir/include2.md").toString()) } - sourceSet { - analysisPlatform = "jvm" - sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } + val jvm = sourceSet { name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmMain/kotlin").toString()) includes = listOf(Paths.get("$includesDir/include1.md").toString()) } } @@ -67,9 +80,25 @@ class LinkableContentTest : AbstractCoreTest() { moduleName = "example" sourceSets { - sourceSet { + val common = sourceSet { + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf(Paths.get("$testDataDir/commonMain/kotlin").toString()) + } + val jvmAndJsSecondCommonMain = sourceSet { + name = "jvmAndJsSecondCommonMain" + displayName = "jvmAndJsSecondCommonMain" + analysisPlatform = "common" + dependentSourceSets = setOf(common.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmAndJsSecondCommonMain/kotlin").toString()) + } + val js = sourceSet { + name = "js" + displayName = "js" analysisPlatform = "js" - sourceRoots = listOf("$testDataDir/jsMain/kotlin") + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jsMain/kotlin").toString()) sourceLinks = listOf( SourceLinkDefinitionImpl( localDirectory = "$testDataDir/jsMain/kotlin", @@ -77,11 +106,13 @@ class LinkableContentTest : AbstractCoreTest() { remoteLineSuffix = "#L" ) ) - name = "js" } - sourceSet { + val jvm = sourceSet { + name = "jvm" + displayName = "jvm" analysisPlatform = "jvm" - sourceRoots = listOf("$testDataDir/jvmMain/kotlin") + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmMain/kotlin").toString()) sourceLinks = listOf( SourceLinkDefinitionImpl( localDirectory = "$testDataDir/jvmMain/kotlin", @@ -89,7 +120,6 @@ class LinkableContentTest : AbstractCoreTest() { remoteLineSuffix = "#L" ) ) - name = "jvm" } } } @@ -131,16 +161,33 @@ class LinkableContentTest : AbstractCoreTest() { val configuration = dokkaConfiguration { moduleName = "example" sourceSets { - sourceSet { - analysisPlatform = "js" - sourceRoots = listOf("$testDataDir/jsMain/kotlin") + val common = sourceSet { + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf(Paths.get("$testDataDir/commonMain/kotlin").toString()) + } + val jvmAndJsSecondCommonMain = sourceSet { + name = "jvmAndJsSecondCommonMain" + displayName = "jvmAndJsSecondCommonMain" + analysisPlatform = "common" + dependentSourceSets = setOf(common.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmAndJsSecondCommonMain/kotlin").toString()) + } + val js = sourceSet { name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jsMain/kotlin").toString()) samples = listOf("$testDataDir/jsMain/resources/Samples.kt") } - sourceSet { - analysisPlatform = "jvm" - sourceRoots = listOf("$testDataDir/jvmMain/kotlin") + val jvm = sourceSet { name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmMain/kotlin").toString()) samples = listOf("$testDataDir/jvmMain/resources/Samples.kt") } } diff --git a/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt index 935b9377..1a57ae02 100644 --- a/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt +++ b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt @@ -1,5 +1,6 @@ package pageMerger +import org.jetbrains.dokka.pages.ClasslikePageNode import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.PageNode import org.junit.jupiter.api.Assertions.assertTrue @@ -123,4 +124,69 @@ class PageNodeMergerTest : AbstractCoreTest() { fun PageNode.childrenRec(): List = listOf(this) + children.flatMap { it.childrenRec() } + + @Test + fun `should not be merged`() { + + val configuration = dokkaConfiguration { + sourceSets { + val common = sourceSet { + moduleName = "example" + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt") + } + val js = sourceSet { + moduleName = "example" + name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(common.sourceSetID) + sourceRoots = listOf("src/jsMain/kotlin/pageMerger/Test.kt") + } + val jvm = sourceSet { + moduleName = "example" + name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + dependentSourceSets = setOf(common.sourceSetID) + sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt") + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |annotation class DoNotMerge + | + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |annotation class DoNotMerge + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + println(it) + val allChildren = it.childrenRec().filterIsInstance() + 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" } + + 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(noClass.isEmpty()) { "There can't be any DoNotMerge page" } + } + } + } } diff --git a/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt index 2e8e0ef3..975373b6 100644 --- a/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt @@ -10,40 +10,43 @@ import utils.TestOutputWriterPlugin class DivergentSignatureTest : AbstractCoreTest() { - @Test - fun `group { common + jvm + js }`() { - - val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() - - val configuration = dokkaConfiguration { - moduleName = "example" - sourceSets { - sourceSet { - displayName = "js" - name = "js" - analysisPlatform = "js" - sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - sourceSet { - displayName = "jvm" - name = "jvm" - analysisPlatform = "jvm" - sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - sourceSet { - displayName = "common" - name = "common" - analysisPlatform = "common" - sourceRoots = listOf("commonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + + val configuration = dokkaConfiguration { + moduleName = "example" + sourceSets { + val common = sourceSet { + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf(Paths.get("$testDataDir/commonMain/kotlin").toString()) + } + val jvmAndJsSecondCommonMain = sourceSet { + name = "jvmAndJsSecondCommonMain" + displayName = "jvmAndJsSecondCommonMain" + analysisPlatform = "common" + dependentSourceSets = setOf(common.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmAndJsSecondCommonMain/kotlin").toString()) + } + val js = sourceSet { + name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jsMain/kotlin").toString()) + } + val jvm = sourceSet { + name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + dependentSourceSets = setOf(common.value.sourceSetID, jvmAndJsSecondCommonMain.value.sourceSetID) + sourceRoots = listOf(Paths.get("$testDataDir/jvmMain/kotlin").toString()) } } + } + + @Test + fun `group { common + jvm + js }`() { val writerPlugin = TestOutputWriterPlugin() @@ -55,7 +58,7 @@ class DivergentSignatureTest : AbstractCoreTest() { val content = writerPlugin.renderedContent("example/example/-clock/get-time.html") assert(content.count() == 1) - assert(content.select("[data-filterable-current=example/js example/jvm example/common]").single().brief == "") + assert(content.select("[data-filterable-current=example/common example/jvm example/js]").single().brief == "common") } } } @@ -63,38 +66,6 @@ class DivergentSignatureTest : AbstractCoreTest() { @Test fun `group { common + jvm }, group { js }`() { - val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() - - val configuration = dokkaConfiguration { - moduleName = "example" - sourceSets { - sourceSet { - displayName = "js" - name = "js" - analysisPlatform = "js" - sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - sourceSet { - displayName = "jvm" - name = "jvm" - analysisPlatform = "jvm" - sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - sourceSet { - displayName = "common" - name = "common" - analysisPlatform = "common" - sourceRoots = listOf("commonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - } - } - val writerPlugin = TestOutputWriterPlugin() testFromData( @@ -104,7 +75,7 @@ class DivergentSignatureTest : AbstractCoreTest() { renderingStage = { _, _ -> val content = writerPlugin.renderedContent("example/example/-clock/get-times-in-millis.html") assert(content.count() == 2) - assert(content.select("[data-filterable-current=example/jvm example/common]").single().brief == "Time in minis") + assert(content.select("[data-filterable-current=example/common example/jvm]").single().brief == "Time in minis common") assert(content.select("[data-filterable-current=example/js]").single().brief == "JS implementation of getTimeInMillis js" ) } } @@ -113,38 +84,6 @@ class DivergentSignatureTest : AbstractCoreTest() { @Test fun `group { js }, group { jvm }, group { js }`() { - val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() - - val configuration = dokkaConfiguration { - moduleName = "example" - sourceSets { - sourceSet { - displayName = "js" - name = "js" - analysisPlatform = "js" - sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - sourceSet { - displayName = "jvm" - name = "jvm" - analysisPlatform = "jvm" - sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - sourceSet { - displayName = "common" - name = "common" - analysisPlatform = "common" - sourceRoots = listOf("commonMain").map { - Paths.get("$testDataDir/$it/kotlin").toString() - } - } - } - } - val writerPlugin = TestOutputWriterPlugin() testFromData( diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt index fec06207..6ad73db7 100644 --- a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt @@ -7,6 +7,10 @@ import greeteer.Greeter */ actual open class Clock { actual fun getTime(): String = System.currentTimeMillis().toString() + + /** + * Time in minis + */ actual fun getTimesInMillis(): String = System.currentTimeMillis().toString() /** -- cgit