diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-02-25 11:40:02 +0100 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-02-27 10:51:51 +0100 |
commit | 1ff50691f11876101b53ba02ba6c344ab688d5df (patch) | |
tree | b2edc43d8fce72931569e963f8c9a9b81fce46b9 | |
parent | 9864b6188e202b023f5365ca8c875772db03a2cb (diff) | |
download | dokka-1ff50691f11876101b53ba02ba6c344ab688d5df.tar.gz dokka-1ff50691f11876101b53ba02ba6c344ab688d5df.tar.bz2 dokka-1ff50691f11876101b53ba02ba6c344ab688d5df.zip |
Crude Documentable merger
-rw-r--r-- | core/src/main/kotlin/model/Documentable.kt | 50 | ||||
-rw-r--r-- | plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt | 270 |
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 |