From cf2e842da89f0effa6bdb5eb942b250c94360b5c Mon Sep 17 00:00:00 2001 From: Filip ZybaƂa Date: Thu, 25 Jun 2020 14:53:24 +0200 Subject: Refactor for documentable filters. --- plugins/base/src/main/kotlin/DokkaBase.kt | 15 +- .../DeprecatedDocumentableFilterTransformer.kt | 260 +++++++++ .../documentables/DocumentableFilter.kt | 601 --------------------- .../DocumentableVisibilityFilterTransformer.kt | 331 ++++++++++++ .../EmptyPackagesFilterTransformer.kt | 32 ++ 5 files changed, 635 insertions(+), 604 deletions(-) create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt delete mode 100644 plugins/base/src/main/kotlin/transformers/documentables/DocumentableFilter.kt create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt (limited to 'plugins/base/src') diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 1d2097ef..cf4ad88f 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -14,7 +14,6 @@ import org.jetbrains.dokka.base.transformers.documentables.DefaultDocumentableMe import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationTransformer import org.jetbrains.dokka.base.transformers.documentables.ReportUndocumentedTransformer import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer -import org.jetbrains.dokka.base.transformers.documentables.DocumentableFilter import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter import org.jetbrains.dokka.base.transformers.pages.merger.FallbackPageMergerStrategy @@ -51,8 +50,18 @@ class DokkaBase : DokkaPlugin() { CoreExtensions.documentableMerger with DefaultDocumentableMerger } - val preMergeDocumentableTransformer by extending(isFallback = true) { - CoreExtensions.preMergeDocumentableTransformer providing ::DocumentableFilter + val deprecatedDocumentableFilter by extending(isFallback = true) { + CoreExtensions.preMergeDocumentableTransformer providing ::DeprecatedDocumentableFilterTransformer + } + + val documentableVisbilityFilter by extending(isFallback = true) { + CoreExtensions.preMergeDocumentableTransformer providing ::DocumentableVisibilityFilterTransformer + } + + val emptyPackagesFilter by extending(isFallback = true) { + CoreExtensions.preMergeDocumentableTransformer providing ::EmptyPackagesFilterTransformer order { + after(deprecatedDocumentableFilter, documentableVisbilityFilter) + } } val actualTypealiasAdder by extending { diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt new file mode 100644 index 00000000..27557107 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt @@ -0,0 +1,260 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer + +class DeprecatedDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List) = modules.map { original -> + val passOptions = context.configuration.passesConfigurations.first { + original.sourceSets.contains(context.sourceSet(it)) + } + val packageOptions = + passOptions.perPackageOptions + original.let { + DeprecatedDocumentableFilter(passOptions, packageOptions).processModule(it) + } + } + + private class DeprecatedDocumentableFilter( + val globalOptions: DokkaConfiguration.PassConfiguration, + val packageOptions: List + ) { + + fun T.isAllowedInPackage(): Boolean where T : WithExtraProperties, T : Documentable { + val packageName = this.dri.packageName + val condition = packageName != null && packageOptions.firstOrNull { + packageName.startsWith(it.prefix) + }?.skipDeprecated + ?: globalOptions.skipDeprecated + + fun T.isDeprecated() = extra[Annotations]?.let { annotations -> + annotations.content.values.flatten().any { + it.dri.toString() == "kotlin/Deprecated///PointingToDeclaration/" + } + } ?: false + + return !(condition && this.isDeprecated()) + } + + 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): Pair> { + var packagesListChanged = false + val filteredPackages = packages.mapNotNull { pckg -> + var modified = false + val functions = filterFunctions(pckg.functions).let { (listModified, list) -> + modified = modified || listModified + list + } + val properties = filterProperties(pckg.properties).let { (listModified, list) -> + modified = modified || listModified + list + } + val classlikes = filterClasslikes(pckg.classlikes).let { (listModified, list) -> + modified = modified || listModified + list + } + when { + !modified -> pckg + else -> { + packagesListChanged = true + DPackage( + pckg.dri, + functions, + properties, + classlikes, + pckg.typealiases, + pckg.documentation, + pckg.expectPresentInSet, + pckg.sourceSets, + pckg.extra + ) + } + } + } + return Pair(packagesListChanged, filteredPackages) + } + + private fun filterFunctions( + functions: List + ) = functions.filter { it.isAllowedInPackage() }.let { + Pair(it.size != functions.size, it) + } + + private fun filterProperties( + properties: List + ): Pair> = properties.filter { + it.isAllowedInPackage() + }.let { + Pair(properties.size != it.size, it) + } + + private fun filterEnumEntries(entries: List) = + entries.filter { it.isAllowedInPackage() }.map { entry -> + DEnumEntry( + entry.dri, + entry.name, + entry.documentation, + entry.expectPresentInSet, + filterFunctions(entry.functions).second, + filterProperties(entry.properties).second, + filterClasslikes(entry.classlikes).second, + entry.sourceSets, + entry.extra + ) + } + + private fun filterClasslikes( + classlikeList: List + ): Pair> { + var modified = false + return classlikeList.filter { classlike -> + when (classlike) { + is DClass -> classlike.isAllowedInPackage() + is DInterface -> classlike.isAllowedInPackage() + is DEnum -> classlike.isAllowedInPackage() + is DObject -> classlike.isAllowedInPackage() + is DAnnotation -> classlike.isAllowedInPackage() + } + }.map { classlike -> + fun helper(): DClasslike = when (classlike) { + is DClass -> DClass( + classlike.dri, + classlike.name, + filterFunctions(classlike.constructors).let { + modified = modified || it.first; it.second + }, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.sources, + classlike.visibility, + classlike.companion, + classlike.generics, + classlike.supertypes, + classlike.documentation, + classlike.expectPresentInSet, + classlike.modifier, + classlike.sourceSets, + classlike.extra + ) + is DAnnotation -> DAnnotation( + classlike.name, + classlike.dri, + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.companion, + filterFunctions(classlike.constructors).let { + modified = modified || it.first; it.second + }, + classlike.generics, + classlike.sourceSets, + classlike.extra + ) + is DEnum -> DEnum( + classlike.dri, + classlike.name, + filterEnumEntries(classlike.entries), + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.companion, + filterFunctions(classlike.constructors).let { + modified = modified || it.first; it.second + }, + classlike.supertypes, + classlike.sourceSets, + classlike.extra + ) + is DInterface -> DInterface( + classlike.dri, + classlike.name, + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.companion, + classlike.generics, + classlike.supertypes, + classlike.sourceSets, + classlike.extra + ) + is DObject -> DObject( + classlike.name, + classlike.dri, + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.supertypes, + classlike.sourceSets, + classlike.extra + ) + } + helper() + }.let { + Pair(it.size != classlikeList.size || modified, it) + } + } + } +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableFilter.kt deleted file mode 100644 index fcead846..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableFilter.kt +++ /dev/null @@ -1,601 +0,0 @@ -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.DAnnotation -import org.jetbrains.dokka.model.DEnum -import org.jetbrains.dokka.model.DFunction -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.kotlin.utils.addToStdlib.safeAs - -internal class DocumentableFilter(val context: DokkaContext) : PreMergeDocumentableTransformer { - - override fun invoke(modules: List): List = modules.map { original -> - val packageOptions = - context.configuration.passesConfigurations.first { original.sourceSets.contains(context.sourceSet(it)) } - .perPackageOptions - val passOptions = context.configuration.passesConfigurations.first { - original.sourceSets.contains(context.sourceSet(it)) - } - original.let { - DeprecationFilter(passOptions,packageOptions).processModule(it) - }.let { - VisibilityFilter(packageOptions, passOptions).processModule(it) - }.let { - EmptyPackagesFilter(passOptions).processModule(it) - } - } - - private class VisibilityFilter( - val packageOptions: List, - val globalOptions: DokkaConfiguration.PassConfiguration - ) { - - fun Visibility.isAllowedInPackage(packageName: String?) = when (this) { - is JavaVisibility.Public, - is JavaVisibility.Default, - is KotlinVisibility.Public -> true - else -> packageName != null - && packageOptions.firstOrNull { packageName.startsWith(it.prefix) }?.includeNonPublic - ?: globalOptions.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): Pair> { - var packagesListChanged = false - val filteredPackages = packages.mapNotNull { - 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 - } - when { - !modified -> it - else -> { - packagesListChanged = true - DPackage( - it.dri, - functions, - properties, - classlikes, - it.typealiases, - it.documentation, - it.expectPresentInSet, - it.sourceSets, - it.extra - ) - } - } - } - return Pair(packagesListChanged, filteredPackages) - } - - private fun alwaysTrue(a: T, p: SourceSetData) = true - private fun alwaysFalse(a: T, p: SourceSetData) = false - - private fun WithVisibility.visibilityForPlatform(data: SourceSetData): Visibility? = visibility[data] - - private fun T.filterPlatforms( - additionalCondition: (T, SourceSetData) -> Boolean = ::alwaysTrue, - alternativeCondition: (T, SourceSetData) -> 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 List.transform( - additionalCondition: (T, SourceSetData) -> Boolean = ::alwaysTrue, - alternativeCondition: (T, SourceSetData) -> Boolean = ::alwaysFalse, - recreate: (T, Set) -> T - ): Pair> where T : Documentable, T : WithVisibility { - var changed = false - val values = mapNotNull { t -> - val filteredPlatforms = t.filterPlatforms(additionalCondition, alternativeCondition) - when (filteredPlatforms.size) { - t.visibility.size -> t - 0 -> { - changed = true - null - } - else -> { - changed = true - recreate(t, filteredPlatforms) - } - } - } - return Pair(changed, values) - } - - private fun filterFunctions( - functions: List, - additionalCondition: (DFunction, SourceSetData) -> Boolean = ::alwaysTrue - ) = - functions.transform(additionalCondition) { original, filteredPlatforms -> - with(original) { - DFunction( - dri, - name, - isConstructor, - parameters, - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - sources.filtered(filteredPlatforms), - visibility.filtered(filteredPlatforms), - type, - generics.mapNotNull { it.filter(filteredPlatforms) }, - receiver, - modifier, - filteredPlatforms, - extra - ) - } - } - - private fun hasVisibleAccessorsForPlatform(property: DProperty, data: SourceSetData) = - property.getter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true || - property.setter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true - - private fun filterProperties( - properties: List, - additionalCondition: (DProperty, SourceSetData) -> Boolean = ::alwaysTrue - ): Pair> = - properties.transform(additionalCondition, ::hasVisibleAccessorsForPlatform) { original, filteredPlatforms -> - with(original) { - DProperty( - dri, - name, - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - sources.filtered(filteredPlatforms), - visibility.filtered(filteredPlatforms), - type, - receiver, - setter, - getter, - modifier, - filteredPlatforms, - generics.mapNotNull { it.filter(filteredPlatforms) }, - extra - ) - } - } - - private fun filterEnumEntries(entries: List, filteredPlatforms: Set) = - entries.mapNotNull { entry -> - if (filteredPlatforms.containsAll(entry.sourceSets)) entry - else { - val intersection = filteredPlatforms.intersect(entry.sourceSets) - if (intersection.isEmpty()) null - else DEnumEntry( - entry.dri, - entry.name, - entry.documentation.filtered(intersection), - entry.expectPresentInSet.filtered(filteredPlatforms), - filterFunctions(entry.functions) { _, data -> data in intersection }.second, - filterProperties(entry.properties) { _, data -> data in intersection }.second, - filterClasslikes(entry.classlikes) { _, data -> data in intersection }.second, - intersection, - entry.extra - ) - } - } - - private fun filterClasslikes( - classlikeList: List, - additionalCondition: (DClasslike, SourceSetData) -> Boolean = ::alwaysTrue - ): Pair> { - var classlikesListChanged = false - val filteredClasslikes: List = 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) else emptyList() - classlikesListChanged = classlikesListChanged || modified - when { - !modified -> this - this is DClass -> DClass( - dri, - name, - constructors, - functions, - properties, - classlikes, - sources.filtered(filteredPlatforms), - visibility.filtered(filteredPlatforms), - companion, - generics, - supertypes.filtered(filteredPlatforms), - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - modifier, - filteredPlatforms, - extra - ) - this is DAnnotation -> DAnnotation( - name, - dri, - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - sources.filtered(filteredPlatforms), - functions, - properties, - classlikes, - visibility.filtered(filteredPlatforms), - companion, - constructors, - generics, - filteredPlatforms, - extra - ) - this is DEnum -> DEnum( - dri, - name, - enumEntries, - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - sources.filtered(filteredPlatforms), - functions, - properties, - classlikes, - visibility.filtered(filteredPlatforms), - companion, - constructors, - supertypes.filtered(filteredPlatforms), - filteredPlatforms, - extra - ) - this is DInterface -> DInterface( - dri, - name, - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - sources.filtered(filteredPlatforms), - functions, - properties, - classlikes, - visibility.filtered(filteredPlatforms), - companion, - generics, - supertypes.filtered(filteredPlatforms), - filteredPlatforms, - extra - ) - this is DObject -> DObject( - name, - dri, - documentation.filtered(filteredPlatforms), - expectPresentInSet.filtered(filteredPlatforms), - sources.filtered(filteredPlatforms), - functions, - properties, - classlikes, - visibility, - supertypes.filtered(filteredPlatforms), - filteredPlatforms, - extra - ) - else -> null - } - } - } - } - return Pair(classlikesListChanged, filteredClasslikes) - } - - - } - - private class DeprecationFilter( - val globalOptions: DokkaConfiguration.PassConfiguration, - val packageOptions: List - ) { - - fun T.isAllowedInPackage(): Boolean where T : WithExtraProperties, T : Documentable { - val packageName = this.dri.packageName - val condition = packageName != null && packageOptions.firstOrNull { - packageName.startsWith(it.prefix) - }?.skipDeprecated - ?: globalOptions.skipDeprecated - - fun T.isDeprecated() = extra[Annotations]?.let { annotations -> - annotations.content.values.flatten().any { - it.dri.toString() == "kotlin/Deprecated///PointingToDeclaration/" - } - } ?: false - - return !(condition && this.isDeprecated()) - } - - 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): Pair> { - var packagesListChanged = false - val filteredPackages = packages.mapNotNull { - 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 - } - when { - !modified -> it - else -> { - packagesListChanged = true - DPackage( - it.dri, - functions, - properties, - classlikes, - it.typealiases, - it.documentation, - it.expectPresentInSet, - it.sourceSets, - it.extra - ) - } - } - } - return Pair(packagesListChanged, filteredPackages) - } - - private fun filterFunctions( - functions: List - ) = functions.filter { it.isAllowedInPackage() }.let { - Pair(it.size != functions.size, it) - } - - private fun filterProperties( - properties: List - ): Pair> = properties.filter { - it.isAllowedInPackage() - }.let { - Pair(properties.size != it.size, it) - } - - private fun filterEnumEntries(entries: List) = - entries.filter { it.isAllowedInPackage() }.map { entry -> - DEnumEntry( - entry.dri, - entry.name, - entry.documentation, - entry.expectPresentInSet, - filterFunctions(entry.functions).second, - filterProperties(entry.properties).second, - filterClasslikes(entry.classlikes).second, - entry.sourceSets, - entry.extra - ) - } - - private fun filterClasslikes( - classlikeList: List - ): Pair> { - var modified = false - return classlikeList.filter { - when (it) { - is DClass -> it.isAllowedInPackage() - is DInterface -> it.isAllowedInPackage() - is DEnum -> it.isAllowedInPackage() - is DObject -> it.isAllowedInPackage() - is DAnnotation -> it.isAllowedInPackage() - } - }.map { - fun helper(): DClasslike = when (it) { - is DClass -> DClass( - it.dri, - it.name, - filterFunctions(it.constructors).let { - modified = modified || it.first; it.second - }, - filterFunctions(it.functions).let { - modified = modified || it.first; it.second - }, - filterProperties(it.properties).let { - modified = modified || it.first; it.second - }, - filterClasslikes(it.classlikes).let { - modified = modified || it.first; it.second - }, - it.sources, - it.visibility, - it.companion, - it.generics, - it.supertypes, - it.documentation, - it.expectPresentInSet, - it.modifier, - it.sourceSets, - it.extra - ) - is DAnnotation -> DAnnotation( - it.name, - it.dri, - it.documentation, - it.expectPresentInSet, - it.sources, - filterFunctions(it.functions).let { - modified = modified || it.first; it.second - }, - filterProperties(it.properties).let { - modified = modified || it.first; it.second - }, - filterClasslikes(it.classlikes).let { - modified = modified || it.first; it.second - }, - it.visibility, - it.companion, - filterFunctions(it.constructors).let { - modified = modified || it.first; it.second - }, - it.generics, - it.sourceSets, - it.extra - ) - is DEnum -> DEnum( - it.dri, - it.name, - filterEnumEntries(it.entries), - it.documentation, - it.expectPresentInSet, - it.sources, - filterFunctions(it.functions).let { - modified = modified || it.first; it.second - }, - filterProperties(it.properties).let { - modified = modified || it.first; it.second - }, - filterClasslikes(it.classlikes).let { - modified = modified || it.first; it.second - }, - it.visibility, - it.companion, - filterFunctions(it.constructors).let { - modified = modified || it.first; it.second - }, - it.supertypes, - it.sourceSets, - it.extra - ) - is DInterface -> DInterface( - it.dri, - it.name, - it.documentation, - it.expectPresentInSet, - it.sources, - filterFunctions(it.functions).let { - modified = modified || it.first; it.second - }, - filterProperties(it.properties).let { - modified = modified || it.first; it.second - }, - filterClasslikes(it.classlikes).let { - modified = modified || it.first; it.second - }, - it.visibility, - it.companion, - it.generics, - it.supertypes, - it.sourceSets, - it.extra - ) - is DObject -> DObject( - it.name, - it.dri, - it.documentation, - it.expectPresentInSet, - it.sources, - filterFunctions(it.functions).let { - modified = modified || it.first; it.second - }, - filterProperties(it.properties).let { - modified = modified || it.first; it.second - }, - filterClasslikes(it.classlikes).let { - modified = modified || it.first; it.second - }, - it.visibility, - it.supertypes, - it.sourceSets, - it.extra - ) - } - helper() - }.let { - Pair(it.size != classlikeList.size || modified, it) - } - } - } - - private class EmptyPackagesFilter( - val passOptions: DokkaConfiguration.PassConfiguration - ) { - fun DPackage.shouldBeSkipped() = passOptions.skipEmptyPackages && - functions.isEmpty() && - properties.isEmpty() && - classlikes.isEmpty() - - fun processModule(module: DModule) = module.copy( - packages = module.packages.mapNotNull { pckg -> - if (pckg.shouldBeSkipped()) null - else pckg - } - ) - } -} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt new file mode 100644 index 00000000..0a01680f --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt @@ -0,0 +1,331 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer + +class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + + override fun invoke(modules: List) = modules.map { original -> + val passOptions = context.configuration.passesConfigurations.first { + original.sourceSets.contains(context.sourceSet(it)) + } + val packageOptions = + passOptions.perPackageOptions + original.let { + DocumentableVisibilityFilter(packageOptions, passOptions).processModule(it) + } + } + + private class DocumentableVisibilityFilter( + val packageOptions: List, + val globalOptions: DokkaConfiguration.PassConfiguration + ) { + fun Visibility.isAllowedInPackage(packageName: String?) = when (this) { + is JavaVisibility.Public, + is JavaVisibility.Default, + is KotlinVisibility.Public -> true + else -> packageName != null + && packageOptions.firstOrNull { packageName.startsWith(it.prefix) }?.includeNonPublic + ?: globalOptions.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): Pair> { + var packagesListChanged = false + val filteredPackages = packages.mapNotNull { + 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 + } + when { + !modified -> it + else -> { + packagesListChanged = true + DPackage( + it.dri, + functions, + properties, + classlikes, + it.typealiases, + it.documentation, + it.expectPresentInSet, + it.sourceSets, + it.extra + ) + } + } + } + return Pair(packagesListChanged, filteredPackages) + } + + private fun alwaysTrue(a: T, p: SourceSetData) = true + private fun alwaysFalse(a: T, p: SourceSetData) = false + + private fun WithVisibility.visibilityForPlatform(data: SourceSetData): Visibility? = visibility[data] + + private fun T.filterPlatforms( + additionalCondition: (T, SourceSetData) -> Boolean = ::alwaysTrue, + alternativeCondition: (T, SourceSetData) -> 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 List.transform( + additionalCondition: (T, SourceSetData) -> Boolean = ::alwaysTrue, + alternativeCondition: (T, SourceSetData) -> Boolean = ::alwaysFalse, + recreate: (T, Set) -> T + ): Pair> where T : Documentable, T : WithVisibility { + var changed = false + val values = mapNotNull { t -> + val filteredPlatforms = t.filterPlatforms(additionalCondition, alternativeCondition) + when (filteredPlatforms.size) { + t.visibility.size -> t + 0 -> { + changed = true + null + } + else -> { + changed = true + recreate(t, filteredPlatforms) + } + } + } + return Pair(changed, values) + } + + private fun filterFunctions( + functions: List, + additionalCondition: (DFunction, SourceSetData) -> Boolean = ::alwaysTrue + ) = + functions.transform(additionalCondition) { original, filteredPlatforms -> + with(original) { + DFunction( + dri, + name, + isConstructor, + parameters, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + visibility.filtered(filteredPlatforms), + type, + generics.mapNotNull { it.filter(filteredPlatforms) }, + receiver, + modifier, + filteredPlatforms, + extra + ) + } + } + + private fun hasVisibleAccessorsForPlatform(property: DProperty, data: SourceSetData) = + property.getter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true || + property.setter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true + + private fun filterProperties( + properties: List, + additionalCondition: (DProperty, SourceSetData) -> Boolean = ::alwaysTrue + ): Pair> = + properties.transform(additionalCondition, ::hasVisibleAccessorsForPlatform) { original, filteredPlatforms -> + with(original) { + DProperty( + dri, + name, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + visibility.filtered(filteredPlatforms), + type, + receiver, + setter, + getter, + modifier, + filteredPlatforms, + generics.mapNotNull { it.filter(filteredPlatforms) }, + extra + ) + } + } + + private fun filterEnumEntries(entries: List, filteredPlatforms: Set) = + entries.mapNotNull { entry -> + if (filteredPlatforms.containsAll(entry.sourceSets)) entry + else { + val intersection = filteredPlatforms.intersect(entry.sourceSets) + if (intersection.isEmpty()) null + else DEnumEntry( + entry.dri, + entry.name, + entry.documentation.filtered(intersection), + entry.expectPresentInSet.filtered(filteredPlatforms), + filterFunctions(entry.functions) { _, data -> data in intersection }.second, + filterProperties(entry.properties) { _, data -> data in intersection }.second, + filterClasslikes(entry.classlikes) { _, data -> data in intersection }.second, + intersection, + entry.extra + ) + } + } + + private fun filterClasslikes( + classlikeList: List, + additionalCondition: (DClasslike, SourceSetData) -> Boolean = ::alwaysTrue + ): Pair> { + var classlikesListChanged = false + val filteredClasslikes: List = 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) else emptyList() + classlikesListChanged = classlikesListChanged || modified + when { + !modified -> this + this is DClass -> DClass( + dri, + name, + constructors, + functions, + properties, + classlikes, + sources.filtered(filteredPlatforms), + visibility.filtered(filteredPlatforms), + companion, + generics, + supertypes.filtered(filteredPlatforms), + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + modifier, + filteredPlatforms, + extra + ) + this is DAnnotation -> DAnnotation( + name, + dri, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility.filtered(filteredPlatforms), + companion, + constructors, + generics, + filteredPlatforms, + extra + ) + this is DEnum -> DEnum( + dri, + name, + enumEntries, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility.filtered(filteredPlatforms), + companion, + constructors, + supertypes.filtered(filteredPlatforms), + filteredPlatforms, + extra + ) + this is DInterface -> DInterface( + dri, + name, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility.filtered(filteredPlatforms), + companion, + generics, + supertypes.filtered(filteredPlatforms), + filteredPlatforms, + extra + ) + this is DObject -> DObject( + name, + dri, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility, + supertypes.filtered(filteredPlatforms), + filteredPlatforms, + extra + ) + else -> null + } + } + } + } + return Pair(classlikesListChanged, filteredClasslikes) + } + } +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt new file mode 100644 index 00000000..6b29531c --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt @@ -0,0 +1,32 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.DPackage +import org.jetbrains.dokka.model.sourceSet +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer + +class EmptyPackagesFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List): List = modules.map { original -> + val passOptions = context.configuration.passesConfigurations.first { + original.sourceSets.contains(context.sourceSet(it)) + } + original.let { + EmptyPackagesFilter(passOptions).processModule(it) + } + } + + private class EmptyPackagesFilter( + val passOptions: DokkaConfiguration.PassConfiguration + ) { + fun DPackage.shouldBeSkipped() = passOptions.skipEmptyPackages && + functions.isEmpty() && + properties.isEmpty() && + classlikes.isEmpty() + + fun processModule(module: DModule) = module.copy( + packages = module.packages.filter { !it.shouldBeSkipped() } + ) + } +} \ No newline at end of file -- cgit