diff options
12 files changed, 308 insertions, 5 deletions
diff --git a/.run/it-basic_dokka.run.xml b/.run/it-basic_dokka.run.xml index 42d6ca63..7a9b42bd 100644 --- a/.run/it-basic_dokka.run.xml +++ b/.run/it-basic_dokka.run.xml @@ -11,7 +11,7 @@ <option name="taskNames"> <list> <option value="clean" /> - <option value="dokka" /> + <option value="dokkaHtml" /> </list> </option> <option name="vmOptions" value="" /> diff --git a/integration-tests/gradle/projects/it-basic/build.gradle.kts b/integration-tests/gradle/projects/it-basic/build.gradle.kts index 1de92418..b3ddde18 100644 --- a/integration-tests/gradle/projects/it-basic/build.gradle.kts +++ b/integration-tests/gradle/projects/it-basic/build.gradle.kts @@ -15,6 +15,11 @@ tasks.withType<DokkaTask> { dokkaSourceSets { configureEach { moduleDisplayName = "Basic Project" + suppressedFiles = listOf("src/main/kotlin/it/suppressedByPath") + perPackageOption { + prefix = "it.suppressedByPackage" + suppress = true + } } } } diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt new file mode 100644 index 00000000..7d42b978 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt @@ -0,0 +1,7 @@ +package it.internal + +/** + * §INTERNAL§ + * This class is internal and should not be rendered + */ +internal class InternalClass diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt new file mode 100644 index 00000000..d8dc9cff --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt @@ -0,0 +1,7 @@ +package it.suppressedByPackage + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPackage diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt new file mode 100644 index 00000000..4dda9da4 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt @@ -0,0 +1,7 @@ +package it.suppressedByPath + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPath diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt index 54021e81..46577e81 100644 --- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt @@ -77,6 +77,7 @@ class BasicGradleIntegrationTest(override val versions: BuildVersions) : Abstrac assertContainsNoErrorClass(file) assertNoUnresolvedLinks(file) assertNoHrefToMissingLocalFileOrDirectory(file) + assertNoSuppressedMarker(file) } assertTrue( diff --git a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt index d2e3c980..c4914652 100644 --- a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt +++ b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt @@ -68,4 +68,12 @@ abstract class AbstractIntegrationTest { } } } + + protected fun assertNoSuppressedMarker(file: File) { + val fileText = file.readText() + assertFalse( + fileText.contains("§SUPPRESSED§"), + "Unexpected `§SUPPRESSED§` in file ${file.path}" + ) + } } diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 6586f2dc..20769d07 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -64,13 +64,17 @@ class DokkaBase : DokkaPlugin() { CoreExtensions.preMergeDocumentableTransformer providing ::DeprecatedDocumentableFilterTransformer } + val suppressedDocumentableFilter by extending { + CoreExtensions.preMergeDocumentableTransformer providing ::SuppressedDocumentableFilterTransformer + } + val documentableVisbilityFilter by extending { CoreExtensions.preMergeDocumentableTransformer providing ::DocumentableVisibilityFilterTransformer } val emptyPackagesFilter by extending { CoreExtensions.preMergeDocumentableTransformer providing ::EmptyPackagesFilterTransformer order { - after(deprecatedDocumentableFilter, documentableVisbilityFilter) + after(deprecatedDocumentableFilter, suppressedDocumentableFilter, documentableVisbilityFilter) } } diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt new file mode 100644 index 00000000..e7a2bf25 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt @@ -0,0 +1,68 @@ +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.transformers.documentation.PreMergeDocumentableTransformer +import java.io.File + +class SuppressedDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List<DModule>): List<DModule> { + return modules.mapNotNull { module -> filterModule(module) } + } + + 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 = pkg.perPackageOptions + if (options?.suppress == true) { + return null + } + + val filteredChildren = pkg.children.filter { child -> !isSuppressed(child) } + 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 WithExpectActual) return false + val sourceFile = File(documentable.source.path).absoluteFile + return documentable.sourceSet.suppressedFiles.any { suppressedFile -> + sourceFile.startsWith(File(suppressedFile).absoluteFile) + } + } + + /** + * A [PreMergeDocumentableTransformer] can safely assume that documentables are not merged and therefore + * only belong to a single source set + */ + private val Documentable.sourceSet: DokkaSourceSet get() = sourceSets.single() + + private val Documentable.perPackageOptions: DokkaConfiguration.PackageOptions? + get() { + val packageName = dri.packageName ?: return null + return sourceSet.perPackageOptions + .sortedByDescending { packageOptions -> packageOptions.prefix.length } + .firstOrNull { packageOptions -> packageName.startsWith(packageOptions.prefix) } + } + + private val <T> T.source: DocumentableSource where T : Documentable, T : WithExpectActual + get() = checkNotNull(sources[sourceSet]) + +} diff --git a/plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt new file mode 100644 index 00000000..93b36d2e --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt @@ -0,0 +1,188 @@ +package transformers + +import org.jetbrains.dokka.PackageOptionsImpl +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertIterableEquals +import org.junit.jupiter.api.Test + +class SuppressedDocumentableFilterTransformerTest : AbstractCoreTest() { + + @Test + fun `class filtered by package options`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + perPackageOptions = listOf( + packageOptions(prefix = "suppressed", suppress = true), + packageOptions(prefix = "default", suppress = false) + ) + } + } + } + + testInline( + """ + /src/suppressed/Suppressed.kt + package suppressed + class Suppressed + + /src/default/Default.kt + package default + class Default.kt + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + assertEquals(1, module.children.size, "Expected just a single package in module") + assertEquals(1, module.packages.size, "Expected just a single package in module") + + val pkg = module.packages.single() + assertEquals("default", pkg.dri.packageName, "Expected 'default' package in module") + assertEquals(1, pkg.children.size, "Expected just a single child in 'default' package") + assertEquals(1, pkg.classlikes.size, "Expected just a single child in 'default' package") + + val classlike = pkg.classlikes.single() + assertEquals(DRI("default", "Default"), classlike.dri, "Expected 'Default' class in 'default' package") + } + } + } + + @Test + fun `class filtered by more specific package options`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + perPackageOptions = listOf( + packageOptions(prefix = "parent.some", suppress = false), + packageOptions(prefix = "parent.some.suppressed", suppress = true), + + packageOptions(prefix = "parent.other", suppress = true), + packageOptions(prefix = "parent.other.default", suppress = false) + ) + } + } + } + + testInline( + """ + /src/parent/some/Some.kt + package parent.some + class Some + + /src/parent/some/suppressed/Suppressed.kt + package parent.some.suppressed + class Suppressed + + /src/parent/other/Other.kt + package parent.other + class Other + + /src/parent/other/default/Default.kt + package parent.other.default + class Default + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + assertEquals(2, module.packages.size, "Expected two packages in module") + assertIterableEquals( + listOf(DRI("parent.some"), DRI("parent.other.default")).sortedBy { it.packageName }, + module.packages.map { it.dri }.sortedBy { it.packageName }, + "Expected 'parent.some' and 'parent.other.default' packages to be not suppressed" + ) + } + } + } + + @Test + fun `class filtered by parent file path`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + suppressedFiles = listOf("src/suppressed") + } + } + } + + testInline( + """ + /src/suppressed/Suppressed.kt + package suppressed + class Suppressed + + /src/default/Default.kt + package default + class Default.kt + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + assertEquals(1, module.children.size, "Expected just a single package in module") + assertEquals(1, module.packages.size, "Expected just a single package in module") + + val pkg = module.packages.single() + assertEquals("default", pkg.dri.packageName, "Expected 'default' package in module") + assertEquals(1, pkg.children.size, "Expected just a single child in 'default' package") + assertEquals(1, pkg.classlikes.size, "Expected just a single child in 'default' package") + + val classlike = pkg.classlikes.single() + assertEquals(DRI("default", "Default"), classlike.dri, "Expected 'Default' class in 'default' package") + } + } + } + + @Test + fun `class filtered by exact file path`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + suppressedFiles = listOf("src/suppressed/Suppressed.kt") + } + } + } + + testInline( + """ + /src/suppressed/Suppressed.kt + package suppressed + class Suppressed + + /src/default/Default.kt + package default + class Default.kt + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + assertEquals(1, module.children.size, "Expected just a single package in module") + assertEquals(1, module.packages.size, "Expected just a single package in module") + + val pkg = module.packages.single() + assertEquals("default", pkg.dri.packageName, "Expected 'default' package in module") + assertEquals(1, pkg.children.size, "Expected just a single child in 'default' package") + assertEquals(1, pkg.classlikes.size, "Expected just a single child in 'default' package") + + val classlike = pkg.classlikes.single() + assertEquals(DRI("default", "Default"), classlike.dri, "Expected 'Default' class in 'default' package") + } + } + } + + private fun packageOptions( + prefix: String, + suppress: Boolean + ) = PackageOptionsImpl( + prefix = prefix, + suppress = suppress, + includeNonPublic = true, + reportUndocumented = false, + skipDeprecated = false + ) + +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt index f5b66cdb..b4601acf 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt @@ -265,6 +265,7 @@ open class DokkaTask : AbstractDokkaTask() { config.samples = config.samples.map { project.file(it).absolutePath } config.includes = config.includes.map { project.file(it).absolutePath } config.suppressedFiles += collectSuppressedFiles(config.sourceRoots) + config.suppressedFiles = config.suppressedFiles.map { project.file(it).absolutePath } return config } diff --git a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt index 2d79e7e5..25b6b6f2 100644 --- a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt +++ b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt @@ -63,9 +63,16 @@ abstract class AbstractCoreTest { val newConfiguration = configuration.copy( outputDir = testDirPath.toAbsolutePath().toString(), - sourceSets = configuration.sourceSets.map { - it.copy(sourceRoots = it.sourceRoots.map { it.copy(path = "${testDirPath.toAbsolutePath()}/${it.path}") }) - } + sourceSets = configuration.sourceSets.map { sourceSet -> + sourceSet.copy( + sourceRoots = sourceSet.sourceRoots.map { sourceRoot -> + sourceRoot.copy(path = "${testDirPath.toAbsolutePath()}/${sourceRoot.path}") + }, + suppressedFiles = sourceSet.suppressedFiles.map { suppressedFile -> + testDirPath.toAbsolutePath().toFile().resolve(suppressedFile).absolutePath + } + ) + }, ) DokkaTestGenerator( newConfiguration, |