aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/kotlin/CoreExtensions.kt2
-rw-r--r--core/src/main/kotlin/DokkaGenerator.kt18
-rw-r--r--core/src/main/kotlin/configuration.kt4
-rw-r--r--core/src/main/kotlin/model/documentableUtils.kt23
-rw-r--r--core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt8
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt5
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt316
-rw-r--r--testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt7
-rw-r--r--testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt3
9 files changed, 379 insertions, 7 deletions
diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt
index be354bc4..d7b0b285 100644
--- a/core/src/main/kotlin/CoreExtensions.kt
+++ b/core/src/main/kotlin/CoreExtensions.kt
@@ -6,6 +6,7 @@ import org.jetbrains.dokka.transformers.descriptors.DescriptorToDocumentableTran
import org.jetbrains.dokka.transformers.documentation.DocumentableMerger
import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator
import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer
+import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer
import org.jetbrains.dokka.transformers.pages.PageTransformer
import org.jetbrains.dokka.transformers.psi.PsiToDocumentableTranslator
import kotlin.reflect.KProperty
@@ -13,6 +14,7 @@ import kotlin.reflect.KProperty
object CoreExtensions {
val descriptorToDocumentableTranslator by coreExtension<DescriptorToDocumentableTranslator>()
val psiToDocumentableTranslator by coreExtension<PsiToDocumentableTranslator>()
+ val preMergeDocumentableTransformer by coreExtension<PreMergeDocumentableTransformer>()
val documentableMerger by coreExtension<DocumentableMerger>()
val documentableTransformer by coreExtension<DocumentableTransformer>()
val documentableToPageTranslator by coreExtension<DocumentableToPageTranslator>()
diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt
index a260d5ad..d598c773 100644
--- a/core/src/main/kotlin/DokkaGenerator.kt
+++ b/core/src/main/kotlin/DokkaGenerator.kt
@@ -39,11 +39,14 @@ class DokkaGenerator(
report("Creating documentation models")
val modulesFromPlatforms = createDocumentationModels(platforms, context)
+ report("Transforming documentation model before merging")
+ val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms, context)
+
report("Merging documentation models")
- val documentationModel = mergeDocumentationModels(modulesFromPlatforms, context)
+ val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge, context)
- report("Transforming documentation model")
- val transformedDocumentation = transformDocumentationModel(documentationModel, context)
+ report("Transforming documentation model after merging")
+ val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel, context)
report("Creating pages")
val pages = createPages(transformedDocumentation, context)
@@ -61,7 +64,7 @@ class DokkaGenerator(
fun setUpAnalysis(configuration: DokkaConfiguration): Map<PlatformData, EnvironmentAndFacade> =
configuration.passesConfigurations.map {
- PlatformData(it.moduleName, it.analysisPlatform, it.targets) to createEnvironmentAndFacade(it)
+ it.platformData to createEnvironmentAndFacade(it)
}.toMap()
fun initializePlugins(
@@ -77,12 +80,17 @@ class DokkaGenerator(
) = platforms.map { (pdata, _) -> translateDescriptors(pdata, context) } +
platforms.map { (pdata, _) -> translatePsi(pdata, context) }
+ fun transformDocumentationModelBeforeMerge(
+ modulesFromPlatforms: List<DModule>,
+ context: DokkaContext
+ ) = context[CoreExtensions.preMergeDocumentableTransformer].fold(modulesFromPlatforms) { acc, t -> t(acc, context) }
+
fun mergeDocumentationModels(
modulesFromPlatforms: List<DModule>,
context: DokkaContext
) = context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms, context)
- fun transformDocumentationModel(
+ fun transformDocumentationModelAfterMerge(
documentationModel: DModule,
context: DokkaContext
) = context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) }
diff --git a/core/src/main/kotlin/configuration.kt b/core/src/main/kotlin/configuration.kt
index 8c6d35e8..4a6b7d68 100644
--- a/core/src/main/kotlin/configuration.kt
+++ b/core/src/main/kotlin/configuration.kt
@@ -1,5 +1,6 @@
package org.jetbrains.dokka
+import org.jetbrains.dokka.pages.PlatformData
import java.io.File
import java.net.URL
@@ -57,6 +58,9 @@ interface DokkaConfiguration {
val analysisPlatform: Platform
val targets: List<String>
val sinceKotlin: String?
+
+ val platformData: PlatformData
+ get() = PlatformData(moduleName, analysisPlatform, targets)
}
interface SourceRoot {
diff --git a/core/src/main/kotlin/model/documentableUtils.kt b/core/src/main/kotlin/model/documentableUtils.kt
new file mode 100644
index 00000000..7f946344
--- /dev/null
+++ b/core/src/main/kotlin/model/documentableUtils.kt
@@ -0,0 +1,23 @@
+package org.jetbrains.dokka.model
+
+import org.jetbrains.dokka.pages.PlatformData
+
+fun <T> PlatformDependent<T>.filtered(platformDataList: List<PlatformData>) = PlatformDependent(
+ map.filter { it.key in platformDataList },
+ expect
+)
+
+fun DTypeParameter.filter(filteredData: List<PlatformData>) =
+ if (filteredData.containsAll(platformData)) this
+ else {
+ val intersection = filteredData.intersect(platformData).toList()
+ if (intersection.isEmpty()) null
+ else DTypeParameter(
+ dri,
+ name,
+ documentation.filtered(intersection),
+ bounds,
+ intersection,
+ extra
+ )
+ } \ No newline at end of file
diff --git a/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt
new file mode 100644
index 00000000..dfb1f26b
--- /dev/null
+++ b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt
@@ -0,0 +1,8 @@
+package org.jetbrains.dokka.transformers.documentation
+
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.plugability.DokkaContext
+
+interface PreMergeDocumentableTransformer {
+ operator fun invoke(modules: List<DModule>, context: DokkaContext): List<DModule>
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt
index 8934dd7f..448373ea 100644
--- a/plugins/base/src/main/kotlin/DokkaBase.kt
+++ b/plugins/base/src/main/kotlin/DokkaBase.kt
@@ -11,6 +11,7 @@ import org.jetbrains.dokka.base.signatures.SignatureProvider
import org.jetbrains.dokka.base.transformers.documentables.DefaultDocumentableMerger
import org.jetbrains.dokka.base.transformers.documentables.InheritorsExtractorTransformer
import org.jetbrains.dokka.base.transformers.pages.annotations.DeprecatedStrikethroughTransformer
+import org.jetbrains.dokka.base.transformers.documentables.DocumentableVisibilityFilter
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
@@ -43,6 +44,10 @@ class DokkaBase : DokkaPlugin() {
CoreExtensions.documentableMerger with DefaultDocumentableMerger
}
+ val preMergeDocumentableTransformer by extending(isFallback = true) {
+ CoreExtensions.preMergeDocumentableTransformer with DocumentableVisibilityFilter
+ }
+
val kotlinSignatureProvider by extending(isFallback = true) {
signatureProvider providing { ctx ->
KotlinSignatureProvider(ctx.single(commentsToContentConverter), ctx.logger)
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt
new file mode 100644
index 00000000..76276f39
--- /dev/null
+++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt
@@ -0,0 +1,316 @@
+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.pages.PlatformData
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer
+
+internal object DocumentableVisibilityFilter : PreMergeDocumentableTransformer {
+
+ override fun invoke(modules: List<DModule>, context: DokkaContext): List<DModule> = modules.map { original ->
+ val packageOptions =
+ context.configuration.passesConfigurations.first { original.platformData.contains(it.platformData) }
+ .perPackageOptions
+ DocumentableFilter(packageOptions).processModule(original)
+ }
+
+ private class DocumentableFilter(val packageOptions: List<DokkaConfiguration.PackageOptions>) {
+
+ 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 == true
+ }
+
+ fun processModule(original: DModule) =
+ filterPackages(original.packages).let { (modified, packages) ->
+ if (!modified) original
+ else
+ DModule(
+ original.name,
+ packages = packages,
+ documentation = original.documentation,
+ platformData = original.platformData,
+ extra = original.extra
+ )
+ }
+
+ private fun filterPackages(packages: List<DPackage>): Pair<Boolean, List<DPackage>> {
+ 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
+ functions.isEmpty() && properties.isEmpty() && classlikes.isEmpty() -> null
+ else -> {
+ packagesListChanged = true
+ DPackage(
+ it.dri,
+ functions,
+ properties,
+ classlikes,
+ it.documentation,
+ it.platformData,
+ it.extra
+ )
+ }
+ }
+ }
+ return Pair(packagesListChanged, filteredPackages)
+ }
+
+ private fun <T : WithVisibility> alwaysTrue(a: T, p: PlatformData) = true
+ private fun <T : WithVisibility> alwaysFalse(a: T, p: PlatformData) = false
+
+ private fun <T> T.filterPlatforms(
+ additionalCondition: (T, PlatformData) -> Boolean = ::alwaysTrue,
+ optionalCondition: (T, PlatformData) -> Boolean = ::alwaysFalse
+ ) where T : Documentable, T : WithVisibility =
+ visibility.mapNotNull { (platformData, visibility) ->
+ platformData.takeIf { d ->
+ (visibility.isAllowedInPackage(dri.packageName) || optionalCondition(
+ this,
+ d
+ )) && additionalCondition(this, d)
+ }
+ }
+
+ private fun <T> List<T>.transform(
+ additionalCondition: (T, PlatformData) -> Boolean = ::alwaysTrue,
+ optionalCondition: (T, PlatformData) -> Boolean = ::alwaysFalse,
+ recreate: (T, List<PlatformData>) -> T
+ ): Pair<Boolean, List<T>> where T : Documentable, T : WithVisibility {
+ var changed = false
+ val values = mapNotNull { t ->
+ val filteredPlatforms = t.filterPlatforms(additionalCondition, optionalCondition)
+ 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<DFunction>,
+ additionalCondition: (DFunction, PlatformData) -> Boolean = ::alwaysTrue
+ ) =
+ functions.transform(additionalCondition) { original, filteredPlatforms ->
+ with(original) {
+ DFunction(
+ dri,
+ name,
+ isConstructor,
+ parameters,
+ documentation.filtered(filteredPlatforms),
+ sources.filtered(filteredPlatforms),
+ visibility.filtered(filteredPlatforms),
+ type,
+ generics.mapNotNull { it.filter(filteredPlatforms) },
+ receiver,
+ modifier,
+ filteredPlatforms,
+ extra
+ )
+ }
+ }
+
+ private fun hasVisibleAccessorsForPlatform(property: DProperty, data: PlatformData) =
+ property.getter?.visibility?.get(data)?.isAllowedInPackage(property.dri.packageName) == true ||
+ property.setter?.visibility?.get(data)?.isAllowedInPackage(property.dri.packageName) == true
+
+ private fun filterProperties(
+ properties: List<DProperty>,
+ additionalCondition: (DProperty, PlatformData) -> Boolean = ::alwaysTrue
+ ): Pair<Boolean, List<DProperty>> =
+ properties.transform(additionalCondition, ::hasVisibleAccessorsForPlatform) { original, filteredPlatforms ->
+ with(original) {
+ DProperty(
+ dri,
+ name,
+ documentation.filtered(filteredPlatforms),
+ sources.filtered(filteredPlatforms),
+ visibility.filtered(filteredPlatforms),
+ type,
+ receiver,
+ setter,
+ getter,
+ modifier,
+ filteredPlatforms,
+ extra
+ )
+ }
+ }
+
+ private fun filterEnumEntries(entries: List<DEnumEntry>, filteredPlatforms: List<PlatformData>) =
+ entries.mapNotNull { entry ->
+ if (filteredPlatforms.containsAll(entry.platformData)) entry
+ else {
+ val intersection = filteredPlatforms.intersect(entry.platformData).toList()
+ if (intersection.isEmpty()) null
+ else DEnumEntry(
+ entry.dri,
+ entry.name,
+ entry.documentation.filtered(intersection),
+ 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<DClasslike>,
+ additionalCondition: (DClasslike, PlatformData) -> 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 = platformData.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),
+ modifier,
+ filteredPlatforms,
+ extra
+ )
+ this is DAnnotation -> DAnnotation(
+ name,
+ dri,
+ documentation.filtered(filteredPlatforms),
+ sources.filtered(filteredPlatforms),
+ functions,
+ properties,
+ classlikes,
+ visibility.filtered(filteredPlatforms),
+ companion,
+ constructors,
+ filteredPlatforms,
+ extra
+ )
+ this is DEnum -> DEnum(
+ dri,
+ name,
+ enumEntries,
+ documentation.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),
+ 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),
+ 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/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt b/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt
index ddee7083..0e77344d 100644
--- a/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt
+++ b/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt
@@ -26,10 +26,13 @@ internal class DokkaTestGenerator(
val modulesFromPlatforms = dokkaGenerator.createDocumentationModels(platforms, context)
documentablesCreationStage(modulesFromPlatforms)
- val documentationModel = dokkaGenerator.mergeDocumentationModels(modulesFromPlatforms, context)
+ val filteredModules = dokkaGenerator.transformDocumentationModelBeforeMerge(modulesFromPlatforms, context)
+ documentablesFirstTransformationStep(filteredModules)
+
+ val documentationModel = dokkaGenerator.mergeDocumentationModels(filteredModules, context)
documentablesMergingStage(documentationModel)
- val transformedDocumentation = dokkaGenerator.transformDocumentationModel(documentationModel, context)
+ val transformedDocumentation = dokkaGenerator.transformDocumentationModelAfterMerge(documentationModel, context)
documentablesTransformationStage(transformedDocumentation)
val pages = dokkaGenerator.createPages(transformedDocumentation, context)
diff --git a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt
index a0e3b709..64d16fef 100644
--- a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt
+++ b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt
@@ -105,6 +105,7 @@ abstract class AbstractCoreTest {
var analysisSetupStage: (Map<PlatformData, EnvironmentAndFacade>) -> Unit = {}
var pluginsSetupStage: (DokkaContext) -> Unit = {}
var documentablesCreationStage: (List<DModule>) -> Unit = {}
+ var documentablesFirstTransformationStep: (List<DModule>) -> Unit = {}
var documentablesMergingStage: (DModule) -> Unit = {}
var documentablesTransformationStage: (DModule) -> Unit = {}
var pagesGenerationStage: (ModulePageNode) -> Unit = {}
@@ -115,6 +116,7 @@ abstract class AbstractCoreTest {
analysisSetupStage,
pluginsSetupStage,
documentablesCreationStage,
+ documentablesFirstTransformationStep,
documentablesMergingStage,
documentablesTransformationStage,
pagesGenerationStage,
@@ -217,6 +219,7 @@ data class TestMethods(
val analysisSetupStage: (Map<PlatformData, EnvironmentAndFacade>) -> Unit,
val pluginsSetupStage: (DokkaContext) -> Unit,
val documentablesCreationStage: (List<DModule>) -> Unit,
+ val documentablesFirstTransformationStep: (List<DModule>) -> Unit,
val documentablesMergingStage: (DModule) -> Unit,
val documentablesTransformationStage: (DModule) -> Unit,
val pagesGenerationStage: (ModulePageNode) -> Unit,