aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/kotlin/model/Documentable.kt50
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt270
2 files changed, 216 insertions, 104 deletions
diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt
index 689b5c9d..48f00474 100644
--- a/core/src/main/kotlin/model/Documentable.kt
+++ b/core/src/main/kotlin/model/Documentable.kt
@@ -8,6 +8,7 @@ import org.jetbrains.dokka.model.properties.WithExtraProperties
import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
+import org.jetbrains.kotlin.load.kotlin.toSourceElement
abstract class Documentable {
abstract val name: String?
@@ -37,7 +38,8 @@ abstract class Documentable {
}
data class PlatformDependent<out T>(
- val map: Map<PlatformData, T>, val expect: T? = null
+ val map: Map<PlatformData, T>,
+ val expect: T? = null
) : Map<PlatformData, T> by map {
val prevalentValue: T?
get() = map.values.distinct().singleOrNull()
@@ -89,13 +91,15 @@ interface WithGenerics {
val generics: List<TypeParameter>
}
+interface WithSupertypes {
+ val supertypes: PlatformDependent<List<DRI>>
+}
+
interface Callable : WithVisibility, WithType, WithAbstraction, WithExpectActual {
val receiver: Parameter?
}
-abstract class Classlike : Documentable(), WithScope, WithVisibility, WithExpectActual {
- abstract val supertypes: PlatformDependent<List<DRI>>
-}
+abstract class Classlike : Documentable(), WithScope, WithVisibility, WithExpectActual
data class Module(
override val name: String,
@@ -144,7 +148,9 @@ data class Class(
override val modifier: WithAbstraction.Modifier,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Class> = PropertyContainer.empty()
-) : Classlike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithExtraProperties<Class> {
+) : Classlike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes,
+ WithExtraProperties<Class> {
+
override val children: List<Documentable>
get() = (functions + properties + classlikes + listOfNotNull(companion) + constructors) as List<Documentable>
@@ -166,7 +172,7 @@ data class Enum(
override val supertypes: PlatformDependent<List<DRI>>,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Enum> = PropertyContainer.empty()
-) : Classlike(), WithCompanion, WithConstructors, WithExtraProperties<Enum> {
+) : Classlike(), WithCompanion, WithConstructors, WithSupertypes, WithExtraProperties<Enum> {
override val children: List<Documentable>
get() = (entries + functions + properties + classlikes + listOfNotNull(companion) + constructors) as List<Documentable>
@@ -203,7 +209,7 @@ data class Function(
override val modifier: WithAbstraction.Modifier,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Function> = PropertyContainer.empty()
- ) : Documentable(), Callable, WithGenerics, WithExtraProperties<Function> {
+) : Documentable(), Callable, WithGenerics, WithExtraProperties<Function> {
override val children: List<Documentable>
get() = parameters
@@ -224,7 +230,7 @@ data class Interface(
override val supertypes: PlatformDependent<List<DRI>>,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Interface> = PropertyContainer.empty()
-) : Classlike(), WithCompanion, WithGenerics, WithExtraProperties<Interface> {
+) : Classlike(), WithCompanion, WithGenerics, WithSupertypes, WithExtraProperties<Interface> {
override val children: List<Documentable>
get() = (functions + properties + classlikes + listOfNotNull(companion)) as List<Documentable>
@@ -243,7 +249,7 @@ data class Object(
override val supertypes: PlatformDependent<List<DRI>>,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Object> = PropertyContainer.empty()
-) : Classlike(), WithExtraProperties<Object> {
+) : Classlike(), WithSupertypes, WithExtraProperties<Object> {
override val children: List<Documentable>
get() = (functions + properties + classlikes) as List<Documentable>
@@ -263,9 +269,7 @@ data class Annotation(
override val constructors: List<Function>,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Annotation> = PropertyContainer.empty()
-) : Documentable(), WithScope, WithVisibility, WithCompanion, WithConstructors, WithExpectActual,
- WithExtraProperties<Annotation> {
-
+) : Classlike(), WithCompanion, WithConstructors, WithExtraProperties<Annotation> {
override val children: List<Documentable>
get() = (functions + properties + classlikes + constructors + listOfNotNull(companion)) as List<Documentable>
@@ -280,7 +284,8 @@ data class Property(
override val visibility: PlatformDependent<Visibility>,
override val type: TypeWrapper,
override val receiver: Parameter?,
- val accessors: PlatformDependent<Function>, // TODO > extra
+ val setter: Function?,
+ val getter: Function,
override val modifier: WithAbstraction.Modifier,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<Property> = PropertyContainer.empty()
@@ -313,7 +318,7 @@ data class TypeParameter(
val bounds: List<Projection>,
override val platformData: List<PlatformData>,
override val extra: PropertyContainer<TypeParameter> = PropertyContainer.empty()
-): Documentable(), WithExtraProperties<TypeParameter> {
+) : Documentable(), WithExtraProperties<TypeParameter> {
override val children: List<Nothing>
get() = emptyList()
@@ -321,10 +326,10 @@ data class TypeParameter(
}
sealed class Projection {
- data class OtherParameter(val name: String): Projection()
- object Star: Projection()
- data class TypeConstructor(val dri: DRI, val projections: List<Projection>): Projection()
- data class Nullable(val inner: Projection): Projection()
+ data class OtherParameter(val name: String) : Projection()
+ object Star : Projection()
+ data class TypeConstructor(val dri: DRI, val projections: List<Projection>) : Projection()
+ data class Nullable(val inner: Projection) : Projection()
}
private fun String.shorten(maxLength: Int) = lineSequence().first().let {
@@ -338,6 +343,9 @@ fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? =
this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull()
}
-sealed class DocumentableSource
-class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource()
-class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource()
+sealed class DocumentableSource(val path: String)
+
+class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) :
+ DocumentableSource(descriptor.toSourceElement.containingFile.toString())
+
+class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource(psi.containingFile.virtualFile.path) \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
index 6b612733..485aca4c 100644
--- a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
+++ b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
@@ -3,20 +3,34 @@ package org.jetbrains.dokka.base.transformers.documentables
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.Enum
import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.model.Package
+import org.jetbrains.dokka.model.Annotation
+import org.jetbrains.dokka.model.properties.mergeExtras
+import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.documentation.DocumentableMerger
+import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal object DefaultDocumentableMerger : DocumentableMerger {
override fun invoke(modules: Collection<Module>, context: DokkaContext): Module {
- if (!modules.all { it.name == modules.first().name })
+ val name = modules.map { it.name }.distinct().singleOrNull() ?: run {
context.logger.error("All module names need to be the same")
- return Module(
- modules.first().name,
- merge(
- modules.flatMap { it.packages },
- Package::mergeWith
- )
- )
+ modules.first().name
+ }
+
+ return modules.reduce { left, right ->
+ val list = listOf(left, right)
+
+ Module(
+ name = name,
+ packages = merge(
+ list.flatMap { it.packages },
+ Package::mergeWith
+ ),
+ documentation = list.platformDependentFor { documentation },
+ platformData = list.flatMap { it.platformData }.distinct()
+ ).mergeExtras(left, right)
+ }
}
}
@@ -25,91 +39,181 @@ private fun <T : Documentable> merge(elements: List<T>, reducer: (T, T) -> T): L
.reduce { _, left, right -> reducer(left, right) }
.values.toList()
-fun PlatformInfo.mergeWith(other: PlatformInfo?) = BasePlatformInfo(
- documentationNode,
- (platformData + (other?.platformData ?: emptyList())).distinct()
-)
+private fun <T : Any, D : Documentable> Iterable<D>.platformDependentFor(
+ selector: D.() -> PlatformDependent<T>
+): PlatformDependent<T> {
+ val actuals = map { it.selector().map }
+ .flatMap { it.entries }
+ .associate { (k, v) -> k to v }
-fun ClassPlatformInfo.mergeWith(other: ClassPlatformInfo?) = ClassPlatformInfo(
- info.mergeWith(other?.info),
- (inherited + (other?.inherited ?: emptyList())).distinct()
-)
+ val expected = firstNotNullResult { it.selector().expect }
-fun List<ClassPlatformInfo>.mergeClassPlatformInfo(): List<ClassPlatformInfo> =
- groupingBy { it.documentationNode.children + it.inherited }.reduce { _, left, right ->
- left.mergeWith(right)
- }.values.toList()
-
-fun List<PlatformInfo>.merge(): List<PlatformInfo> =
- groupingBy { it.documentationNode }.reduce { _, left, right ->
- left.mergeWith(right)
- }.values.toList()
-
-fun Function.mergeWith(other: Function): Function = Function(
- dri,
- name,
- returnType,
- isConstructor,
- other.receiver?.let { receiver?.mergeWith(it) },
- merge(parameters + other.parameters, Parameter::mergeWith),
- expected?.mergeWith(other.expected),
- (actual + other.actual).merge(),
- visibility = (visibility + other.visibility)
-)
+ return PlatformDependent(actuals, expected)
+}
-fun Property.mergeWith(other: Property) = Property(
- dri,
- name,
- other.receiver?.let { receiver?.mergeWith(it) },
- expected?.mergeWith(other.expected),
- (actual + other.actual).merge(),
- accessors = (this.accessors + other.accessors).distinct(),
- visibility = (visibility + other.visibility)
+private fun <T: Any> PlatformDependent<T>.mergeWith(other: PlatformDependent<T>) = PlatformDependent(
+ map = this + other,
+ expect = expect ?: other.expect
)
+private fun <T> mergeExpectActual(
+ elements: List<T>,
+ reducer: (T, T) -> T,
+ platformSetter: T.(List<PlatformData>) -> T
+): List<T> where T : Documentable, T : WithExpectActual {
+
+ fun findExpect(actual: T, expects: List<T>): Expect<T> =
+ expects.find { it.platformData.containsAll(actual.platformData) }.let { Expect.from(it) }
+
+ fun reduceExpectActual(entry: Map.Entry<Expect<T>, List<T>>): List<T> = when (val expect = entry.key) {
+ Expect.NotFound -> entry.value
+ is Expect.Found -> entry.value.plus(expect.expect).reduce(reducer).let(::listOf)
+ }
+
+ fun analyzeExpectActual(sameDriElements: List<T>): List<T> {
+ val (expect, actual) = sameDriElements.partition { it.actual.expect != null }
+ val mergedExpect = expect.groupBy { it.actual.expect?.path }.values.map { e ->
+ e.first().platformSetter(e.flatMap { it.platformData }.distinct())
+ }
+ return actual.groupBy { findExpect(it, mergedExpect) }.flatMap { reduceExpectActual(it) }
+ }
+
+ return elements.groupBy { it.dri }.values.flatMap(::analyzeExpectActual)
+}
+
+private sealed class Expect<out T : Any> {
+ object NotFound : Expect<Nothing>()
+ data class Found<T : Any>(val expect: T) : Expect<T>()
+
+ companion object {
+ fun <T : Any> from(t: T?) = t?.let(::Found) ?: NotFound
+ }
+}
+
+fun Package.mergeWith(other: Package) : Package = copy(
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ packages = merge(packages + other.packages, Package::mergeWith),
+ documentation = documentation.mergeWith(other.documentation),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
+
+fun Function.mergeWith(other: Function) : Function = copy(
+ parameters = merge(this.parameters + other.parameters, Parameter::mergeWith),
+ receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver,
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct(),
+ generics = merge(generics + other.generics, TypeParameter::mergeWith)
+).mergeExtras(this, other)
+
+fun Property.mergeWith(other: Property): Property = copy(
+ receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver,
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct(),
+ getter = getter.mergeWith(other.getter),
+ setter = setter?.let { s -> other.setter?.let { s.mergeWith(it) } ?: s } ?: other.setter
+).mergeExtras(this, other)
+
+fun Classlike.setPlatformData(platformData: List<PlatformData>): Classlike = when(this) {
+ is Class -> copy(platformData = platformData)
+ is Enum -> copy(platformData = platformData)
+ is Interface -> copy(platformData = platformData)
+ is Object -> copy(platformData = platformData)
+ else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot have platform set")
+}
+
fun Classlike.mergeWith(other: Classlike): Classlike = when {
this is Class && other is Class -> mergeWith(other)
this is Enum && other is Enum -> mergeWith(other)
- else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be merged with ${other::class.qualifiedName} ${other.name}")
+ this is Interface && other is Interface -> mergeWith(other)
+ this is Object && other is Object -> mergeWith(other)
+ this is Annotation && other is Annotation -> mergeWith(other)
+ else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be mergesd with ${other::class.qualifiedName} ${other.name}")
}
-fun Class.mergeWith(other: Class) = Class(
- dri = dri,
- name = name,
- kind = kind,
- constructors = merge(constructors + other.constructors, Function::mergeWith),
- functions = merge(functions + other.functions, Function::mergeWith),
- properties = merge(properties + other.properties, Property::mergeWith),
- classlikes = merge(classlikes + other.classlikes, Classlike::mergeWith),
- expected = expected?.mergeWith(other.expected),
- actual = (actual + other.actual).mergeClassPlatformInfo(),
- visibility = (visibility + other.visibility)
-)
+fun Class.mergeWith(other: Class): Class = copy(
+ constructors = mergeExpectActual(constructors + other.constructors, Function::mergeWith) { copy(platformData = it) },
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion,
+ generics = merge(generics + other.generics, TypeParameter::mergeWith),
+ supertypes = supertypes.mergeWith(other.supertypes),
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
-fun Enum.mergeWith(other: Enum) = Enum(
- dri = dri,
- name = name,
- functions = merge(functions + other.functions, Function::mergeWith),
- properties = merge(properties + other.properties, Property::mergeWith),
- classlikes = merge(classlikes + other.classlikes, Classlike::mergeWith),
- expected = expected?.mergeWith(other.expected),
- actual = (actual + other.actual).mergeClassPlatformInfo(),
- entries = (this.entries + other.entries.distinctBy { it.dri }.toList()),
- constructors = merge(constructors + other.constructors, Function::mergeWith),
- visibility = visibility
-)
+fun Enum.mergeWith(other: Enum): Enum = copy(
+ entries = merge(entries + other.entries, EnumEntry::mergeWith),
+ constructors = mergeExpectActual(constructors + other.constructors, Function::mergeWith) { copy(platformData = it) },
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion,
+ supertypes = supertypes.mergeWith(other.supertypes),
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
-fun Parameter.mergeWith(other: Parameter) = Parameter(
- dri,
- name,
- type,
- expected?.mergeWith(other.expected),
- (actual + other.actual).merge()
-)
+fun EnumEntry.mergeWith(other: EnumEntry): EnumEntry = copy(
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ documentation = documentation.mergeWith(other.documentation),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
+
+fun Object.mergeWith(other: Object): Object = copy(
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ supertypes = supertypes.mergeWith(other.supertypes),
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
+
+fun Interface.mergeWith(other: Interface): Interface = copy(
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion,
+ generics = merge(generics + other.generics, TypeParameter::mergeWith),
+ supertypes = supertypes.mergeWith(other.supertypes),
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
+
+fun Annotation.mergeWith(other: Annotation): Annotation = copy(
+ constructors = mergeExpectActual(constructors + other.constructors, Function::mergeWith) { copy(platformData = it) },
+ functions = mergeExpectActual(functions + other.functions, Function::mergeWith) { copy(platformData = it) },
+ properties = mergeExpectActual(properties + other.properties, Property::mergeWith) { copy(platformData = it) },
+ classlikes = mergeExpectActual(classlikes + other.classlikes, Classlike::mergeWith, Classlike::setPlatformData),
+ companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion,
+ documentation = documentation.mergeWith(other.documentation),
+ actual = actual.mergeWith(other.actual),
+ visibility = visibility.mergeWith(other.visibility),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
+
+fun Parameter.mergeWith(other: Parameter): Parameter = copy(
+ documentation = documentation.mergeWith(other.documentation),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other)
-fun Package.mergeWith(other: Package): Package = Package(
- dri,
- merge(functions + other.functions, Function::mergeWith),
- merge(properties + other.properties, Property::mergeWith),
- merge(classlikes + other.classlikes, Classlike::mergeWith)
-) \ No newline at end of file
+fun TypeParameter.mergeWith(other: TypeParameter): TypeParameter = copy(
+ documentation = documentation.mergeWith(other.documentation),
+ platformData = (platformData + other.platformData).distinct()
+).mergeExtras(this, other) \ No newline at end of file