From c01e49eec9558736959d12820361624a3c3e41e5 Mon Sep 17 00:00:00 2001 From: Marcin Aman Date: Mon, 1 Mar 2021 11:57:30 +0100 Subject: Suppress tag support (#1742) * Suppress tag support * Support Hide tag in javadoc * Extract hide tag to be in separate plugin --- .../user_guide/android-plugin/android-plugin.md | 8 + docs/src/doc/docs/user_guide/introduction.md | 1 + docs/src/doc/mkdocs.yml | 1 + plugins/android-documentation/build.gradle.kts | 11 ++ .../src/main/kotlin/AndroidDocumentationPlugin.kt | 13 ++ .../transformers/HideTagDocumentableFilter.kt | 13 ++ .../org.jetbrains.dokka.plugability.DokkaPlugin | 1 + .../transformers/HideTagDocumentableFilterTest.kt | 71 ++++++++ plugins/base/src/main/kotlin/DokkaBase.kt | 13 +- .../ModuleAndPackageDocumentationReader.kt | 2 +- .../documentables/SuppressTagDocumentableFilter.kt | 12 ++ ...ByConfigurationDocumentableFilterTransformer.kt | 51 ++++++ ...SuppressedByTagDocumentableFilterTransformer.kt | 110 ++++++++++++ .../SuppressedDocumentableFilterTransformer.kt | 51 ------ .../translators/psi/parsers/InheritDocResolver.kt | 4 +- .../translators/psi/parsers/JavadocParser.kt | 17 +- .../kotlin/translators/psi/parsers/JavadocTag.kt | 2 +- .../kotlin/transformers/SuppressTagFilterTest.kt | 187 ++++++++++++++++++++ ...nfigurationDocumentableFilterTransformerTest.kt | 188 +++++++++++++++++++++ .../SuppressedDocumentableFilterTransformerTest.kt | 188 --------------------- settings.gradle.kts | 1 + 21 files changed, 696 insertions(+), 249 deletions(-) create mode 100644 docs/src/doc/docs/user_guide/android-plugin/android-plugin.md create mode 100644 plugins/android-documentation/build.gradle.kts create mode 100644 plugins/android-documentation/src/main/kotlin/AndroidDocumentationPlugin.kt create mode 100644 plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt create mode 100644 plugins/android-documentation/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin create mode 100644 plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt create mode 100644 plugins/base/src/main/kotlin/transformers/documentables/SuppressedByTagDocumentableFilterTransformer.kt delete mode 100644 plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt create mode 100644 plugins/base/src/test/kotlin/transformers/SuppressTagFilterTest.kt create mode 100644 plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt delete mode 100644 plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt diff --git a/docs/src/doc/docs/user_guide/android-plugin/android-plugin.md b/docs/src/doc/docs/user_guide/android-plugin/android-plugin.md new file mode 100644 index 00000000..75e3d927 --- /dev/null +++ b/docs/src/doc/docs/user_guide/android-plugin/android-plugin.md @@ -0,0 +1,8 @@ +# Android documentationn plugin + +Android documentation plugin aims to improve the documentation on android platform. + +### Features: + +* `@hide` support - `@hide` javadoc tag is an equivalent of `@suppress` tag in kdoc. It hides certain entry from being + displayed in the documentation. \ No newline at end of file diff --git a/docs/src/doc/docs/user_guide/introduction.md b/docs/src/doc/docs/user_guide/introduction.md index 0de6a5ef..cb263ebe 100644 --- a/docs/src/doc/docs/user_guide/introduction.md +++ b/docs/src/doc/docs/user_guide/introduction.md @@ -10,6 +10,7 @@ Currently maintained plugins are: * `jekyll-plugin` - configures `Jekyll` output format * `javadoc-plugin` - configures `Javadoc` output format, automatically applies `kotlin-as-java-plugin` * `kotlin-as-java-plugin` - translates Kotlin definitions to Java +* `android-documentation-plugin` - provides android specific enhancements like `@hide` support Please see the usage instructions for each build system on how to add plugins to Dokka. diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml index c58376e3..ac962baa 100644 --- a/docs/src/doc/mkdocs.yml +++ b/docs/src/doc/mkdocs.yml @@ -50,6 +50,7 @@ nav: - Maven: user_guide/maven/usage.md - Command line: user_guide/cli/usage.md - Html frontend: user_guide/base-specific/frontend.md + - Android plugin: user_guide/android-plugin/android-plugin.md - Plugin development: - Introduction: developer_guide/introduction.md - Data model: developer_guide/data_model.md diff --git a/plugins/android-documentation/build.gradle.kts b/plugins/android-documentation/build.gradle.kts new file mode 100644 index 00000000..79a89165 --- /dev/null +++ b/plugins/android-documentation/build.gradle.kts @@ -0,0 +1,11 @@ +import org.jetbrains.registerDokkaArtifactPublication + +dependencies { + implementation(project(":plugins:base")) + testImplementation(project(":plugins:base")) + testImplementation(project(":plugins:base:base-test-utils")) +} + +registerDokkaArtifactPublication("androidDocumentationPlugin") { + artifactId = "android-documentation-plugin" +} diff --git a/plugins/android-documentation/src/main/kotlin/AndroidDocumentationPlugin.kt b/plugins/android-documentation/src/main/kotlin/AndroidDocumentationPlugin.kt new file mode 100644 index 00000000..fd345802 --- /dev/null +++ b/plugins/android-documentation/src/main/kotlin/AndroidDocumentationPlugin.kt @@ -0,0 +1,13 @@ +package org.jetbrains.dokka.android + +import org.jetbrains.dokka.android.transformers.HideTagDocumentableFilter +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.plugability.DokkaPlugin + +class AndroidDocumentationPlugin : DokkaPlugin() { + private val dokkaBase by lazy { plugin() } + + val suppressedByHideTagDocumentableFilter by extending { + dokkaBase.preMergeDocumentableTransformer providing ::HideTagDocumentableFilter order { before(dokkaBase.emptyPackagesFilter) } + } +} \ No newline at end of file diff --git a/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt b/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt new file mode 100644 index 00000000..074413cc --- /dev/null +++ b/plugins/android-documentation/src/main/kotlin/transformers/HideTagDocumentableFilter.kt @@ -0,0 +1,13 @@ +package org.jetbrains.dokka.android.transformers + +import org.jetbrains.dokka.base.transformers.documentables.SuppressedByTagDocumentableFilterTransformer +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.model.doc.CustomTagWrapper +import org.jetbrains.dokka.plugability.DokkaContext + +class HideTagDocumentableFilter(val dokkaContext: DokkaContext) : + SuppressedByTagDocumentableFilterTransformer(dokkaContext) { + override fun shouldBeSuppressed(d: Documentable): Boolean = + d.documentation.any { (_, docs) -> docs.dfs { it is CustomTagWrapper && it.name.trim() == "hide" } != null } +} \ No newline at end of file diff --git a/plugins/android-documentation/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/android-documentation/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..ee7f92e0 --- /dev/null +++ b/plugins/android-documentation/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.android.AndroidDocumentationPlugin diff --git a/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt b/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt new file mode 100644 index 00000000..01a802bf --- /dev/null +++ b/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt @@ -0,0 +1,71 @@ +package transformers + +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.model.DClass +import kotlin.test.assertEquals +import org.junit.jupiter.api.Test + +class HideTagDocumentableFilterTest : BaseAbstractTest() { + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + } + } + } + + + @Test + fun `should work as hide in java with functions`() { + testInline( + """ + |/src/suppressed/Testing.java + |package testing; + | + |public class Testing { + | /** + | * @hide + | */ + | public void shouldNotBeVisible() { } + |} + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + val testingClass = modules.flatMap { it.packages }.flatMap { it.classlikes }.single() as DClass + assertEquals(0, testingClass.functions.size) + } + } + } + + @Test + fun `should work as hide in java with classes`() { + testInline( + """ + |/src/suppressed/Suppressed.java + |package testing; + | + |/** + | * @hide + | */ + |public class Suppressed { + |} + |/src/suppressed/Visible.java + |package testing; + | + |/** + | * Another docs + | * @undeprecate + | */ + |public class Visible { + |} + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + val classes = modules.flatMap { it.packages }.flatMap { it.classlikes }.map { it.name } + assertEquals(listOf("Visible"), classes) + } + } + } + + +} \ 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 4da2cb6e..2dbad3a7 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -67,7 +67,11 @@ class DokkaBase : DokkaPlugin() { } val suppressedDocumentableFilter by extending { - preMergeDocumentableTransformer providing ::SuppressedDocumentableFilterTransformer + preMergeDocumentableTransformer providing ::SuppressedByConfigurationDocumentableFilterTransformer + } + + val suppressedBySuppressTagDocumentableFilter by extending { + preMergeDocumentableTransformer providing ::SuppressTagDocumentableFilter } val documentableVisbilityFilter by extending { @@ -76,7 +80,12 @@ class DokkaBase : DokkaPlugin() { val emptyPackagesFilter by extending { preMergeDocumentableTransformer providing ::EmptyPackagesFilterTransformer order { - after(deprecatedDocumentableFilter, suppressedDocumentableFilter, documentableVisbilityFilter) + after( + deprecatedDocumentableFilter, + suppressedDocumentableFilter, + documentableVisbilityFilter, + suppressedBySuppressTagDocumentableFilter + ) } } diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationReader.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationReader.kt index b74242c3..37903988 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationReader.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationReader.kt @@ -45,7 +45,7 @@ private class ContextModuleAndPackageDocumentationReader( ): SourceSetDependent { return sourceSets.associateWithNotNull { sourceSet -> val fragments = documentationFragments[sourceSet].orEmpty().filter(predicate) - val resolutionFacade = kotlinAnalysis?.get(sourceSet)?.facade + val resolutionFacade = kotlinAnalysis[sourceSet].facade val documentations = fragments.map { fragment -> parseModuleAndPackageDocumentation( context = ModuleAndPackageDocumentationParsingContext(context.logger, resolutionFacade), diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt new file mode 100644 index 00000000..7efb23ab --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.model.doc.Suppress + +class SuppressTagDocumentableFilter(val dokkaContext: DokkaContext) : + SuppressedByTagDocumentableFilterTransformer(dokkaContext) { + override fun shouldBeSuppressed(d: Documentable): Boolean = + d.documentation.any { (_, docs) -> docs.dfs { it is Suppress } != null } +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt new file mode 100644 index 00000000..a013bfee --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt @@ -0,0 +1,51 @@ +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 + +class SuppressedByConfigurationDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List): List { + 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(), + classlikes = filteredChildren.filterIsInstance(), + typealiases = filteredChildren.filterIsInstance(), + properties = filteredChildren.filterIsInstance() + ) + } + } + + 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/SuppressedByTagDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByTagDocumentableFilterTransformer.kt new file mode 100644 index 00000000..f40abe70 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByTagDocumentableFilterTransformer.kt @@ -0,0 +1,110 @@ +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 + +abstract class SuppressedByTagDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List): List = + modules.map { module -> + val (documentable, wasChanged) = processModule(module) + documentable.takeIf { wasChanged } ?: module + } + + abstract fun shouldBeSuppressed(d: Documentable): Boolean + + private fun processModule(module: DModule): DocumentableWithChanges { + 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 { + 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 { processMember(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 { + 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 { processMember(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 { processMember(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 processMember(member: T): DocumentableWithChanges = + if (shouldBeSuppressed(member)) DocumentableWithChanges.filteredDocumentable() + else DocumentableWithChanges(member, false) + + private data class DocumentableWithChanges(val documentable: T?, val changed: Boolean = false) { + companion object { + fun filteredDocumentable(): DocumentableWithChanges = + DocumentableWithChanges(null, true) + } + } +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt deleted file mode 100644 index b066e69b..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt +++ /dev/null @@ -1,51 +0,0 @@ -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 - -class SuppressedDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { - override fun invoke(modules: List): List { - 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(), - classlikes = filteredChildren.filterIsInstance(), - typealiases = filteredChildren.filterIsInstance(), - properties = filteredChildren.filterIsInstance() - ) - } - } - - 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/translators/psi/parsers/InheritDocResolver.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt index c2fb6fb4..21c2c72a 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull internal data class CommentResolutionContext( val comment: PsiDocComment, - val tag: JavadocTag, + val tag: JavadocTag?, val name: String? = null, val parameterIndex: Int? = null, ) @@ -25,7 +25,7 @@ internal class InheritDocResolver( JavadocTag.PARAM -> context.parameterIndex?.let { paramIndex -> resolveParamTag(context.comment, paramIndex) } JavadocTag.DEPRECATED -> resolveGenericTag(context.comment, JavadocTag.DESCRIPTION) JavadocTag.SEE -> emptyList() - else -> resolveGenericTag(context.comment, context.tag) + else -> context.tag?.let { tag -> resolveGenericTag(context.comment, tag) } } private fun resolveGenericTag(currentElement: PsiDocComment, tag: JavadocTag): List = diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt index 96c62b36..a02bceac 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt @@ -12,6 +12,7 @@ import org.jetbrains.dokka.analysis.from import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.doc.Deprecated +import org.jetbrains.dokka.model.doc.Suppress import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.utilities.enumValueOrNull import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName @@ -118,7 +119,15 @@ class JavadocParser( else -> null //TODO https://github.com/Kotlin/dokka/issues/1618 } - } + } ?: CustomTagWrapper( + wrapTagIfNecessary( + convertJavadocElements( + tag.contentElementsWithSiblingIfNeeded(), + context = CommentResolutionContext(docComment, null) + ) + ), + tag.name + ) private fun wrapTagIfNecessary(list: List): CustomDocTag = if (list.size == 1 && (list.first() as? CustomDocTag)?.name == MarkdownElementTypes.MARKDOWN_FILE.name) @@ -148,14 +157,14 @@ class JavadocParser( } private data class ParserState( - val currentJavadocTag: JavadocTag, + val currentJavadocTag: JavadocTag?, val previousElement: PsiElement? = null, val openPreTags: Int = 0, val closedPreTags: Int = 0 ) private data class ParsingResult(val newState: ParserState, val parsedLine: String? = null) { - constructor(tag: JavadocTag) : this(ParserState(tag)) + constructor(tag: JavadocTag?) : this(ParserState(tag)) operator fun plus(other: ParsingResult): ParsingResult = ParsingResult( @@ -267,7 +276,7 @@ class JavadocParser( private fun convertInlineDocTag( tag: PsiInlineDocTag, - javadocTag: JavadocTag, + javadocTag: JavadocTag?, context: CommentResolutionContext ) = when (tag.name) { diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt index 8ea39453..869ced30 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt @@ -1,7 +1,7 @@ package org.jetbrains.dokka.base.translators.psi.parsers internal enum class JavadocTag { - PARAM, THROWS, RETURN, AUTHOR, SEE, DEPRECATED, EXCEPTION, + PARAM, THROWS, RETURN, AUTHOR, SEE, DEPRECATED, EXCEPTION, HIDE, /** * Artificial tag created to handle tag-less section diff --git a/plugins/base/src/test/kotlin/transformers/SuppressTagFilterTest.kt b/plugins/base/src/test/kotlin/transformers/SuppressTagFilterTest.kt new file mode 100644 index 00000000..268f934d --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/SuppressTagFilterTest.kt @@ -0,0 +1,187 @@ +package transformers + +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.WithCompanion +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class SuppressTagFilterTest : BaseAbstractTest() { + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + } + } + } + + @Test + fun `should filter classes with suppress tag`() { + testInline( + """ + |/src/suppressed/NotSuppressed.kt + |/** + | * sample docs + |*/ + |class NotSuppressed + |/src/suppressed/Suppressed.kt + |/** + | * sample docs + | * @suppress + |*/ + |class Suppressed + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + assertEquals( + "NotSuppressed", + modules.flatMap { it.packages }.flatMap { it.classlikes }.singleOrNull()?.name + ) + } + } + } + + @Test + fun `should filter functions with suppress tag`() { + testInline( + """ + |/src/suppressed/Suppressed.kt + |class Suppressed { + | /** + | * sample docs + | * @suppress + | */ + | fun suppressedFun(){ } + |} + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + assertNull(modules.flatMap { it.packages }.flatMap { it.classlikes }.flatMap { it.functions } + .firstOrNull { it.name == "suppressedFun" }) + } + } + } + + @Test + fun `should filter top level functions`() { + testInline( + """ + |/src/suppressed/Suppressed.kt + |/** + | * sample docs + | * @suppress + | */ + |fun suppressedFun(){ } + | + |/** + | * Sample + | */ + |fun notSuppressedFun() { } + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + assertNull(modules.flatMap { it.packages }.flatMap { it.functions } + .firstOrNull { it.name == "suppressedFun" }) + } + } + } + + @Test + fun `should filter top level type aliases`() { + testInline( + """ + |/src/suppressed/suppressed.kt + |/** + | * sample docs + | * @suppress + | */ + |typealias suppressedTypeAlias = String + | + |/** + | * Sample + | */ + |typealias notSuppressedTypeAlias = String + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + assertNull(modules.flatMap { it.packages }.flatMap { it.typealiases } + .firstOrNull { it.name == "suppressedTypeAlias" }) + assertNotNull(modules.flatMap { it.packages }.flatMap { it.typealiases } + .firstOrNull { it.name == "notSuppressedTypeAlias" }) + } + } + } + + @Test + fun `should filter companion object`() { + testInline( + """ + |/src/suppressed/Suppressed.kt + |class Suppressed { + |/** + | * @suppress + | */ + |companion object { + | val x = 1 + |} + |} + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + assertNull((modules.flatMap { it.packages }.flatMap { it.classlikes } + .firstOrNull { it.name == "Suppressed" } as? WithCompanion)?.companion) + } + } + } + + @Test + fun `should suppress inner classlike`() { + testInline( + """ + |/src/suppressed/Testing.kt + |class Testing { + | /** + | * Sample + | * @suppress + | */ + | inner class Suppressed { + | val x = 1 + | } + |} + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + val testingClass = modules.flatMap { it.packages }.flatMap { it.classlikes }.single() + assertNull(testingClass.classlikes.firstOrNull()) + } + } + } + + @Test + fun `should suppress enum entry`() { + testInline( + """ + |/src/suppressed/Testing.kt + |enum class Testing { + | /** + | * Sample + | * @suppress + | */ + | SUPPRESSED, + | + | /** + | * Not suppressed + | */ + | NOT_SUPPRESSED + |} + """.trimIndent(), configuration + ) { + documentablesFirstTransformationStep = { modules -> + val testingClass = modules.flatMap { it.packages }.flatMap { it.classlikes }.single() as DEnum + assertEquals(listOf("NOT_SUPPRESSED"), testingClass.entries.map { it.name }) + } + } + } +} \ No newline at end of file diff --git a/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt new file mode 100644 index 00000000..c6d46b1f --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt @@ -0,0 +1,188 @@ +package transformers + +import org.jetbrains.dokka.PackageOptionsImpl +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertIterableEquals +import org.junit.jupiter.api.Test + +class SuppressedByConfigurationDocumentableFilterTransformerTest : BaseAbstractTest() { + + @Test + fun `class filtered by package options`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + perPackageOptions = listOf( + packageOptions(matchingRegex = "suppressed.*", suppress = true), + packageOptions(matchingRegex = "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(matchingRegex = "parent.some.*", suppress = false), + packageOptions(matchingRegex = "parent.some.suppressed.*", suppress = true), + + packageOptions(matchingRegex = "parent.other.*", suppress = true), + packageOptions(matchingRegex = "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( + matchingRegex: String, + suppress: Boolean + ) = PackageOptionsImpl( + matchingRegex = matchingRegex, + suppress = suppress, + includeNonPublic = true, + reportUndocumented = false, + skipDeprecated = false + ) + +} diff --git a/plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt deleted file mode 100644 index a022458a..00000000 --- a/plugins/base/src/test/kotlin/transformers/SuppressedDocumentableFilterTransformerTest.kt +++ /dev/null @@ -1,188 +0,0 @@ -package transformers - -import org.jetbrains.dokka.PackageOptionsImpl -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertIterableEquals -import org.junit.jupiter.api.Test - -class SuppressedDocumentableFilterTransformerTest : BaseAbstractTest() { - - @Test - fun `class filtered by package options`() { - val configuration = dokkaConfiguration { - sourceSets { - sourceSet { - sourceRoots = listOf("src") - perPackageOptions = listOf( - packageOptions(matchingRegex = "suppressed.*", suppress = true), - packageOptions(matchingRegex = "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(matchingRegex = "parent.some.*", suppress = false), - packageOptions(matchingRegex = "parent.some.suppressed.*", suppress = true), - - packageOptions(matchingRegex = "parent.other.*", suppress = true), - packageOptions(matchingRegex = "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( - matchingRegex: String, - suppress: Boolean - ) = PackageOptionsImpl( - matchingRegex = matchingRegex, - suppress = suppress, - includeNonPublic = true, - reportUndocumented = false, - skipDeprecated = false - ) - -} diff --git a/settings.gradle.kts b/settings.gradle.kts index b9433391..7b0e5f62 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ include("plugins:base:base-test-utils") include("plugins:all-modules-page") include("plugins:templating") include("plugins:versioning") +include("plugins:android-documentation") include("plugins:mathjax") include("plugins:gfm") -- cgit