diff options
Diffstat (limited to 'plugins/base/src/main/kotlin/transformers')
30 files changed, 0 insertions, 2698 deletions
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt b/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt deleted file mode 100644 index dde1a2af..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer - -/** - * Since we can not merge [DClasslike] with [DTypeAlias.underlyingType] and [DTypeAlias.extra], - * we have this transformer to add [ActualTypealias] extra in expect [DClasslike] - * - * The transformer should be applied after merging all documentables - */ -// TODO assign actual [DTypeAlias.expectPresentInSet] an expect source set, currently, [DTypeAlias.expectPresentInSet] always = null -public class ActualTypealiasAdder : DocumentableTransformer { - - override fun invoke(original: DModule, context: DokkaContext): DModule { - return original.generateTypealiasesMap().let { aliases -> - original.copy(packages = original.packages.map { - it.copy(classlikes = addActualTypeAliasToClasslikes(it.classlikes, aliases)) - }) - } - } - - private fun DModule.generateTypealiasesMap(): Map<DRI, DTypeAlias> = - packages.flatMap { pkg -> - pkg.typealiases.map { typeAlias -> - typeAlias.dri to typeAlias - } - }.toMap() - - - private fun addActualTypeAliasToClasslikes( - elements: Iterable<DClasslike>, - typealiases: Map<DRI, DTypeAlias> - ): List<DClasslike> = elements.flatMap { - when (it) { - is DClass -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DEnum -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DInterface -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DObject -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DAnnotation -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - else -> throw IllegalStateException("${it::class.qualifiedName} ${it.name} cannot have extra added") - } - } - - private fun <T> addActualTypeAlias( - elements: Iterable<T>, - typealiases: Map<DRI, DTypeAlias> - ): List<T> where T : DClasslike, T : WithExtraProperties<T>, T : WithSources = - elements.map { element -> - if (element.expectPresentInSet != null) { - typealiases[element.dri]?.let { ta -> - val actualTypealiasExtra = ActualTypealias(ta.copy(expectPresentInSet = element.expectPresentInSet)) - val merged = element.withNewExtras(element.extra + actualTypealiasExtra).let { - when (it) { - is DClass -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DEnum -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DInterface -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DObject -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DAnnotation -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - else -> throw IllegalStateException("${it::class.qualifiedName} ${it.name} cannot have copy its sourceSets") - } - } - @Suppress("UNCHECKED_CAST") - merged as T - } ?: element - } else { - element - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt b/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt deleted file mode 100644 index e9c7342e..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -@Deprecated( - message = "Declaration was moved to dokka-core", - replaceWith = ReplaceWith("org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier"), - level = DeprecationLevel.WARNING // TODO change to error after Kotlin 1.9.20 -) -public typealias ClashingDriIdentifier = org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt deleted file mode 100644 index 4905e876..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.PackageOptions -import org.jetbrains.dokka.model.Annotations -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.EnumValue -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.perPackageOptions -import org.jetbrains.dokka.transformers.documentation.sourceSet - -/** - * If [PackageOptions.skipDeprecated] or [DokkaConfiguration.DokkaSourceSet.skipDeprecated] is set - * to `true`, suppresses documentables marked with [kotlin.Deprecated] or [java.lang.Deprecated]. - * Package options are given preference over global options. - * - * Documentables with [kotlin.Deprecated.level] set to [DeprecationLevel.HIDDEN] - * are suppressed regardless of global and package options. - */ -public class DeprecatedDocumentableFilterTransformer( - context: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(context) { - - override fun shouldBeSuppressed(d: Documentable): Boolean { - val annotations = (d as? WithExtraProperties<*>)?.annotations() ?: return false - if (annotations.isEmpty()) - return false - - val deprecatedAnnotations = filterDeprecatedAnnotations(annotations) - if (deprecatedAnnotations.isEmpty()) - return false - - val kotlinDeprecated = deprecatedAnnotations.find { it.dri.packageName == "kotlin" } - if (kotlinDeprecated?.isHidden() == true) - return true - - return perPackageOptions(d)?.skipDeprecated ?: sourceSet(d).skipDeprecated - } - - private fun WithExtraProperties<*>.annotations(): List<Annotations.Annotation> { - return this.extra.allOfType<Annotations>().flatMap { annotations -> - annotations.directAnnotations.values.singleOrNull() ?: emptyList() - } - } - - private fun filterDeprecatedAnnotations(annotations: List<Annotations.Annotation>): List<Annotations.Annotation> { - return annotations.filter { - (it.dri.packageName == "kotlin" && it.dri.classNames == "Deprecated") || - (it.dri.packageName == "java.lang" && it.dri.classNames == "Deprecated") - } - } - - private fun Annotations.Annotation.isHidden(): Boolean { - val level = (this.params["level"] as? EnumValue) ?: return false - return level.enumName == "DeprecationLevel.HIDDEN" - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt deleted file mode 100644 index 10b25a20..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public abstract class DocumentableReplacerTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> = - modules.map { module -> - val (documentable, wasChanged) = processModule(module) - documentable.takeIf { wasChanged } ?: module - } - - protected open fun processModule(module: DModule): AnyWithChanges<DModule> { - val afterProcessing = module.packages.map { processPackage(it) } - val processedModule = module.takeIf { afterProcessing.none { it.changed } } - ?: module.copy(packages = afterProcessing.mapNotNull { it.target }) - return AnyWithChanges(processedModule, afterProcessing.any { it.changed }) - } - - protected open fun processPackage(dPackage: DPackage): AnyWithChanges<DPackage> { - val classlikes = dPackage.classlikes.map { processClassLike(it) } - val typeAliases = dPackage.typealiases.map { processTypeAlias(it) } - val functions = dPackage.functions.map { processFunction(it) } - val properies = dPackage.properties.map { processProperty(it) } - - val wasChanged = (classlikes + typeAliases + functions + properies).any { it.changed } - return (dPackage.takeIf { !wasChanged } ?: dPackage.copy( - classlikes = classlikes.mapNotNull { it.target }, - typealiases = typeAliases.mapNotNull { it.target }, - functions = functions.mapNotNull { it.target }, - properties = properies.mapNotNull { it.target } - )).let { processedPackage -> AnyWithChanges(processedPackage, wasChanged) } - } - - protected open fun processClassLike(classlike: DClasslike): AnyWithChanges<DClasslike> { - val functions = classlike.functions.map { processFunction(it) } - val classlikes = classlike.classlikes.map { processClassLike(it) } - val properties = classlike.properties.map { processProperty(it) } - val companion = (classlike as? WithCompanion)?.companion?.let { processClassLike(it) } - - val wasClasslikeChanged = (functions + classlikes + properties).any { it.changed } || companion?.changed == true - return when (classlike) { - is DClass -> { - val constructors = classlike.constructors.map { processFunction(it) } - val generics = classlike.generics.map { processTypeParameter(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } || generics.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - constructors = constructors.mapNotNull { it.target }, - generics = generics.mapNotNull { it.target }, - companion = companion?.target as? DObject - )).let { AnyWithChanges(it, wasClassChange) } - } - is DInterface -> { - val generics = classlike.generics.map { processTypeParameter(it) } - val wasInterfaceChange = wasClasslikeChanged || generics.any { it.changed } - (classlike.takeIf { !wasInterfaceChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - generics = generics.mapNotNull { it.target }, - companion = companion?.target as? DObject - )).let { AnyWithChanges(it, wasClasslikeChanged) } - } - is DObject -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - )).let { AnyWithChanges(it, wasClasslikeChanged) } - is DAnnotation -> { - val constructors = classlike.constructors.map { processFunction(it) } - val generics = classlike.generics.map { processTypeParameter(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } || generics.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - constructors = constructors.mapNotNull { it.target }, - generics = generics.mapNotNull { it.target }, - companion = companion?.target as? DObject - )).let { AnyWithChanges(it, wasClassChange) } - } - is DEnum -> { - val constructors = classlike.constructors.map { processFunction(it) } - val entries = classlike.entries.map { processEnumEntry(it) } - val wasClassChange = - wasClasslikeChanged || (constructors + entries).any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - constructors = constructors.mapNotNull { it.target }, - companion = companion?.target as? DObject, - entries = entries.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasClassChange) } - } - } - } - - protected open fun processEnumEntry(dEnumEntry: DEnumEntry): AnyWithChanges<DEnumEntry> { - val functions = dEnumEntry.functions.map { processFunction(it) } - val properties = dEnumEntry.properties.map { processProperty(it) } - val classlikes = dEnumEntry.classlikes.map { processClassLike(it) } - - val wasChanged = (functions + properties + classlikes).any { it.changed } - return (dEnumEntry.takeIf { !wasChanged } ?: dEnumEntry.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processFunction(dFunction: DFunction): AnyWithChanges<DFunction> { - val type = processBound(dFunction.type) - val parameters = dFunction.parameters.map { processParameter(it) } - val receiver = dFunction.receiver?.let { processParameter(it) } - val generics = dFunction.generics.map { processTypeParameter(it) } - - val wasChanged = parameters.any { it.changed } || receiver?.changed == true - || type.changed || generics.any { it.changed } - return (dFunction.takeIf { !wasChanged } ?: dFunction.copy( - type = type.target ?: dFunction.type, - parameters = parameters.mapNotNull { it.target }, - receiver = receiver?.target, - generics = generics.mapNotNull { it.target }, - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processProperty(dProperty: DProperty): AnyWithChanges<DProperty> { - val getter = dProperty.getter?.let { processFunction(it) } - val setter = dProperty.setter?.let { processFunction(it) } - val type = processBound(dProperty.type) - val generics = dProperty.generics.map { processTypeParameter(it) } - - val wasChanged = getter?.changed == true || setter?.changed == true - || type.changed || generics.any { it.changed } - return (dProperty.takeIf { !wasChanged } ?: dProperty.copy( - type = type.target ?: dProperty.type, - setter = setter?.target, - getter = getter?.target, - generics = generics.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processParameter(dParameter: DParameter): AnyWithChanges<DParameter> { - val type = processBound(dParameter.type) - - val wasChanged = type.changed - return (dParameter.takeIf { !wasChanged } ?: dParameter.copy( - type = type.target ?: dParameter.type, - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processTypeParameter(dTypeParameter: DTypeParameter): AnyWithChanges<DTypeParameter> { - val bounds = dTypeParameter.bounds.map { processBound(it) } - - val wasChanged = bounds.any { it.changed } - return (dTypeParameter.takeIf { !wasChanged } ?: dTypeParameter.copy( - bounds = bounds.mapIndexed { i, v -> v.target ?: dTypeParameter.bounds[i] } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processBound(bound: Bound): AnyWithChanges<Bound> { - return when(bound) { - is GenericTypeConstructor -> processGenericTypeConstructor(bound) - is FunctionalTypeConstructor -> processFunctionalTypeConstructor(bound) - else -> AnyWithChanges(bound, false) - } - } - - protected open fun processVariance(variance: Variance<*>): AnyWithChanges<Variance<*>> { - val bound = processBound(variance.inner) - if (!bound.changed) - return AnyWithChanges(variance, false) - return when (variance) { - is Covariance<*> -> AnyWithChanges( - Covariance(bound.target ?: variance.inner), true) - is Contravariance<*> -> AnyWithChanges( - Contravariance(bound.target ?: variance.inner), true) - is Invariance<*> -> AnyWithChanges( - Invariance(bound.target ?: variance.inner), true) - else -> AnyWithChanges(variance, false) - } - } - - protected open fun processProjection(projection: Projection): AnyWithChanges<Projection> = - when (projection) { - is Bound -> processBound(projection) - is Variance<Bound> -> processVariance(projection) - else -> AnyWithChanges(projection, false) - } - - protected open fun processGenericTypeConstructor( - genericTypeConstructor: GenericTypeConstructor - ): AnyWithChanges<GenericTypeConstructor> { - val projections = genericTypeConstructor.projections.map { processProjection(it) } - - val wasChanged = projections.any { it.changed } - return (genericTypeConstructor.takeIf { !wasChanged } ?: genericTypeConstructor.copy( - projections = projections.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processFunctionalTypeConstructor( - functionalTypeConstructor: FunctionalTypeConstructor - ): AnyWithChanges<FunctionalTypeConstructor> { - val projections = functionalTypeConstructor.projections.map { processProjection(it) } - - val wasChanged = projections.any { it.changed } - return (functionalTypeConstructor.takeIf { !wasChanged } ?: functionalTypeConstructor.copy( - projections = projections.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processTypeAlias(dTypeAlias: DTypeAlias): AnyWithChanges<DTypeAlias> { - val underlyingType = dTypeAlias.underlyingType.mapValues { processBound(it.value) } - val generics = dTypeAlias.generics.map { processTypeParameter(it) } - - val wasChanged = underlyingType.any { it.value.changed } || generics.any { it.changed } - return (dTypeAlias.takeIf { !wasChanged } ?: dTypeAlias.copy( - underlyingType = underlyingType.mapValues { it.value.target ?: dTypeAlias.underlyingType.getValue(it.key) }, - generics = generics.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - - protected data class AnyWithChanges<out T>(val target: T?, val changed: Boolean = false) -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt deleted file mode 100644 index 6155a71f..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.DokkaDefaults -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public class DocumentableVisibilityFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.map { original -> - val sourceSet = original.sourceSets.single() - val packageOptions = sourceSet.perPackageOptions - DocumentableVisibilityFilter(packageOptions, sourceSet).processModule(original) - } - } - - private class DocumentableVisibilityFilter( - val packageOptions: List<DokkaConfiguration.PackageOptions>, - val globalOptions: DokkaSourceSet - ) { - fun Visibility.isAllowedInPackage(packageName: String?) = when (this) { - is JavaVisibility.Public, - is KotlinVisibility.Public -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PUBLIC) - is JavaVisibility.Private, - is KotlinVisibility.Private -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PRIVATE) - is JavaVisibility.Protected, - is KotlinVisibility.Protected -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PROTECTED) - is KotlinVisibility.Internal -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.INTERNAL) - is JavaVisibility.Default -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PACKAGE) - } - - private fun isAllowedInPackage(packageName: String?, visibility: DokkaConfiguration.Visibility): Boolean { - val packageOpts = packageName.takeIf { it != null }?.let { name -> - packageOptions.firstOrNull { Regex(it.matchingRegex).matches(name) } - } - - val (documentedVisibilities, includeNonPublic) = - @Suppress("DEPRECATION") // for includeNonPublic, preserve backwards compatibility - when { - packageOpts != null -> packageOpts.documentedVisibilities to packageOpts.includeNonPublic - else -> globalOptions.documentedVisibilities to globalOptions.includeNonPublic - } - - // if `documentedVisibilities` is explicitly overridden by the user (i.e. not default value by reference), - // deprecated `includeNonPublic` should not be taken into account, so that only one setting prevails - val isDocumentedVisibilitiesOverridden = documentedVisibilities !== DokkaDefaults.documentedVisibilities - return documentedVisibilities.contains(visibility) || (!isDocumentedVisibilitiesOverridden && includeNonPublic) - } - - fun processModule(original: DModule) = - filterPackages(original.packages).let { (modified, packages) -> - if (!modified) original - else - DModule( - original.name, - packages = packages, - documentation = original.documentation, - sourceSets = original.sourceSets, - extra = original.extra - ) - } - - - private fun filterPackages(packages: List<DPackage>): Pair<Boolean, List<DPackage>> { - var packagesListChanged = false - val filteredPackages = packages.map { - var modified = false - val functions = filterFunctions(it.functions).let { (listModified, list) -> - modified = modified || listModified - list - } - val properties = filterProperties(it.properties).let { (listModified, list) -> - modified = modified || listModified - list - } - val classlikes = filterClasslikes(it.classlikes).let { (listModified, list) -> - modified = modified || listModified - list - } - val typeAliases = filterTypeAliases(it.typealiases).let { (listModified, list) -> - modified = modified || listModified - list - } - when { - !modified -> it - else -> { - packagesListChanged = true - DPackage( - it.dri, - functions, - properties, - classlikes, - typeAliases, - it.documentation, - it.expectPresentInSet, - it.sourceSets, - it.extra - ) - } - } - } - return Pair(packagesListChanged, filteredPackages) - } - - @Suppress("UNUSED_PARAMETER") - private fun <T : WithVisibility> alwaysTrue(a: T, p: DokkaSourceSet) = true - @Suppress("UNUSED_PARAMETER") - private fun <T : WithVisibility> alwaysFalse(a: T, p: DokkaSourceSet) = false - @Suppress("UNUSED_PARAMETER") - private fun <T> alwaysNoModify(a: T, sourceSets: Set<DokkaSourceSet>) = false to a - - private fun WithVisibility.visibilityForPlatform(data: DokkaSourceSet): Visibility? = visibility[data] - - private fun <T> T.filterPlatforms( - additionalCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysTrue, - alternativeCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysFalse - ) where T : Documentable, T : WithVisibility = - sourceSets.filter { d -> - visibilityForPlatform(d)?.isAllowedInPackage(dri.packageName) == true && - additionalCondition(this, d) || - alternativeCondition(this, d) - }.toSet() - - private fun <T> List<T>.transform( - additionalCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysTrue, - alternativeCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysFalse, - modify: (T, Set<DokkaSourceSet>) -> Pair<Boolean, T> = ::alwaysNoModify, - recreate: (T, Set<DokkaSourceSet>) -> T, - ): Pair<Boolean, List<T>> where T : Documentable, T : WithVisibility { - var changed = false - val values = mapNotNull { t -> - val filteredPlatforms = t.filterPlatforms(additionalCondition, alternativeCondition) - when (filteredPlatforms.size) { - t.visibility.size -> { - val (wasChanged, element) = modify(t, filteredPlatforms) - changed = changed || wasChanged - element - } - 0 -> { - changed = true - null - } - else -> { - changed = true - recreate(t, filteredPlatforms) - } - } - } - return Pair(changed, values) - } - - private fun filterFunctions( - functions: List<DFunction>, - additionalCondition: (DFunction, DokkaSourceSet) -> Boolean = ::alwaysTrue - ) = - functions.transform(additionalCondition) { original, filteredPlatforms -> - with(original) { - copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - generics = generics.mapNotNull { it.filter(filteredPlatforms) }, - sourceSets = filteredPlatforms, - ) - } - } - - private fun hasVisibleAccessorsForPlatform(property: DProperty, data: DokkaSourceSet) = - property.getter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true || - property.setter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true - - private fun filterProperties( - properties: List<DProperty>, - additionalCondition: (DProperty, DokkaSourceSet) -> Boolean = ::alwaysTrue, - additionalConditionAccessors: (DFunction, DokkaSourceSet) -> Boolean = ::alwaysTrue - ): Pair<Boolean, List<DProperty>> { - - val modifier: (DProperty, Set<DokkaSourceSet>) -> Pair<Boolean, DProperty> = - { original, _ -> - val setter = original.setter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - val getter = original.getter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - - val modified = setter?.first == true || getter?.first == true - - val property = - if (modified) - original.copy( - setter = setter?.second?.firstOrNull(), - getter = getter?.second?.firstOrNull() - ) - else original - modified to property - } - - return properties.transform( - additionalCondition, - ::hasVisibleAccessorsForPlatform, - modifier - ) { original, filteredPlatforms -> - val setter = original.setter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - val getter = original.getter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - - with(original) { - copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - sourceSets = filteredPlatforms, - generics = generics.mapNotNull { it.filter(filteredPlatforms) }, - setter = setter?.second?.firstOrNull(), - getter = getter?.second?.firstOrNull() - ) - } - } - } - - private fun filterEnumEntries(entries: List<DEnumEntry>, filteredPlatforms: Set<DokkaSourceSet>): Pair<Boolean, List<DEnumEntry>> = - entries.fold(Pair(false, emptyList())) { acc, entry -> - val intersection = filteredPlatforms.intersect(entry.sourceSets) - if (intersection.isEmpty()) Pair(true, acc.second) - else { - val functions = filterFunctions(entry.functions) { _, data -> data in intersection } - val properties = filterProperties(entry.properties) { _, data -> data in intersection } - val classlikes = filterClasslikes(entry.classlikes) { _, data -> data in intersection } - - DEnumEntry( - entry.dri, - entry.name, - entry.documentation.filtered(intersection), - entry.expectPresentInSet.filtered(filteredPlatforms), - functions.second, - properties.second, - classlikes.second, - intersection, - entry.extra - ).let { Pair(functions.first || properties.first || classlikes.first, acc.second + it) } - } - } - - private fun filterClasslikes( - classlikeList: List<DClasslike>, - additionalCondition: (DClasslike, DokkaSourceSet) -> Boolean = ::alwaysTrue - ): Pair<Boolean, List<DClasslike>> { - var classlikesListChanged = false - val filteredClasslikes: List<DClasslike> = classlikeList.mapNotNull { - with(it) { - val filteredPlatforms = filterPlatforms(additionalCondition) - if (filteredPlatforms.isEmpty()) { - classlikesListChanged = true - null - } else { - var modified = sourceSets.size != filteredPlatforms.size - val functions = - filterFunctions(functions) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } - val properties = - filterProperties(properties) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } - val classlikes = - filterClasslikes(classlikes) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } - val companion = - if (this is WithCompanion) filterClasslikes(listOfNotNull(companion)) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list.firstOrNull() as DObject? - } else null - val constructors = if (this is WithConstructors) - filterFunctions(constructors) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } else emptyList() - val generics = - if (this is WithGenerics) generics.mapNotNull { param -> param.filter(filteredPlatforms) } else emptyList() - val enumEntries = - if (this is DEnum) filterEnumEntries(entries, filteredPlatforms).let { (listModified, list) -> - modified = modified || listModified - list - } else emptyList() - classlikesListChanged = classlikesListChanged || modified - when { - !modified -> this - this is DClass -> copy( - constructors = constructors, - functions = functions, - properties = properties, - classlikes = classlikes, - sources = sources.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - generics = generics, - supertypes = supertypes.filtered(filteredPlatforms), - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - this is DAnnotation -> copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - constructors = constructors, - generics = generics, - sourceSets = filteredPlatforms - ) - this is DEnum -> copy( - entries = enumEntries, - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - constructors = constructors, - supertypes = supertypes.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - this is DInterface -> copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - generics = generics, - supertypes = supertypes.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - this is DObject -> copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - supertypes = supertypes.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - else -> null - } - } - } - } - return Pair(classlikesListChanged, filteredClasslikes) - } - - private fun filterTypeAliases( - typeAliases: List<DTypeAlias>, - additionalCondition: (DTypeAlias, DokkaSourceSet) -> Boolean = ::alwaysTrue - ) = - typeAliases.transform(additionalCondition) { original, filteredPlatforms -> - with(original) { - copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - underlyingType = underlyingType.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - generics = generics.mapNotNull { it.filter(filteredPlatforms) }, - sourceSets = filteredPlatforms, - ) - } - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/EmptyModulesFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/EmptyModulesFilterTransformer.kt deleted file mode 100644 index 7a2387dc..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/EmptyModulesFilterTransformer.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public class EmptyModulesFilterTransformer : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.filter { it.children.isNotEmpty() } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt deleted file mode 100644 index 30ac8f70..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.dokka.transformers.documentation.sourceSet - -public class EmptyPackagesFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.mapNotNull(::filterModule) - } - - private fun filterModule(module: DModule): DModule? { - val nonEmptyPackages = module.packages.filterNot { pkg -> - sourceSet(pkg).skipEmptyPackages && pkg.children.isEmpty() - } - - return when { - nonEmptyPackages == module.packages -> module - nonEmptyPackages.isEmpty() -> null - else -> module.copy(packages = nonEmptyPackages) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt deleted file mode 100644 index e6102622..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.DriOfAny -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.properties.ExtraProperty -import org.jetbrains.dokka.model.properties.MergeStrategy -import org.jetbrains.dokka.model.properties.plus -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer -import org.jetbrains.dokka.utilities.parallelForEach -import org.jetbrains.dokka.utilities.parallelMap -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -public class ExtensionExtractorTransformer : DocumentableTransformer { - override fun invoke(original: DModule, context: DokkaContext): DModule = runBlocking(Dispatchers.Default) { - val classGraph = async { - if (!context.configuration.suppressInheritedMembers) - context.plugin<InternalKotlinAnalysisPlugin>().querySingle { fullClassHierarchyBuilder }.build(original) - else - emptyMap() - } - - val channel = Channel<Pair<DRI, Callable>>(10) - launch { - original.packages.parallelForEach { collectExtensions(it, channel) } - channel.close() - } - val extensionMap = channel.toList().toMultiMap() - - val newPackages = original.packages.parallelMap { it.addExtensionInformation(classGraph.await(), extensionMap) } - original.copy(packages = newPackages) - } - - private suspend fun <T : Documentable> T.addExtensionInformation( - classGraph: SourceSetDependent<Map<DRI, List<DRI>>>, - extensionMap: Map<DRI, List<Callable>> - ): T = coroutineScope { - val newClasslikes = (this@addExtensionInformation as? WithScope) - ?.classlikes - ?.map { async { it.addExtensionInformation(classGraph, extensionMap) } } - .orEmpty() - - @Suppress("UNCHECKED_CAST") - when (this@addExtensionInformation) { - is DPackage -> { - val newTypealiases = typealiases.map { async { it.addExtensionInformation(classGraph, extensionMap) } } - copy(classlikes = newClasslikes.awaitAll(), typealiases = newTypealiases.awaitAll()) - } - - is DClass -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DEnum -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DInterface -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DObject -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DAnnotation -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DTypeAlias -> copy(extra = extra + findExtensions(classGraph, extensionMap)) - else -> throw IllegalStateException( - "${this@addExtensionInformation::class.simpleName} is not expected to have extensions" - ) - } as T - } - - private suspend fun collectExtensions( - documentable: Documentable, - channel: SendChannel<Pair<DRI, Callable>> - ): Unit = coroutineScope { - if (documentable is WithScope) { - documentable.classlikes.forEach { - launch { collectExtensions(it, channel) } - } - - if (documentable is DObject || documentable is DPackage) { - (documentable.properties.asSequence() + documentable.functions.asSequence()) - .flatMap { it.asPairsWithReceiverDRIs() } - .forEach { channel.send(it) } - } - } - } - - private fun <T : Documentable> T.findExtensions( - classGraph: SourceSetDependent<Map<DRI, List<DRI>>>, - extensionMap: Map<DRI, List<Callable>> - ): CallableExtensions? { - val resultSet = mutableSetOf<Callable>() - - fun collectFrom(element: DRI) { - extensionMap[element]?.let { resultSet.addAll(it) } - sourceSets.forEach { sourceSet -> classGraph[sourceSet]?.get(element)?.forEach { collectFrom(it) } } - } - collectFrom(dri) - - return if (resultSet.isEmpty()) null else CallableExtensions(resultSet) - } - - private fun Callable.asPairsWithReceiverDRIs(): Sequence<Pair<DRI, Callable>> = - receiver?.type?.let { findReceiverDRIs(it) }.orEmpty().map { it to this } - - // In normal cases we return at max one DRI, but sometimes receiver type can be bound by more than one type constructor - // for example `fun <T> T.example() where T: A, T: B` is extension of both types A and B - // another one `typealias A = B` - // Note: in some cases returning empty sequence doesn't mean that we cannot determine the DRI but only that we don't - // care about it since there is nowhere to put documentation of given extension. - private fun Callable.findReceiverDRIs(bound: Bound): Sequence<DRI> = when (bound) { - is Nullable -> findReceiverDRIs(bound.inner) - is DefinitelyNonNullable -> findReceiverDRIs(bound.inner) - is TypeParameter -> - if (this is DFunction && bound.dri == this.dri) - generics.find { it.name == bound.name }?.bounds?.asSequence()?.flatMap { findReceiverDRIs(it) }.orEmpty() - else - emptySequence() - - is TypeConstructor -> sequenceOf(bound.dri) - is PrimitiveJavaType -> emptySequence() - is Void -> emptySequence() - is JavaObject -> sequenceOf(DriOfAny) - is Dynamic -> sequenceOf(DriOfAny) - is UnresolvedBound -> emptySequence() - is TypeAliased -> findReceiverDRIs(bound.typeAlias) + findReceiverDRIs(bound.inner) - } - - private fun <T, U> Iterable<Pair<T, U>>.toMultiMap(): Map<T, List<U>> = - groupBy(Pair<T, *>::first, Pair<*, U>::second) -} - -public data class CallableExtensions(val extensions: Set<Callable>) : ExtraProperty<Documentable> { - public companion object Key : ExtraProperty.Key<Documentable, CallableExtensions> { - override fun mergeStrategyFor(left: CallableExtensions, right: CallableExtensions): MergeStrategy<Documentable> = - MergeStrategy.Replace(CallableExtensions(left.extensions + right.extensions)) - } - - override val key: Key = Key -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt deleted file mode 100644 index d9b7053a..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.InheritedMember -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.plugability.DokkaContext - -public class InheritedEntriesDocumentableFilterTransformer( - context: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(context) { - - override fun shouldBeSuppressed(d: Documentable): Boolean { - @Suppress("UNCHECKED_CAST") - val inheritedMember = (d as? WithExtraProperties<Documentable>)?.extra?.get(InheritedMember) - val containsInheritedFrom = inheritedMember?.inheritedFrom?.any { entry -> entry.value != null } ?: false - - return context.configuration.suppressInheritedMembers && containsInheritedFrom - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt deleted file mode 100644 index 2c7d6b89..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.properties.ExtraProperty -import org.jetbrains.dokka.model.properties.MergeStrategy -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer - -public class InheritorsExtractorTransformer : DocumentableTransformer { - override fun invoke(original: DModule, context: DokkaContext): DModule = - original.generateInheritanceMap().let { inheritanceMap -> original.appendInheritors(inheritanceMap) as DModule } - - private fun <T : Documentable> T.appendInheritors(inheritanceMap: Map<DokkaSourceSet, Map<DRI, List<DRI>>>): Documentable = - InheritorsInfo(inheritanceMap.getForDRI(dri)).let { info -> - when (this) { - is DModule -> copy(packages = packages.map { it.appendInheritors(inheritanceMap) as DPackage }) - is DPackage -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - is DClass -> if (info.isNotEmpty()) { - copy( - extra = extra + info, - classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } else { - copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } - is DEnum -> if (info.isNotEmpty()) { - copy( - extra = extra + info, - classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } else { - copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } - is DInterface -> if (info.isNotEmpty()) { - copy( - extra = extra + info, - classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } else { - copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } - is DObject -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - is DAnnotation -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - else -> this - } - } - - private fun InheritorsInfo.isNotEmpty() = this.value.values.fold(0) { acc, list -> acc + list.size } > 0 - - private fun Map<DokkaSourceSet, Map<DRI, List<DRI>>>.getForDRI(dri: DRI) = - map { (v, k) -> - v to k[dri] - }.associate { (k, v) -> k to v.orEmpty() } - - private fun DModule.generateInheritanceMap() = - getInheritanceEntriesRec().filterNot { it.second.isEmpty() }.groupBy({ it.first }) { it.second } - .map { (k, v) -> - k to v.flatMap { p -> p.groupBy({ it.first }) { it.second }.toList() } - .groupBy({ it.first }) { it.second }.map { (k2, v2) -> k2 to v2.flatten() }.toMap() - }.filter { it.second.values.isNotEmpty() }.toMap() - - private fun <T : Documentable> T.getInheritanceEntriesRec(): List<Pair<DokkaSourceSet, List<Pair<DRI, DRI>>>> = - this.toInheritanceEntries() + children.flatMap { it.getInheritanceEntriesRec() } - - private fun <T : Documentable> T.toInheritanceEntries() = - (this as? WithSupertypes)?.let { - it.supertypes.map { (k, v) -> k to v.map { it.typeConstructor.dri to dri } } - }.orEmpty() - -} - -public class InheritorsInfo( - public val value: SourceSetDependent<List<DRI>> -) : ExtraProperty<Documentable> { - public companion object : ExtraProperty.Key<Documentable, InheritorsInfo> { - override fun mergeStrategyFor(left: InheritorsInfo, right: InheritorsInfo): MergeStrategy<Documentable> = - MergeStrategy.Replace( - InheritorsInfo( - (left.value.entries.toList() + right.value.entries.toList()) - .groupBy({ it.key }) { it.value } - .map { (k, v) -> k to v.flatten() }.toMap() - ) - ) - } - - override val key: ExtraProperty.Key<Documentable, *> = InheritorsInfo -} - diff --git a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt deleted file mode 100644 index 7a360cb8..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext - -public class KotlinArrayDocumentableReplacerTransformer( - context: DokkaContext -): DocumentableReplacerTransformer(context) { - - private fun Documentable.isJVM() = - sourceSets.any{ it.analysisPlatform == Platform.jvm } - - override fun processGenericTypeConstructor(genericTypeConstructor: GenericTypeConstructor): AnyWithChanges<GenericTypeConstructor> = - genericTypeConstructor.takeIf { genericTypeConstructor.dri == DRI("kotlin", "Array") } - ?.let { - with(it.projections.firstOrNull() as? Variance<Bound>) { - with(this?.inner as? GenericTypeConstructor) { - when (this?.dri) { - DRI("kotlin", "Int") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), - true) - DRI("kotlin", "Boolean") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), - true) - DRI("kotlin", "Float") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "FloatArray"), emptyList()), - true) - DRI("kotlin", "Double") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "DoubleArray"), emptyList()), - true) - DRI("kotlin", "Long") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "LongArray"), emptyList()), - true) - DRI("kotlin", "Short") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "ShortArray"), emptyList()), - true) - DRI("kotlin", "Char") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "CharArray"), emptyList()), - true) - DRI("kotlin", "Byte") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "ByteArray"), emptyList()), - true) - else -> null - } - } - } - } - ?: super.processGenericTypeConstructor(genericTypeConstructor) - - override fun processModule(module: DModule): AnyWithChanges<DModule> = - if(module.isJVM()) - super.processModule(module) - else AnyWithChanges(module) -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt deleted file mode 100644 index c19bc15e..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.model.SourceSetDependent -import org.jetbrains.dokka.model.doc.DocumentationNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin -import org.jetbrains.dokka.analysis.kotlin.internal.ModuleAndPackageDocumentationReader - -internal class ModuleAndPackageDocumentationTransformer( - private val moduleAndPackageDocumentationReader: ModuleAndPackageDocumentationReader -) : PreMergeDocumentableTransformer { - - constructor(context: DokkaContext) : this( - context.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } - ) - - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.map { module -> - module.copy( - documentation = module.documentation + moduleAndPackageDocumentationReader.read(module), - packages = module.packages.map { pkg -> - pkg.copy( - documentation = pkg.documentation + moduleAndPackageDocumentationReader.read(pkg) - ) - } - ) - } - } - - private operator fun SourceSetDependent<DocumentationNode>.plus( - other: SourceSetDependent<DocumentationNode> - ): Map<DokkaSourceSet, DocumentationNode> = - (asSequence() + other.asSequence()) - .distinct() - .groupBy({ it.key }, { it.value }) - .mapValues { (_, values) -> DocumentationNode(values.flatMap { it.children }) } - -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer.kt deleted file mode 100644 index 09c6ac87..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.DFunction -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.ObviousMember -import org.jetbrains.dokka.plugability.DokkaContext - -public class ObviousFunctionsDocumentableFilterTransformer( - context: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(context) { - override fun shouldBeSuppressed(d: Documentable): Boolean = - context.configuration.suppressObviousFunctions && d is DFunction && d.extra[ObviousMember] != null -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt deleted file mode 100644 index 2b270f18..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -internal class ReportUndocumentedTransformer : DocumentableTransformer { - - override fun invoke(original: DModule, context: DokkaContext): DModule = original.apply { - withDescendants().forEach { documentable -> invoke(documentable, context) } - } - - private fun invoke(documentable: Documentable, context: DokkaContext) { - documentable.sourceSets.forEach { sourceSet -> - if (shouldBeReportedIfNotDocumented(documentable, sourceSet, context)) { - reportIfUndocumented(context, documentable, sourceSet) - } - } - } - - private fun shouldBeReportedIfNotDocumented( - documentable: Documentable, sourceSet: DokkaSourceSet, context: DokkaContext - ): Boolean { - val packageOptionsOrNull = packageOptionsOrNull(sourceSet, documentable) - - if (!(packageOptionsOrNull?.reportUndocumented ?: sourceSet.reportUndocumented)) { - return false - } - - if (documentable is DParameter || documentable is DPackage || documentable is DModule) { - return false - } - - if (isConstructor(documentable)) { - return false - } - - val syntheticDetector = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { syntheticDocumentableDetector } - if (syntheticDetector.isSynthetic(documentable, sourceSet)) { - return false - } - - if (isPrivateOrInternalApi(documentable, sourceSet)) { - return false - } - - return true - } - - private fun reportIfUndocumented( - context: DokkaContext, - documentable: Documentable, - sourceSet: DokkaSourceSet - ) { - if (isUndocumented(documentable, sourceSet)) { - val documentableDescription = with(documentable) { - buildString { - dri.packageName?.run { - append(this) - append("/") - } - - dri.classNames?.run { - append(this) - append("/") - } - - dri.callable?.run { - append(name) - append("/") - append(signature()) - append("/") - } - - val sourceSetName = sourceSet.displayName - if (sourceSetName != null.toString()) { - append(" ($sourceSetName)") - } - } - } - - context.logger.warn("Undocumented: $documentableDescription") - } - } - - private fun isUndocumented(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - fun resolveDependentSourceSets(sourceSet: DokkaSourceSet): List<DokkaSourceSet> { - return sourceSet.dependentSourceSets.mapNotNull { sourceSetID -> - documentable.sourceSets.singleOrNull { it.sourceSetID == sourceSetID } - } - } - - fun withAllDependentSourceSets(sourceSet: DokkaSourceSet): Sequence<DokkaSourceSet> = sequence { - yield(sourceSet) - for (dependentSourceSet in resolveDependentSourceSets(sourceSet)) { - yieldAll(withAllDependentSourceSets(dependentSourceSet)) - } - } - - - return withAllDependentSourceSets(sourceSet).all { sourceSetOrDependentSourceSet -> - documentable.documentation[sourceSetOrDependentSourceSet]?.children?.isEmpty() ?: true - } - } - - private fun isConstructor(documentable: Documentable): Boolean { - if (documentable !is DFunction) return false - return documentable.isConstructor - } - - private fun isPrivateOrInternalApi(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - return when ((documentable as? WithVisibility)?.visibility?.get(sourceSet)) { - KotlinVisibility.Public -> false - KotlinVisibility.Private -> true - KotlinVisibility.Protected -> true - KotlinVisibility.Internal -> true - JavaVisibility.Public -> false - JavaVisibility.Private -> true - JavaVisibility.Protected -> true - JavaVisibility.Default -> true - null -> false - } - } - - private fun packageOptionsOrNull( - dokkaSourceSet: DokkaSourceSet, - documentable: Documentable - ): DokkaConfiguration.PackageOptions? { - val packageName = documentable.dri.packageName ?: return null - return dokkaSourceSet.perPackageOptions - .filter { packageOptions -> Regex(packageOptions.matchingRegex).matches(packageName) } - .maxByOrNull { packageOptions -> packageOptions.matchingRegex.length } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt deleted file mode 100644 index 1dbf1262..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.dfs -import org.jetbrains.dokka.model.doc.Suppress -import org.jetbrains.dokka.plugability.DokkaContext - -public class SuppressTagDocumentableFilter( - public val dokkaContext: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(dokkaContext) { - override fun shouldBeSuppressed(d: Documentable): Boolean = - d.documentation.any { (_, docs) -> docs.dfs { it is Suppress } != null } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer.kt deleted file mode 100644 index 4631cece..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public abstract class SuppressedByConditionDocumentableFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> = - modules.map { module -> - val (documentable, wasChanged) = processModule(module) - documentable.takeIf { wasChanged } ?: module - } - - public abstract fun shouldBeSuppressed(d: Documentable): Boolean - - private fun processModule(module: DModule): DocumentableWithChanges<DModule> { - val afterProcessing = module.packages.map { processPackage(it) } - val processedModule = module.takeIf { afterProcessing.none { it.changed } } - ?: module.copy(packages = afterProcessing.mapNotNull { it.documentable }) - return DocumentableWithChanges(processedModule, afterProcessing.any { it.changed }) - } - - private fun processPackage(dPackage: DPackage): DocumentableWithChanges<DPackage> { - if (shouldBeSuppressed(dPackage)) return DocumentableWithChanges.filteredDocumentable() - - val classlikes = dPackage.classlikes.map { processClassLike(it) } - val typeAliases = dPackage.typealiases.map { processMember(it) } - val functions = dPackage.functions.map { processMember(it) } - val properies = dPackage.properties.map { processProperty(it) } - - val wasChanged = (classlikes + typeAliases + functions + properies).any { it.changed } - return (dPackage.takeIf { !wasChanged } ?: dPackage.copy( - classlikes = classlikes.mapNotNull { it.documentable }, - typealiases = typeAliases.mapNotNull { it.documentable }, - functions = functions.mapNotNull { it.documentable }, - properties = properies.mapNotNull { it.documentable } - )).let { processedPackage -> DocumentableWithChanges(processedPackage, wasChanged) } - } - - private fun processClassLike(classlike: DClasslike): DocumentableWithChanges<DClasslike> { - if (shouldBeSuppressed(classlike)) return DocumentableWithChanges.filteredDocumentable() - - val functions = classlike.functions.map { processMember(it) } - val classlikes = classlike.classlikes.map { processClassLike(it) } - val properties = classlike.properties.map { processProperty(it) } - val companion = (classlike as? WithCompanion)?.companion?.let { processClassLike(it) } - - val wasClasslikeChanged = (functions + classlikes + properties).any { it.changed } || companion?.changed == true - return when (classlike) { - is DClass -> { - val constructors = classlike.constructors.map { processMember(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - constructors = constructors.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject - )).let { DocumentableWithChanges(it, wasClassChange) } - } - is DInterface -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject - )).let { DocumentableWithChanges(it, wasClasslikeChanged) } - is DObject -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - )).let { DocumentableWithChanges(it, wasClasslikeChanged) } - is DAnnotation -> { - val constructors = classlike.constructors.map { processMember(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - constructors = constructors.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject - )).let { DocumentableWithChanges(it, wasClassChange) } - } - is DEnum -> { - val constructors = classlike.constructors.map { processMember(it) } - val entries = classlike.entries.map { processEnumEntry(it) } - val wasClassChange = - wasClasslikeChanged || (constructors + entries).any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - constructors = constructors.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject, - entries = entries.mapNotNull { it.documentable } - )).let { DocumentableWithChanges(it, wasClassChange) } - } - } - } - - private fun processEnumEntry(dEnumEntry: DEnumEntry): DocumentableWithChanges<DEnumEntry> { - if (shouldBeSuppressed(dEnumEntry)) return DocumentableWithChanges.filteredDocumentable() - - val functions = dEnumEntry.functions.map { processMember(it) } - val properties = dEnumEntry.properties.map { processProperty(it) } - val classlikes = dEnumEntry.classlikes.map { processClassLike(it) } - - val wasChanged = (functions + properties + classlikes).any { it.changed } - return (dEnumEntry.takeIf { !wasChanged } ?: dEnumEntry.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - )).let { DocumentableWithChanges(it, wasChanged) } - } - - private fun processProperty(dProperty: DProperty): DocumentableWithChanges<DProperty> { - if (shouldBeSuppressed(dProperty)) return DocumentableWithChanges.filteredDocumentable() - - val getter = dProperty.getter?.let { processMember(it) } ?: DocumentableWithChanges(null, false) - val setter = dProperty.setter?.let { processMember(it) } ?: DocumentableWithChanges(null, false) - - val wasChanged = getter.changed || setter.changed - return (dProperty.takeIf { !wasChanged } ?: dProperty.copy( - getter = getter.documentable, - setter = setter.documentable - )).let { DocumentableWithChanges(it, wasChanged) } - } - - private fun <T : Documentable> processMember(member: T): DocumentableWithChanges<T> = - if (shouldBeSuppressed(member)) DocumentableWithChanges.filteredDocumentable() - else DocumentableWithChanges(member, false) - - private data class DocumentableWithChanges<T : Documentable>(val documentable: T?, val changed: Boolean = false) { - companion object { - fun <T : Documentable> filteredDocumentable(): DocumentableWithChanges<T> = - DocumentableWithChanges(null, true) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt deleted file mode 100644 index 3195f88d..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.dokka.transformers.documentation.perPackageOptions -import org.jetbrains.dokka.transformers.documentation.source -import org.jetbrains.dokka.transformers.documentation.sourceSet -import java.io.File - -public class SuppressedByConfigurationDocumentableFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.mapNotNull(::filterModule) - } - - private fun filterModule(module: DModule): DModule? { - val packages = module.packages.mapNotNull { pkg -> filterPackage(pkg) } - return when { - packages == module.packages -> module - packages.isEmpty() -> null - else -> module.copy(packages = packages) - } - } - - private fun filterPackage(pkg: DPackage): DPackage? { - val options = perPackageOptions(pkg) - if (options?.suppress == true) { - return null - } - - val filteredChildren = pkg.children.filterNot(::isSuppressed) - return when { - filteredChildren == pkg.children -> pkg - filteredChildren.isEmpty() -> null - else -> pkg.copy( - functions = filteredChildren.filterIsInstance<DFunction>(), - classlikes = filteredChildren.filterIsInstance<DClasslike>(), - typealiases = filteredChildren.filterIsInstance<DTypeAlias>(), - properties = filteredChildren.filterIsInstance<DProperty>() - ) - } - } - - private fun isSuppressed(documentable: Documentable): Boolean { - if (documentable !is WithSources) return false - val sourceFile = File(source(documentable).path).absoluteFile - return sourceSet(documentable).suppressedFiles.any { suppressedFile -> - sourceFile.startsWith(suppressedFile.absoluteFile) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt b/plugins/base/src/main/kotlin/transformers/documentables/utils.kt deleted file mode 100644 index 60a6396a..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.Annotations -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.ExceptionInSupertypes -import org.jetbrains.dokka.model.properties.WithExtraProperties - -public val <T : Documentable> WithExtraProperties<T>.isException: Boolean - get() = extra[ExceptionInSupertypes] != null - - -public val <T : Documentable> WithExtraProperties<T>.deprecatedAnnotation: Annotations.Annotation? - get() = extra[Annotations]?.let { annotations -> - annotations.directAnnotations.values.flatten().firstOrNull { - it.isDeprecated() - } - } - -/** - * @return true if [T] has [kotlin.Deprecated] or [java.lang.Deprecated] - * annotation for **any** source set - */ -public fun <T : Documentable> WithExtraProperties<T>.isDeprecated(): Boolean = deprecatedAnnotation != null - -/** - * @return true for [kotlin.Deprecated] and [java.lang.Deprecated] - */ -public fun Annotations.Annotation.isDeprecated(): Boolean { - return (this.dri.packageName == "kotlin" && this.dri.classNames == "Deprecated") || - (this.dri.packageName == "java.lang" && this.dri.classNames == "Deprecated") -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt deleted file mode 100644 index 1ba049c8..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages - -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.doc.Sample -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.pages.PageTransformer -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin -import org.jetbrains.dokka.analysis.kotlin.internal.SampleProvider -import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory - -internal const val KOTLIN_PLAYGROUND_SCRIPT = "https://unpkg.com/kotlin-playground@1/dist/playground.min.js" - -internal class DefaultSamplesTransformer(val context: DokkaContext) : PageTransformer { - - private val sampleProviderFactory: SampleProviderFactory = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { sampleProviderFactory } - - override fun invoke(input: RootPageNode): RootPageNode { - return sampleProviderFactory.build().use { sampleProvider -> - input.transformContentPagesTree { page -> - val samples = (page as? WithDocumentables)?.documentables?.flatMap { - it.documentation.entries.flatMap { entry -> - entry.value.children.filterIsInstance<Sample>().map { entry.key to it } - } - } ?: return@transformContentPagesTree page - - val newContent = samples.fold(page.content) { acc, (sampleSourceSet, sample) -> - sampleProvider.getSample(sampleSourceSet, sample.name) - ?.let { - acc.addSample(page, sample.name, it) - } ?: acc - } - - page.modified( - content = newContent, - embeddedResources = page.embeddedResources + KOTLIN_PLAYGROUND_SCRIPT - ) - } - } - } - - - private fun ContentNode.addSample( - contentPage: ContentPage, - fqLink: String, - sample: SampleProvider.SampleSnippet, - ): ContentNode { - val node = contentCode(contentPage.content.sourceSets, contentPage.dri, createSampleBody(sample.imports, sample.body), "kotlin") - return dfs(fqLink, node) - } - - fun createSampleBody(imports: String, body: String) = - """ |$imports - |fun main() { - | //sampleStart - | $body - | //sampleEnd - |}""".trimMargin() - - private fun ContentNode.dfs(fqName: String, node: ContentCodeBlock): ContentNode { - return when (this) { - is ContentHeader -> copy(children.map { it.dfs(fqName, node) }) - is ContentDivergentGroup -> @Suppress("UNCHECKED_CAST") copy(children.map { - it.dfs(fqName, node) - } as List<ContentDivergentInstance>) - is ContentDivergentInstance -> copy( - before.let { it?.dfs(fqName, node) }, - divergent.dfs(fqName, node), - after.let { it?.dfs(fqName, node) }) - is ContentCodeBlock -> copy(children.map { it.dfs(fqName, node) }) - is ContentCodeInline -> copy(children.map { it.dfs(fqName, node) }) - is ContentDRILink -> copy(children.map { it.dfs(fqName, node) }) - is ContentResolvedLink -> copy(children.map { it.dfs(fqName, node) }) - is ContentEmbeddedResource -> copy(children.map { it.dfs(fqName, node) }) - is ContentTable -> copy(children = children.map { it.dfs(fqName, node) as ContentGroup }) - is ContentList -> copy(children.map { it.dfs(fqName, node) }) - is ContentGroup -> copy(children.map { it.dfs(fqName, node) }) - is PlatformHintedContent -> copy(inner.dfs(fqName, node)) - is ContentText -> if (text == fqName) node else this - is ContentBreakLine -> this - else -> this.also { context.logger.error("Could not recognize $this ContentNode in SamplesTransformer") } - } - } - - private fun contentCode( - sourceSets: Set<DisplaySourceSet>, - dri: Set<DRI>, - content: String, - language: String, - styles: Set<Style> = emptySet(), - extra: PropertyContainer<ContentNode> = PropertyContainer.empty() - ) = - ContentCodeBlock( - children = listOf( - ContentText( - text = content, - dci = DCI(dri, ContentKind.Sample), - sourceSets = sourceSets, - style = emptySet(), - extra = PropertyContainer.empty() - ) - ), - language = language, - dci = DCI(dri, ContentKind.Sample), - sourceSets = sourceSets, - style = styles + ContentStyle.RunnableSample + TextStyle.Monospace, - extra = extra - ) -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt deleted file mode 100644 index 9ff5960d..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.annotations - - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME -import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.doc.CustomDocTag -import org.jetbrains.dokka.model.doc.CustomTagWrapper -import org.jetbrains.dokka.model.doc.DocumentationNode -import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer -import org.jetbrains.dokka.utilities.associateWithNotNull - -public class SinceKotlinVersion(str: String) : Comparable<SinceKotlinVersion> { - private val parts: List<Int> = str.split(".").map { it.toInt() } - - /** - * Corner case: 1.0 == 1.0.0 - */ - override fun compareTo(other: SinceKotlinVersion): Int { - val i1 = parts.listIterator() - val i2 = other.parts.listIterator() - - while (i1.hasNext() || i2.hasNext()) { - val diff = (if (i1.hasNext()) i1.next() else 0) - (if (i2.hasNext()) i2.next() else 0) - if (diff != 0) return diff - } - - return 0 - } - - override fun toString(): String = parts.joinToString(".") -} - -public class SinceKotlinTransformer( - public val context: DokkaContext -) : DocumentableTransformer { - - private val minSinceKotlinVersionOfPlatform = mapOf( - Platform.common to SinceKotlinVersion("1.0"), - Platform.jvm to SinceKotlinVersion("1.0"), - Platform.js to SinceKotlinVersion("1.1"), - Platform.native to SinceKotlinVersion("1.3"), - Platform.wasm to SinceKotlinVersion("1.8"), - ) - - override fun invoke(original: DModule, context: DokkaContext): DModule = original.transform() as DModule - - private fun <T : Documentable> T.transform(parent: SourceSetDependent<SinceKotlinVersion>? = null): Documentable { - val versions = calculateVersions(parent) - return when (this) { - is DModule -> copy( - packages = packages.map { it.transform() as DPackage } - ) - - is DPackage -> copy( - classlikes = classlikes.map { it.transform() as DClasslike }, - functions = functions.map { it.transform() as DFunction }, - properties = properties.map { it.transform() as DProperty }, - typealiases = typealiases.map { it.transform() as DTypeAlias } - ) - - is DClass -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DEnum -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DInterface -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DObject -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DTypeAlias -> copy( - documentation = appendSinceKotlin(versions) - ) - - is DAnnotation -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DFunction -> copy( - documentation = appendSinceKotlin(versions) - ) - - is DProperty -> copy( - documentation = appendSinceKotlin(versions) - ) - - is DParameter -> copy( - documentation = appendSinceKotlin(versions) - ) - - else -> this.also { context.logger.warn("Unrecognized documentable $this while SinceKotlin transformation") } - } - } - - private fun List<Annotations.Annotation>.findSinceKotlinAnnotation(): Annotations.Annotation? = - this.find { it.dri.packageName == "kotlin" && it.dri.classNames == "SinceKotlin" } - - private fun Documentable.getVersion(sourceSet: DokkaConfiguration.DokkaSourceSet): SinceKotlinVersion { - val annotatedVersion = - annotations()[sourceSet] - ?.findSinceKotlinAnnotation() - ?.params?.let { it["version"] as? StringValue }?.value - ?.let { SinceKotlinVersion(it) } - - val minSinceKotlin = minSinceKotlinVersionOfPlatform[sourceSet.analysisPlatform] - ?: throw IllegalStateException("No value for platform: ${sourceSet.analysisPlatform}") - - return annotatedVersion?.takeIf { version -> version >= minSinceKotlin } ?: minSinceKotlin - } - - - private fun Documentable.calculateVersions(parent: SourceSetDependent<SinceKotlinVersion>?): SourceSetDependent<SinceKotlinVersion> { - return sourceSets.associateWithNotNull { sourceSet -> - val version = getVersion(sourceSet) - val parentVersion = parent?.get(sourceSet) - if (parentVersion != null) - maxOf(version, parentVersion) - else - version - } - } - - private fun Documentable.appendSinceKotlin(versions: SourceSetDependent<SinceKotlinVersion>) = - sourceSets.fold(documentation) { acc, sourceSet -> - - val version = versions[sourceSet] - - val sinceKotlinCustomTag = CustomTagWrapper( - CustomDocTag( - listOf( - Text( - version.toString() - ) - ), - name = MARKDOWN_ELEMENT_FILE_NAME - ), - "Since Kotlin" - ) - if (acc[sourceSet] == null) - acc + (sourceSet to DocumentationNode(listOf(sinceKotlinCustomTag))) - else - acc.mapValues { - if (it.key == sourceSet) it.value.copy( - it.value.children + listOf( - sinceKotlinCustomTag - ) - ) else it.value - } - } - - internal companion object { - internal const val SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP = "dokka.shouldDisplaySinceKotlin" - internal fun shouldDisplaySinceKotlin() = - System.getProperty(SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP) in listOf("true", "1") - } -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt deleted file mode 100644 index 6ca3f8d0..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.comments - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.model.doc.DocTag -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.pages.ContentNode -import org.jetbrains.dokka.pages.DCI -import org.jetbrains.dokka.pages.Style - -public interface CommentsToContentConverter { - public fun buildContent( - docTag: DocTag, - dci: DCI, - sourceSets: Set<DokkaSourceSet>, - styles: Set<Style> = emptySet(), - extras: PropertyContainer<ContentNode> = PropertyContainer.empty() - ): List<ContentNode> -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt deleted file mode 100644 index e4e0f53f..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.comments - - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.model.properties.plus -import org.jetbrains.dokka.model.toDisplaySourceSets -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.utilities.firstIsInstanceOrNull - -public open class DocTagToContentConverter : CommentsToContentConverter { - override fun buildContent( - docTag: DocTag, - dci: DCI, - sourceSets: Set<DokkaSourceSet>, - styles: Set<Style>, - extras: PropertyContainer<ContentNode> - ): List<ContentNode> { - - fun buildChildren(docTag: DocTag, newStyles: Set<Style> = emptySet(), newExtras: SimpleAttr? = null) = - docTag.children.flatMap { - buildContent(it, dci, sourceSets, styles + newStyles, newExtras?.let { extras + it } ?: extras) - } - - fun buildTableRows(rows: List<DocTag>, newStyle: Style): List<ContentGroup> = - rows.flatMap { - @Suppress("UNCHECKED_CAST") - buildContent(it, dci, sourceSets, styles + newStyle, extras) as List<ContentGroup> - } - - fun buildHeader(level: Int) = - listOf( - ContentHeader( - buildChildren(docTag), - level, - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - - fun buildList(ordered: Boolean, newStyles: Set<Style> = emptySet(), start: Int = 1) = - listOf( - ContentList( - buildChildren(docTag), - ordered, - dci, - sourceSets.toDisplaySourceSets(), - styles + newStyles, - ((PropertyContainer.empty<ContentNode>()) + SimpleAttr("start", start.toString())) - ) - ) - - fun buildNewLine() = listOf( - ContentBreakLine( - sourceSets.toDisplaySourceSets() - ) - ) - - fun P.collapseParagraphs(): P = - if (children.size == 1 && children.first() is P) (children.first() as P).collapseParagraphs() else this - - return when (docTag) { - is H1 -> buildHeader(1) - is H2 -> buildHeader(2) - is H3 -> buildHeader(3) - is H4 -> buildHeader(4) - is H5 -> buildHeader(5) - is H6 -> buildHeader(6) - is Ul -> buildList(false) - is Ol -> buildList(true, start = docTag.params["start"]?.toInt() ?: 1) - is Li -> listOf( - ContentGroup(buildChildren(docTag), dci, sourceSets.toDisplaySourceSets(), styles, extras) - ) - is Dl -> buildList(false, newStyles = setOf(ListStyle.DescriptionList)) - is Dt -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + ListStyle.DescriptionTerm - ) - ) - is Dd -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + ListStyle.DescriptionDetails - ) - ) - is Br -> buildNewLine() - is B -> buildChildren(docTag, setOf(TextStyle.Strong)) - is I -> buildChildren(docTag, setOf(TextStyle.Italic)) - is P -> listOf( - ContentGroup( - buildChildren(docTag.collapseParagraphs()), - dci, - sourceSets.toDisplaySourceSets(), - styles + setOf(TextStyle.Paragraph), - extras - ) - ) - is A -> listOf( - ContentResolvedLink( - buildChildren(docTag), - docTag.params.getValue("href"), - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is DocumentationLink -> listOf( - ContentDRILink( - buildChildren(docTag), - docTag.dri, - DCI( - setOf(docTag.dri), - ContentKind.Main - ), - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is BlockQuote -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + TextStyle.Quotation, - ) - ) - is Pre, is CodeBlock -> listOf( - ContentCodeBlock( - buildChildren(docTag), - docTag.params.getOrDefault("lang", ""), - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is CodeInline -> listOf( - ContentCodeInline( - buildChildren(docTag), - "", - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is Img -> listOf( - ContentEmbeddedResource( - address = docTag.params["href"]!!, - altText = docTag.params["alt"], - dci = dci, - sourceSets = sourceSets.toDisplaySourceSets(), - style = styles, - extra = extras - ) - ) - is HorizontalRule -> listOf( - ContentText( - "", - dci, - sourceSets.toDisplaySourceSets(), - setOf() - ) - ) - is Text -> listOf( - ContentText( - docTag.body, - dci, - sourceSets.toDisplaySourceSets(), - styles, - extras + HtmlContent.takeIf { docTag.params["content-type"] == "html" } - ) - ) - is Strikethrough -> buildChildren(docTag, setOf(TextStyle.Strikethrough)) - is Table -> { - //https://html.spec.whatwg.org/multipage/tables.html#the-caption-element - if (docTag.children.any { it is TBody }) { - val head = docTag.children.filterIsInstance<THead>().flatMap { it.children } - val body = docTag.children.filterIsInstance<TBody>().flatMap { it.children } - listOf( - ContentTable( - header = buildTableRows(head.filterIsInstance<Th>(), CommentTable), - caption = docTag.children.firstIsInstanceOrNull<Caption>()?.let { - ContentGroup( - buildContent(it, dci, sourceSets), - dci, - sourceSets.toDisplaySourceSets(), - styles, - extras - ) - }, - buildTableRows(body.filterIsInstance<Tr>(), CommentTable), - dci, - sourceSets.toDisplaySourceSets(), - styles + CommentTable - ) - ) - } else { - listOf( - ContentTable( - header = buildTableRows(docTag.children.filterIsInstance<Th>(), CommentTable), - caption = null, - buildTableRows(docTag.children.filterIsInstance<Tr>(), CommentTable), - dci, - sourceSets.toDisplaySourceSets(), - styles + CommentTable - ) - ) - } - } - is Th, - is Tr -> listOf( - ContentGroup( - docTag.children.map { - ContentGroup(buildChildren(it), dci, sourceSets.toDisplaySourceSets(), styles, extras) - }, - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is Index -> listOf( - ContentGroup( - buildChildren(docTag, newStyles = styles + ContentStyle.InDocumentationAnchor), - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is CustomDocTag -> if (docTag.isNonemptyFile()) { - listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles, - extra = extras - ) - ) - } else { - buildChildren(docTag) - } - is Caption -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + ContentStyle.Caption, - extra = extras - ) - ) - is Var -> buildChildren(docTag, setOf(TextStyle.Var)) - is U -> buildChildren(docTag, setOf(TextStyle.Underlined)) - - else -> buildChildren(docTag) - } - } - - private fun CustomDocTag.isNonemptyFile() = name == MARKDOWN_ELEMENT_FILE_NAME && children.size > 1 -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt deleted file mode 100644 index 80886cc5..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.pages.ContentPage -import org.jetbrains.dokka.pages.PageNode -import org.jetbrains.dokka.utilities.DokkaLogger - -public class FallbackPageMergerStrategy( - private val logger: DokkaLogger -) : PageMergerStrategy { - override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { - pages.map { - (it as? ContentPage) - } - val renderedPath = path.joinToString(separator = "/") - if (pages.size != 1) logger.warn("For $renderedPath: expected 1 page, but got ${pages.size}") - return listOf(pages.first()) - } -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt deleted file mode 100644 index e52c233c..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.pages.PageNode -import org.jetbrains.dokka.pages.RootPageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.query -import org.jetbrains.dokka.transformers.pages.PageTransformer - -public class PageMerger(context: DokkaContext) : PageTransformer { - - private val strategies: Iterable<PageMergerStrategy> = context.plugin<DokkaBase>().query { pageMergerStrategy } - - override fun invoke(input: RootPageNode): RootPageNode = - input.modified(children = input.children.map { it.mergeChildren(emptyList()) }) - - private fun PageNode.mergeChildren(path: List<String>): PageNode = children.groupBy { it::class }.map { - it.value.groupBy { it.name }.map { (n, v) -> mergePageNodes(v, path + n) }.map { it.assertSingle(path) } - }.let { pages -> - modified(children = pages.flatten().map { it.mergeChildren(path + it.name) }) - } - - private fun mergePageNodes(pages: List<PageNode>, path: List<String>): List<PageNode> = - strategies.fold(pages) { acc, strategy -> tryMerge(strategy, acc, path) } - - private fun tryMerge(strategy: PageMergerStrategy, pages: List<PageNode>, path: List<String>) = - if (pages.size > 1) strategy.tryMerge(pages, path) else pages -} - -private fun <T> Iterable<T>.assertSingle(path: List<String>): T = try { - single() - } catch (e: Exception) { - val renderedPath = path.joinToString(separator = "/") - throw IllegalStateException("Page merger is misconfigured. Error for $renderedPath: ${e.message}") - } diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt deleted file mode 100644 index ea1b1f03..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.pages.PageNode - -public fun interface PageMergerStrategy { - - public fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> - -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt deleted file mode 100644 index 864545e6..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.base.renderers.sourceSets -import org.jetbrains.dokka.base.transformers.documentables.isDeprecated -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.dfs -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.utilities.DokkaLogger - -/** - * Merges [MemberPage] elements that have the same name. - * That includes **both** properties and functions. - */ -public class SameMethodNamePageMergerStrategy( - public val logger: DokkaLogger -) : PageMergerStrategy { - override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { - val members = pages - .filterIsInstance<MemberPageNode>() - .takeIf { it.isNotEmpty() } - ?.sortedBy { it.containsDeprecatedDocumentables() } // non-deprecated first - ?: return pages - - val name = pages.first().name.also { - if (pages.any { page -> page.name != it }) { // Is this even possible? - logger.error("Page names for $it do not match!") - } - } - val dri = members.flatMap { it.dri }.toSet() - - - val merged = MemberPageNode( - dri = dri, - name = name, - children = members.flatMap { it.children }.distinct(), - content = squashDivergentInstances(members).withSourceSets(members.allSourceSets()), - embeddedResources = members.flatMap { it.embeddedResources }.distinct(), - documentables = members.flatMap { it.documentables } - ) - - return (pages - members) + listOf(merged) - } - - @Suppress("UNCHECKED_CAST") - private fun MemberPageNode.containsDeprecatedDocumentables() = - this.documentables.any { (it as? WithExtraProperties<Documentable>)?.isDeprecated() == true } - - private fun List<MemberPageNode>.allSourceSets(): Set<DisplaySourceSet> = - fold(emptySet()) { acc, e -> acc + e.sourceSets() } - - private fun squashDivergentInstances(nodes: List<MemberPageNode>): ContentNode = - nodes.map { it.content } - .reduce { acc, node -> - acc.mapTransform<ContentDivergentGroup, ContentNode> { g -> - g.copy(children = (g.children + - ((node.dfs { it is ContentDivergentGroup && it.groupID == g.groupID } as? ContentDivergentGroup) - ?.children ?: emptyList()) - ) - ) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt deleted file mode 100644 index 8d52a39d..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.toDisplaySourceSets -import org.jetbrains.dokka.pages.ContentComposite -import org.jetbrains.dokka.pages.ContentNode -import org.jetbrains.dokka.pages.RootPageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.pages.PageTransformer - -public class SourceSetMergingPageTransformer(context: DokkaContext) : PageTransformer { - - private val mergedSourceSets = context.configuration.sourceSets.toDisplaySourceSets() - .associateBy { sourceSet -> sourceSet.key } - - override fun invoke(input: RootPageNode): RootPageNode { - return input.transformContentPagesTree { contentPage -> - val content: ContentNode = contentPage.content - contentPage.modified(content = transformWithMergedSourceSets(content)) - } - } - - private fun transformWithMergedSourceSets( - contentNode: ContentNode - ): ContentNode { - val mergedSourceSets = contentNode.sourceSets.map { mergedSourceSets.getValue(it.key) }.toSet() - return when (contentNode) { - is ContentComposite -> contentNode - .transformChildren(::transformWithMergedSourceSets) - .withSourceSets(mergedSourceSets) - else -> contentNode.withSourceSets(mergedSourceSets.toSet()) - } - } -} - -private val DisplaySourceSet.key get() = SourceSetMergingKey(name, platform) - -private data class SourceSetMergingKey(private val displayName: String, private val platform: Platform) diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt deleted file mode 100644 index 80eeca7e..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.sourcelinks - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.pages.PageTransformer -import java.io.File - -public class SourceLinksTransformer( - public val context: DokkaContext -) : PageTransformer { - - private val builder : PageContentBuilder = PageContentBuilder( - context.plugin<DokkaBase>().querySingle { commentsToContentConverter }, - context.plugin<DokkaBase>().querySingle { signatureProvider }, - context.logger - ) - - override fun invoke(input: RootPageNode): RootPageNode { - val sourceLinks = getSourceLinksFromConfiguration() - if (sourceLinks.isEmpty()) { - return input - } - return input.transformContentPagesTree { node -> - when (node) { - is WithDocumentables -> { - val sources = node.documentables - .filterIsInstance<WithSources>() - .fold(mutableMapOf<DRI, List<Pair<DokkaSourceSet, String>>>()) { acc, documentable -> - val dri = (documentable as Documentable).dri - acc.compute(dri) { _, v -> - val sources = resolveSources(sourceLinks, documentable) - v?.plus(sources) ?: sources - } - acc - } - if (sources.isNotEmpty()) - node.modified(content = transformContent(node.content, sources)) - else - node - } - else -> node - } - } - } - - private fun getSourceLinksFromConfiguration(): List<SourceLink> { - return context.configuration.sourceSets - .flatMap { it.sourceLinks.map { sl -> SourceLink(sl, it) } } - } - - private fun resolveSources( - sourceLinks: List<SourceLink>, documentable: WithSources - ): List<Pair<DokkaSourceSet, String>> { - return documentable.sources.mapNotNull { (sourceSet, documentableSource) -> - val sourceLink = sourceLinks.find { sourceLink -> - File(documentableSource.path).startsWith(sourceLink.path) && sourceLink.sourceSetData == sourceSet - } ?: return@mapNotNull null - - sourceSet to documentableSource.toLink(sourceLink) - } - } - - private fun DocumentableSource.toLink(sourceLink: SourceLink): String { - val sourcePath = File(this.path).invariantSeparatorsPath - val sourceLinkPath = File(sourceLink.path).invariantSeparatorsPath - - val lineNumber = this.computeLineNumber() - return sourceLink.url + - sourcePath.split(sourceLinkPath)[1] + - sourceLink.lineSuffix + - "${lineNumber ?: 1}" - } - - private fun ContentNode.signatureGroupOrNull() = - (this as? ContentGroup)?.takeIf { it.dci.kind == ContentKind.Symbol } - - private fun transformContent( - contentNode: ContentNode, sources: Map<DRI, List<Pair<DokkaSourceSet, String>>> - ): ContentNode = - contentNode.signatureGroupOrNull()?.let { sg -> - val sgIds = sg.sourceSets.computeSourceSetIds() - sources[sg.dci.dri.singleOrNull()]?.let { sourceLinks -> - sourceLinks - .filter { it.first.sourceSetID in sgIds } - .takeIf { it.isNotEmpty() } - ?.let { filteredSourcesLinks -> - sg.copy(children = sg.children + filteredSourcesLinks.map { - buildContentLink( - sg.dci.dri.first(), - it.first, - it.second - ) - }) - } - } - } ?: when (contentNode) { - is ContentComposite -> contentNode.transformChildren { transformContent(it, sources) } - else -> contentNode - } - - private fun buildContentLink(dri: DRI, sourceSet: DokkaSourceSet, link: String) = builder.contentFor( - dri, - setOf(sourceSet), - ContentKind.Source, - setOf(TextStyle.FloatingRight) - ) { - text("(") - link("source", link) - text(")") - } -} - -public data class SourceLink( - val path: String, - val url: String, - val lineSuffix: String?, - val sourceSetData: DokkaSourceSet -) { - public constructor( - sourceLinkDefinition: DokkaConfiguration.SourceLinkDefinition, - sourceSetData: DokkaSourceSet - ) : this( - sourceLinkDefinition.localDirectory, - sourceLinkDefinition.remoteUrl.toExternalForm(), - sourceLinkDefinition.remoteLineSuffix, - sourceSetData - ) -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt b/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt deleted file mode 100644 index fcec234f..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.tags - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.model.doc.CustomTagWrapper -import org.jetbrains.dokka.model.doc.DocTag - -/** - * Provides an ability to render custom doc tags - * - * Custom tags can be generated during build, for instance via transformers from converting an annotation - * (such as in [org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer]) - * - * Also, custom tags can come from the kdoc itself, where "custom" is defined as unknown to the compiler/spec. - * `@property` and `@throws` are not custom tags - they are defined by the spec and have special meaning - * and separate blocks on the documentation page, it's clear how to render it. Whereas `@usesMathJax` is - * a custom tag - it's application/plugin specific and is not handled by dokka by default. - * - * Using this provider, we can map custom tags (such as `@usesMathJax`) and generate content for it that - * will be displayed on the pages. - */ -public interface CustomTagContentProvider { - - /** - * Whether this content provider supports given [CustomTagWrapper]. - * - * Tags can be filtered out either by name or by nested [DocTag] type - */ - public fun isApplicable(customTag: CustomTagWrapper): Boolean - - /** - * Full blown content description, most likely to be on a separate page - * dedicated to just one element (i.e one class/function), so any - * amount of detail should be fine. - */ - public fun DocumentableContentBuilder.contentForDescription( - sourceSet: DokkaSourceSet, - customTag: CustomTagWrapper - ) {} - - /** - * Brief comment section, usually displayed as a summary/preview. - * - * For instance, when listing all functions of a class on one page, - * it'll be too much to display complete documentation for each function. - * Instead, a small brief is shown for each one (i.e the first paragraph - * or some other important information) - the user can go to the dedicated - * page for more details if they find the brief interesting. - * - * Tag-wise, it would make sense to include `Since Kotlin`, since it's - * important information for the users of stdlib. It would make little - * sense to include `@usesMathjax` here, as this information seems - * to be more specific and detailed than is needed for a brief. - */ - public fun DocumentableContentBuilder.contentForBrief( - sourceSet: DokkaSourceSet, - customTag: CustomTagWrapper - ) {} -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt b/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt deleted file mode 100644 index 7c35f719..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.tags - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.translators.documentables.KDOC_TAG_HEADER_LEVEL -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.model.doc.CustomTagWrapper -import org.jetbrains.dokka.pages.TextStyle - -public object SinceKotlinTagContentProvider : CustomTagContentProvider { - - private const val SINCE_KOTLIN_TAG_NAME = "Since Kotlin" - - override fun isApplicable(customTag: CustomTagWrapper): Boolean = customTag.name == SINCE_KOTLIN_TAG_NAME - - override fun DocumentableContentBuilder.contentForDescription( - sourceSet: DokkaConfiguration.DokkaSourceSet, - customTag: CustomTagWrapper - ) { - group(sourceSets = setOf(sourceSet), styles = emptySet()) { - header(KDOC_TAG_HEADER_LEVEL, customTag.name) - comment(customTag.root) - } - } - - override fun DocumentableContentBuilder.contentForBrief( - sourceSet: DokkaConfiguration.DokkaSourceSet, - customTag: CustomTagWrapper - ) { - group(sourceSets = setOf(sourceSet), styles = setOf(TextStyle.InlineComment)) { - text(customTag.name + " ", styles = setOf(TextStyle.Bold)) - comment(customTag.root, styles = emptySet()) - } - } -} |