diff options
Diffstat (limited to 'plugins')
14 files changed, 969 insertions, 24 deletions
diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index 9cd15875..10d2c564 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -28,6 +28,7 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final fun getInheritorsExtractor ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getJavadocLocationProvider ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getKotlinAnalysis ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKotlinArrayDocumentableReplacer ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getKotlinSignatureProvider ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getLocationProvider ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getLocationProviderFactory ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; @@ -1020,6 +1021,40 @@ public final class org/jetbrains/dokka/base/transformers/documentables/Deprecate public fun invoke (Ljava/util/List;)Ljava/util/List; } +public abstract class org/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer : org/jetbrains/dokka/transformers/documentation/PreMergeDocumentableTransformer { + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; + public fun invoke (Ljava/util/List;)Ljava/util/List; + protected fun processBound (Lorg/jetbrains/dokka/model/Bound;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processClassLike (Lorg/jetbrains/dokka/model/DClasslike;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processEnumEntry (Lorg/jetbrains/dokka/model/DEnumEntry;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected final fun processFunction (Lorg/jetbrains/dokka/model/DFunction;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processFunctionalTypeConstructor (Lorg/jetbrains/dokka/model/FunctionalTypeConstructor;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processGenericTypeConstructor (Lorg/jetbrains/dokka/model/GenericTypeConstructor;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processModule (Lorg/jetbrains/dokka/model/DModule;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processPackage (Lorg/jetbrains/dokka/model/DPackage;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processParameter (Lorg/jetbrains/dokka/model/DParameter;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processProjection (Lorg/jetbrains/dokka/model/Projection;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processProperty (Lorg/jetbrains/dokka/model/DProperty;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processTypeAlias (Lorg/jetbrains/dokka/model/DTypeAlias;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processTypeParameter (Lorg/jetbrains/dokka/model/DTypeParameter;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + protected fun processVariance (Lorg/jetbrains/dokka/model/Variance;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; +} + +protected final class org/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges { + public fun <init> (Ljava/lang/Object;Z)V + public synthetic fun <init> (Ljava/lang/Object;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Object; + public final fun component2 ()Z + public final fun copy (Ljava/lang/Object;Z)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges;Ljava/lang/Object;ZILjava/lang/Object;)Lorg/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer$AnyWithChanges; + public fun equals (Ljava/lang/Object;)Z + public final fun getChanged ()Z + public final fun getTarget ()Ljava/lang/Object; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class org/jetbrains/dokka/base/transformers/documentables/DocumentableVisibilityFilterTransformer : org/jetbrains/dokka/transformers/documentation/PreMergeDocumentableTransformer { public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; @@ -1068,6 +1103,10 @@ public final class org/jetbrains/dokka/base/transformers/documentables/Inheritor public fun mergeStrategyFor (Lorg/jetbrains/dokka/base/transformers/documentables/InheritorsInfo;Lorg/jetbrains/dokka/base/transformers/documentables/InheritorsInfo;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; } +public final class org/jetbrains/dokka/base/transformers/documentables/KotlinArrayDocumentableReplacerTransformer : org/jetbrains/dokka/base/transformers/documentables/DocumentableReplacerTransformer { + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V +} + public final class org/jetbrains/dokka/base/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer : org/jetbrains/dokka/base/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer { public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun shouldBeSuppressed (Lorg/jetbrains/dokka/model/Documentable;)Z @@ -1216,7 +1255,8 @@ public final class org/jetbrains/dokka/base/translators/descriptors/DefaultDescr } public final class org/jetbrains/dokka/base/translators/documentables/BriefFromContentNodesKt { - public static final fun briefFromContentNodes (Ljava/util/List;)Ljava/util/List; + public static final fun firstParagraphBrief (Lorg/jetbrains/dokka/model/doc/DocTag;)Lorg/jetbrains/dokka/model/doc/DocTag; + public static final fun firstSentenceBriefFromContentNodes (Ljava/util/List;)Ljava/util/List; } public abstract interface annotation class org/jetbrains/dokka/base/translators/documentables/ContentBuilderMarker : java/lang/annotation/Annotation { @@ -1324,6 +1364,8 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild protected final fun createText (Ljava/lang/String;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/pages/ContentText; public final fun divergentGroup (Lorg/jetbrains/dokka/pages/ContentDivergentGroup$GroupID;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZLkotlin/jvm/functions/Function1;)V public static synthetic fun divergentGroup$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/pages/ContentDivergentGroup$GroupID;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public final fun firstParagraphComment (Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V + public static synthetic fun firstParagraphComment$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)V public final fun firstSentenceComment (Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V public static synthetic fun firstSentenceComment$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)V protected final fun getContents ()Ljava/util/List; diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 7bc2912c..7e88d08f 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -86,6 +86,10 @@ class DokkaBase : DokkaPlugin() { preMergeDocumentableTransformer providing ::InheritedEntriesDocumentableFilterTransformer } + val kotlinArrayDocumentableReplacer by extending { + preMergeDocumentableTransformer providing ::KotlinArrayDocumentableReplacerTransformer + } + val emptyPackagesFilter by extending { preMergeDocumentableTransformer providing ::EmptyPackagesFilterTransformer order { after( diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt new file mode 100644 index 00000000..f5ef8ed1 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt @@ -0,0 +1,228 @@ +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 DocumentableReplacerTransformer(val context: DokkaContext) : + PreMergeDocumentableTransformer { + override fun invoke(modules: List<DModule>): List<DModule> = + modules.map { module -> + val (documentable, wasChanged) = processModule(module) + documentable.takeIf { wasChanged } ?: module + } + + protected open fun processModule(module: DModule): AnyWithChanges<DModule> { + val afterProcessing = module.packages.map { processPackage(it) } + val processedModule = module.takeIf { afterProcessing.none { it.changed } } + ?: module.copy(packages = afterProcessing.mapNotNull { it.target }) + return AnyWithChanges(processedModule, afterProcessing.any { it.changed }) + } + + protected open fun processPackage(dPackage: DPackage): AnyWithChanges<DPackage> { + val classlikes = dPackage.classlikes.map { processClassLike(it) } + val typeAliases = dPackage.typealiases.map { processTypeAlias(it) } + val functions = dPackage.functions.map { processFunction(it) } + val properies = dPackage.properties.map { processProperty(it) } + + val wasChanged = (classlikes + typeAliases + functions + properies).any { it.changed } + return (dPackage.takeIf { !wasChanged } ?: dPackage.copy( + classlikes = classlikes.mapNotNull { it.target }, + typealiases = typeAliases.mapNotNull { it.target }, + functions = functions.mapNotNull { it.target }, + properties = properies.mapNotNull { it.target } + )).let { processedPackage -> AnyWithChanges(processedPackage, wasChanged) } + } + + protected open fun processClassLike(classlike: DClasslike): AnyWithChanges<DClasslike> { + val functions = classlike.functions.map { processFunction(it) } + val classlikes = classlike.classlikes.map { processClassLike(it) } + val properties = classlike.properties.map { processProperty(it) } + val companion = (classlike as? WithCompanion)?.companion?.let { processClassLike(it) } + + val wasClasslikeChanged = (functions + classlikes + properties).any { it.changed } || companion?.changed == true + return when (classlike) { + is DClass -> { + val constructors = classlike.constructors.map { processFunction(it) } + val generics = classlike.generics.map { processTypeParameter(it) } + val wasClassChange = + wasClasslikeChanged || constructors.any { it.changed } || generics.any { it.changed } + (classlike.takeIf { !wasClassChange } ?: classlike.copy( + functions = functions.mapNotNull { it.target }, + classlikes = classlikes.mapNotNull { it.target }, + properties = properties.mapNotNull { it.target }, + constructors = constructors.mapNotNull { it.target }, + generics = generics.mapNotNull { it.target }, + companion = companion?.target as? DObject + )).let { AnyWithChanges(it, wasClassChange) } + } + is DInterface -> { + val generics = classlike.generics.map { processTypeParameter(it) } + val wasInterfaceChange = wasClasslikeChanged || generics.any { it.changed } + (classlike.takeIf { !wasInterfaceChange } ?: classlike.copy( + functions = functions.mapNotNull { it.target }, + classlikes = classlikes.mapNotNull { it.target }, + properties = properties.mapNotNull { it.target }, + generics = generics.mapNotNull { it.target }, + companion = companion?.target as? DObject + )).let { AnyWithChanges(it, wasClasslikeChanged) } + } + is DObject -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( + functions = functions.mapNotNull { it.target }, + classlikes = classlikes.mapNotNull { it.target }, + properties = properties.mapNotNull { it.target }, + )).let { AnyWithChanges(it, wasClasslikeChanged) } + is DAnnotation -> { + val constructors = classlike.constructors.map { processFunction(it) } + val generics = classlike.generics.map { processTypeParameter(it) } + val wasClassChange = + wasClasslikeChanged || constructors.any { it.changed } || generics.any { it.changed } + (classlike.takeIf { !wasClassChange } ?: classlike.copy( + functions = functions.mapNotNull { it.target }, + classlikes = classlikes.mapNotNull { it.target }, + properties = properties.mapNotNull { it.target }, + constructors = constructors.mapNotNull { it.target }, + generics = generics.mapNotNull { it.target }, + companion = companion?.target as? DObject + )).let { AnyWithChanges(it, wasClassChange) } + } + is DEnum -> { + val constructors = classlike.constructors.map { processFunction(it) } + val entries = classlike.entries.map { processEnumEntry(it) } + val wasClassChange = + wasClasslikeChanged || (constructors + entries).any { it.changed } + (classlike.takeIf { !wasClassChange } ?: classlike.copy( + functions = functions.mapNotNull { it.target }, + classlikes = classlikes.mapNotNull { it.target }, + properties = properties.mapNotNull { it.target }, + constructors = constructors.mapNotNull { it.target }, + companion = companion?.target as? DObject, + entries = entries.mapNotNull { it.target } + )).let { AnyWithChanges(it, wasClassChange) } + } + } + } + + protected open fun processEnumEntry(dEnumEntry: DEnumEntry): AnyWithChanges<DEnumEntry> { + val functions = dEnumEntry.functions.map { processFunction(it) } + val properties = dEnumEntry.properties.map { processProperty(it) } + val classlikes = dEnumEntry.classlikes.map { processClassLike(it) } + + val wasChanged = (functions + properties + classlikes).any { it.changed } + return (dEnumEntry.takeIf { !wasChanged } ?: dEnumEntry.copy( + functions = functions.mapNotNull { it.target }, + classlikes = classlikes.mapNotNull { it.target }, + properties = properties.mapNotNull { it.target }, + )).let { AnyWithChanges(it, wasChanged) } + } + + protected fun processFunction(dFunction: DFunction): AnyWithChanges<DFunction> { + val type = processBound(dFunction.type) + val parameters = dFunction.parameters.map { processParameter(it) } + val receiver = dFunction.receiver?.let { processParameter(it) } + val generics = dFunction.generics.map { processTypeParameter(it) } + + val wasChanged = parameters.any { it.changed } || receiver?.changed == true + || type.changed || generics.any { it.changed } + return (dFunction.takeIf { !wasChanged } ?: dFunction.copy( + type = type.target ?: dFunction.type, + parameters = parameters.mapNotNull { it.target }, + receiver = receiver?.target, + generics = generics.mapNotNull { it.target }, + )).let { AnyWithChanges(it, wasChanged) } + } + + protected open fun processProperty(dProperty: DProperty): AnyWithChanges<DProperty> { + val getter = dProperty.getter?.let { processFunction(it) } + val setter = dProperty.setter?.let { processFunction(it) } + val type = processBound(dProperty.type) + val generics = dProperty.generics.map { processTypeParameter(it) } + + val wasChanged = getter?.changed == true || setter?.changed == true + || type.changed || generics.any { it.changed } + return (dProperty.takeIf { !wasChanged } ?: dProperty.copy( + type = type.target ?: dProperty.type, + setter = setter?.target, + getter = getter?.target, + generics = generics.mapNotNull { it.target } + )).let { AnyWithChanges(it, wasChanged) } + } + + protected open fun processParameter(dParameter: DParameter): AnyWithChanges<DParameter> { + val type = processBound(dParameter.type) + + val wasChanged = type.changed + return (dParameter.takeIf { !wasChanged } ?: dParameter.copy( + type = type.target ?: dParameter.type, + )).let { AnyWithChanges(it, wasChanged) } + } + + protected open fun processTypeParameter(dTypeParameter: DTypeParameter): AnyWithChanges<DTypeParameter> { + val bounds = dTypeParameter.bounds.map { processBound(it) } + + val wasChanged = bounds.any { it.changed } + return (dTypeParameter.takeIf { !wasChanged } ?: dTypeParameter.copy( + bounds = bounds.mapIndexed { i, v -> v.target ?: dTypeParameter.bounds[i] } + )).let { AnyWithChanges(it, wasChanged) } + } + + protected open fun processBound(bound: Bound) = when(bound) { + is GenericTypeConstructor -> processGenericTypeConstructor(bound) + is FunctionalTypeConstructor -> processFunctionalTypeConstructor(bound) + else -> AnyWithChanges(bound, false) + } + + protected open fun processVariance(variance: Variance<*>): AnyWithChanges<Variance<*>> { + val bound = processBound(variance.inner) + if (!bound.changed) + return AnyWithChanges(variance, false) + return when (variance) { + is Covariance<*> -> AnyWithChanges( + Covariance(bound.target ?: variance.inner), true) + is Contravariance<*> -> AnyWithChanges( + Contravariance(bound.target ?: variance.inner), true) + is Invariance<*> -> AnyWithChanges( + Invariance(bound.target ?: variance.inner), true) + else -> AnyWithChanges(variance, false) + } + } + + protected open fun processProjection(projection: Projection): AnyWithChanges<Projection> = + when (projection) { + is Bound -> processBound(projection) + is Variance<Bound> -> processVariance(projection) + else -> AnyWithChanges(projection, false) + } + + protected open fun processGenericTypeConstructor(genericTypeConstructor: GenericTypeConstructor): AnyWithChanges<GenericTypeConstructor> { + val projections = genericTypeConstructor.projections.map { processProjection(it) } + + val wasChanged = projections.any { it.changed } + return (genericTypeConstructor.takeIf { !wasChanged } ?: genericTypeConstructor.copy( + projections = projections.mapNotNull { it.target } + )).let { AnyWithChanges(it, wasChanged) } + } + + protected open fun processFunctionalTypeConstructor(functionalTypeConstructor: FunctionalTypeConstructor): AnyWithChanges<FunctionalTypeConstructor> { + val projections = functionalTypeConstructor.projections.map { processProjection(it) } + + val wasChanged = projections.any { it.changed } + return (functionalTypeConstructor.takeIf { !wasChanged } ?: functionalTypeConstructor.copy( + projections = projections.mapNotNull { it.target } + )).let { AnyWithChanges(it, wasChanged) } + } + + protected open fun processTypeAlias(dTypeAlias: DTypeAlias): AnyWithChanges<DTypeAlias> { + val underlyingType = dTypeAlias.underlyingType.mapValues { processBound(it.value) } + val generics = dTypeAlias.generics.map { processTypeParameter(it) } + + val wasChanged = underlyingType.any { it.value.changed } || generics.any { it.changed } + return (dTypeAlias.takeIf { !wasChanged } ?: dTypeAlias.copy( + underlyingType = underlyingType.mapValues { it.value.target ?: dTypeAlias.underlyingType.getValue(it.key) }, + generics = generics.mapNotNull { it.target } + )).let { AnyWithChanges(it, wasChanged) } + } + + + protected data class AnyWithChanges<out T>(val target: T?, val changed: Boolean = false) +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt new file mode 100644 index 00000000..251422f4 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt @@ -0,0 +1,63 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext + +class KotlinArrayDocumentableReplacerTransformer(context: DokkaContext): + DocumentableReplacerTransformer(context) { + + private fun Documentable.isJVM() = + sourceSets.any{ it.analysisPlatform == Platform.jvm } + + override fun processGenericTypeConstructor(genericTypeConstructor: GenericTypeConstructor): AnyWithChanges<GenericTypeConstructor> = + genericTypeConstructor.takeIf { genericTypeConstructor.dri == DRI("kotlin", "Array") } + ?.let { + with(it.projections.firstOrNull() as? Variance<Bound>) { + with(this?.inner as? GenericTypeConstructor) { + when (this?.dri) { + DRI("kotlin", "Int") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), + true) + DRI("kotlin", "Boolean") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + true) + DRI("kotlin", "Float") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "FloatArray"), emptyList()), + true) + DRI("kotlin", "Double") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "DoubleArray"), emptyList()), + true) + DRI("kotlin", "Long") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "LongArray"), emptyList()), + true) + DRI("kotlin", "Short") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "ShortArray"), emptyList()), + true) + DRI("kotlin", "Char") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "CharArray"), emptyList()), + true) + DRI("kotlin", "Byte") -> + AnyWithChanges( + GenericTypeConstructor(DRI("kotlin", "ByteArray"), emptyList()), + true) + else -> null + } + } + } + } + ?: super.processGenericTypeConstructor(genericTypeConstructor) + + override fun processModule(module: DModule): AnyWithChanges<DModule> = + if(module.isJVM()) + super.processModule(module) + else AnyWithChanges(module) +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 081608d6..4b5fc1c0 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -141,7 +141,7 @@ open class DefaultPageCreator( link(it.name, it.dri) if (it.sourceSets.size == 1 || (documentations.isNotEmpty() && haveSameContent)) { - documentations.first()?.let { firstSentenceComment(kind = ContentKind.Comment, content = it.root) } + documentations.first()?.let { firstParagraphComment(kind = ContentKind.Comment, content = it.root) } } } } @@ -375,7 +375,7 @@ open class DefaultPageCreator( val platforms = d.sourceSets fun DocumentableContentBuilder.contentForParams() { - if (tags.isNotEmptyForTag<Param>()) { + if (tags.isNotEmptyForTag<Param>() && d !is DProperty) { header(2, "Parameters", kind = ContentKind.Parameters) group( extra = mainExtra + SimpleAttr.header("Parameters"), @@ -504,15 +504,33 @@ open class DefaultPageCreator( protected open fun DocumentableContentBuilder.contentForBrief(documentable: Documentable) { documentable.sourceSets.forEach { sourceSet -> - documentable.documentation[sourceSet]?.children?.firstOrNull()?.root?.let { + documentable.documentation[sourceSet]?.let { + /* + Get description or a tag that holds documentation. + This tag can be either property or constructor but constructor tags are handled already in analysis so we + only need to keep an eye on property + + We purposefully ignore all other tags as they should not be visible in brief + */ + it.firstMemberOfTypeOrNull<Description>() ?: it.firstMemberOfTypeOrNull<Property>().takeIf { documentable is DProperty } + }?.let { group(sourceSets = setOf(sourceSet), kind = ContentKind.BriefComment) { - if (documentable.hasSeparatePage) firstSentenceComment(it) - else comment(it) + if (documentable.hasSeparatePage) createBriefComment(documentable, sourceSet, it) + else comment(it.root) } } } } + private fun DocumentableContentBuilder.createBriefComment(documentable: Documentable, sourceSet: DokkaSourceSet, tag: TagWrapper){ + (documentable as? WithSources)?.documentableLanguage(sourceSet)?.let { + when(it){ + DocumentableLanguage.KOTLIN -> firstParagraphComment(tag.root) + DocumentableLanguage.JAVA -> firstSentenceComment(tag.root) + } + } ?: firstParagraphComment(tag.root) + } + protected open fun DocumentableContentBuilder.contentForSinceKotlin(documentable: Documentable) { documentable.documentation.mapValues { it.value.children.find { it is CustomTagWrapper && it.name == "Since Kotlin" } as CustomTagWrapper? diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index cbab6273..f9bc7e26 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -292,13 +292,37 @@ open class PageContentBuilder( contents += ContentGroup(content, DCI(mainDRI, kind), sourceSets.toDisplaySourceSets(), styles, extra) } - fun firstSentenceComment( + fun firstParagraphComment( content: DocTag, kind: Kind = ContentKind.Comment, sourceSets: Set<DokkaSourceSet> = mainSourcesetData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra ) { + firstParagraphBrief(content)?.let { brief -> + val builtDescription = commentsConverter.buildContent( + brief, + DCI(mainDRI, kind), + sourceSets + ) + + contents += ContentGroup( + builtDescription, + DCI(mainDRI, kind), + sourceSets.toDisplaySourceSets(), + styles, + extra + ) + } + } + + fun firstSentenceComment( + content: DocTag, + kind: Kind = ContentKind.Comment, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ){ val builtDescription = commentsConverter.buildContent( content, DCI(mainDRI, kind), @@ -306,7 +330,7 @@ open class PageContentBuilder( ) contents += ContentGroup( - briefFromContentNodes(builtDescription), + firstSentenceBriefFromContentNodes(builtDescription), DCI(mainDRI, kind), sourceSets.toDisplaySourceSets(), styles, diff --git a/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt b/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt index 7ac6763d..81ddb6ed 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt @@ -1,10 +1,20 @@ package org.jetbrains.dokka.base.translators.documentables +import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.* +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.safeAs -fun briefFromContentNodes(description: List<ContentNode>): List<ContentNode> { +fun firstParagraphBrief(docTag: DocTag): DocTag? = + when(docTag){ + is P -> docTag + is CustomDocTag -> docTag.children.firstNotNullResult { firstParagraphBrief(it) } + is Text -> docTag + else -> null + } + +fun firstSentenceBriefFromContentNodes(description: List<ContentNode>): List<ContentNode> { val firstSentenceRegex = """^((?:[^.?!]|[.!?](?!\s))*[.!?])""".toRegex() //Description that is entirely based on html content. In html it is hard to define a brief so we render all of it @@ -13,21 +23,34 @@ fun briefFromContentNodes(description: List<ContentNode>): List<ContentNode> { } var sentenceFound = false - fun lookthrough(node: ContentNode): ContentNode = - if (node is ContentText && !node.isHtml && firstSentenceRegex.containsMatchIn(node.text)) { + fun lookthrough(node: ContentNode, neighbours: List<ContentNode>, currentIndex: Int): ContentNode = + if (node.finishesWithSentenceNotFollowedByHtml(firstSentenceRegex, neighbours, currentIndex) || node.containsSentenceFinish(firstSentenceRegex)) { + node as ContentText sentenceFound = true node.copy(text = firstSentenceRegex.find(node.text)?.value.orEmpty()) } else if (node is ContentGroup) { - node.copy(children = node.children.mapNotNull { - if (!sentenceFound) lookthrough(it) else null + node.copy(children = node.children.mapIndexedNotNull { i, element -> + if (!sentenceFound) lookthrough(element, node.children, i) else null }, style = node.style - TextStyle.Paragraph) } else { node } - return description.mapNotNull { - if (!sentenceFound) lookthrough(it) else null + return description.mapIndexedNotNull { i, element -> + if (!sentenceFound) lookthrough(element, description, i) else null } } -private val ContentText.isHtml +private fun ContentNode.finishesWithSentenceNotFollowedByHtml(firstSentenceRegex: Regex, neighbours: List<ContentNode>, currentIndex: Int): Boolean = + this is ContentText && !isHtml && matchContainsEnd(this, firstSentenceRegex) && !neighbours.nextElementIsHtml(currentIndex) + +private fun ContentNode.containsSentenceFinish(firstSentenceRegex: Regex): Boolean = + this is ContentText && !isHtml && firstSentenceRegex.containsMatchIn(text) && !matchContainsEnd(this, firstSentenceRegex) + +private fun matchContainsEnd(node: ContentText, regex: Regex): Boolean = + regex.find(node.text)?.let { node.text.endsWith(it.value) } ?: false + +private fun List<ContentNode>.nextElementIsHtml(currentElementIndex: Int): Boolean = + currentElementIndex != lastIndex && get(currentElementIndex + 1).isHtml + +private val ContentNode.isHtml get() = extra[HtmlContent] != null diff --git a/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt b/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt new file mode 100644 index 00000000..b3ce7c5c --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt @@ -0,0 +1,15 @@ +package org.jetbrains.dokka.base.translators.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.PsiDocumentableSource +import org.jetbrains.dokka.model.WithSources + +internal enum class DocumentableLanguage { + JAVA, KOTLIN +} + +internal fun WithSources.documentableLanguage(sourceSet: DokkaConfiguration.DokkaSourceSet): DocumentableLanguage = + when (sources[sourceSet]) { + is PsiDocumentableSource -> DocumentableLanguage.JAVA + else -> DocumentableLanguage.KOTLIN + }
\ No newline at end of file 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 9cb362cb..8583edf7 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt @@ -22,9 +22,7 @@ import org.jetbrains.kotlin.idea.util.CommentSaver.Companion.tokenType import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode +import org.jsoup.nodes.* import java.util.* import org.jetbrains.dokka.utilities.htmlEscape @@ -414,6 +412,7 @@ class JavadocParser( } else { node.wholeText.parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces = true) }).orEmpty() + is Comment -> listOf(Text(body = node.outerHtml(), params = DocTag.contentTypeParam("html"))) is Element -> createBlock(node, keepFormatting) else -> emptyList() } diff --git a/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt b/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt new file mode 100644 index 00000000..7d8a169b --- /dev/null +++ b/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt @@ -0,0 +1,330 @@ +package content.functions + +import org.junit.Assert.assertEquals +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.TypeConstructor +import org.jetbrains.dokka.model.DClass +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.* +import org.junit.jupiter.api.Test +import kotlin.test.assertNull + + +class ContentForBriefTest : BaseAbstractTest() { + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + } + } + } + + private val codeWithSecondaryAndPrimaryConstructorsDocumented = + """ + |/src/main/kotlin/test/source.kt + |package test + | + |/** + | * Dummy text. + | * + | * @constructor constructor docs + | * @param exampleParameter dummy parameter. + | */ + |class Example(val exampleParameter: Int) { + | + | /** + | * secondary constructor + | * @param param1 param1 docs + | */ + | constructor(param1: String) : this(1) + |} + """.trimIndent() + + private val codeWithDocumentedParameter = + """ + |/src/main/kotlin/test/source.kt + |package test + | + |/** + | * Dummy text. + | * + | * @param exampleParameter dummy parameter. + | */ + |class Example(val exampleParameter: Int) { + |} + """.trimIndent() + + @Test + fun `primary constructor should not inherit docs from its parameter`() { + testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) { + pagesTransformationStage = { module -> + val classPage = + module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + val constructorsTable = + classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable + + assertEquals(2, constructorsTable.children.size) + val primary = constructorsTable.children.first { + it.dci.dri.first().callable?.params?.first() == TypeConstructor( + "kotlin.Int", + emptyList() + ) + } + val primaryConstructorDocs = + primary.dfs { it is ContentText && it.dci.kind == ContentKind.Comment } as ContentText + + assertEquals("constructor docs", primaryConstructorDocs.text) + } + } + } + + @Test + fun `secondary constructor should not inherit docs from its parameter`() { + testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) { + pagesTransformationStage = { module -> + val classPage = + module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + val constructorsTable = + classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable + + assertEquals(2, constructorsTable.children.size) + val primary = constructorsTable.children.first { + it.dci.dri.first().callable?.params?.first() == TypeConstructor( + "kotlin.String", + emptyList() + ) + } + val primaryConstructorDocs = + primary.dfs { it is ContentText && it.dci.kind == ContentKind.Comment } as ContentText + + assertEquals("secondary constructor", primaryConstructorDocs.text) + } + } + } + + @Test + fun `primary constructor should not inherit docs from its parameter when no specific docs are provided`() { + testInline(codeWithDocumentedParameter, testConfiguration) { + pagesTransformationStage = { module -> + val classPage = + module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + val constructorsTable = + classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable + + assertEquals(1, constructorsTable.children.size) + val primary = constructorsTable.children.first() + val primaryConstructorDocs = primary.dfs { it is ContentText && it.dci.kind == ContentKind.Comment } + + assertNull(primaryConstructorDocs, "Expected no primary constructor docs to be present") + } + } + } + + @Test + fun `brief for functions should work with html`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class Example(val exampleParameter: Int) { + | /** + | * This is an example <!-- not visible --> of html + | * + | * This is definitely not a brief + | */ + | fun test(): String = "TODO" + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "This is an example <!-- not visible --> of html", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + @Test + fun `brief for functions should work with ie`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class Example(val exampleParameter: Int) { + | /** + | * The user token, i.e. "Bearer xyz". Throw an exception if not available. + | * + | * This is definitely not a brief + | */ + | fun test(): String = "TODO" + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "The user token, i.e. \"Bearer xyz\". Throw an exception if not available.", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + @Test + fun `brief for functions should work with eg`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class Example(val exampleParameter: Int) { + | /** + | * The user token, e.g. "Bearer xyz". Throw an exception if not available. + | * + | * This is definitely not a brief + | */ + | fun test(): String = "TODO" + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "The user token, e.g. \"Bearer xyz\". Throw an exception if not available.", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + @Test + fun `brief for functions should be first sentence for Java`() { + testInline( + """ + |/src/main/java/test/Example.java + |package test; + | + |public class Example { + | /** + | * The user token, or not. This is definitely not a brief in java + | */ + | public static String test() { + | return "TODO"; + | } + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "The user token, or not.", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + @Test + fun `brief for functions should work with ie for Java`() { + testInline( + """ + |/src/main/java/test/Example.java + |package test; + | + |public class Example { + | /** + | * The user token, e.g. "Bearer xyz". This is definitely not a brief in java + | */ + | public static String test() { + | return "TODO"; + | } + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "The user token, e.g. \"Bearer xyz\".", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + //Source: https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html#exampleresult + @Test + fun `brief for functions should work with html comment for Java`() { + testInline( + """ + |/src/main/java/test/Example.java + |package test; + | + |public class Example { + | /** + | * This is a simulation of Prof.<!-- --> Knuth's MIX computer. This is definitely not a brief in java + | */ + | public static String test() { + | return "TODO"; + | } + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "This is a simulation of Prof.<!-- --> Knuth's MIX computer.", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + @Test + fun `brief for functions should work with html comment at the end for Java`() { + testInline( + """ + |/src/main/java/test/Example.java + |package test; + | + |public class Example { + | /** + | * This is a simulation of Prof.<!-- --> Knuth's MIX computer. This is definitely not a brief in java <!-- --> + | */ + | public static String test() { + | return "TODO"; + | } + |} + """.trimIndent(), + testConfiguration + ) { + pagesTransformationStage = { module -> + val functionBriefDocs = module.singleFunctionDescription("Example") + + assertEquals( + "This is a simulation of Prof.<!-- --> Knuth's MIX computer.", + functionBriefDocs.children.joinToString("") { (it as ContentText).text }) + } + } + } + + private fun RootPageNode.singleFunctionDescription(className: String): ContentGroup { + val classPage = dfs { it.name == className && (it as ContentPage).documentable is DClass } as ContentPage + val functionsTable = + classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Functions } as ContentTable + + assertEquals(1, functionsTable.children.size) + val function = functionsTable.children.first() + return function.dfs { it is ContentGroup && it.dci.kind == ContentKind.Comment && it.children.all { it is ContentText } } as ContentGroup + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt new file mode 100644 index 00000000..b9b1dc1e --- /dev/null +++ b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt @@ -0,0 +1,199 @@ +package filter + +import com.jetbrains.rd.util.firstOrNull +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + @Test + fun `function with array type params`() { + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |fun testFunction(param1: Array<Int>, param2: Array<Boolean>, + | param3: Array<Float>, param4: Array<Double>, + | param5: Array<Long>, param6: Array<Short>, + | param7: Array<Char>, param8: Array<Byte>) { } + | + | + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + val params = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull()?.parameters + + val typeArrayNames = listOf("IntArray", "BooleanArray", "FloatArray", "DoubleArray", "LongArray", "ShortArray", + "CharArray", "ByteArray") + + Assertions.assertEquals(typeArrayNames.size, params?.size) + params?.forEachIndexed{ i, param -> + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", typeArrayNames[i]), emptyList()), + param.type) + } + } + } + } + @Test + fun `function with specific parameters of array type`() { + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |fun testFunction(param1: Array<Array<Int>>, param2: (Array<Int>) -> Array<Int>) { } + | + | + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + val params = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull()?.parameters + Assertions.assertEquals( + Invariance(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList())), + (params?.firstOrNull()?.type as? GenericTypeConstructor)?.projections?.firstOrNull()) + Assertions.assertEquals( + Invariance(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList())), + (params?.get(1)?.type as? FunctionalTypeConstructor)?.projections?.get(0)) + Assertions.assertEquals( + Invariance(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList())), + (params?.get(1)?.type as? FunctionalTypeConstructor)?.projections?.get(1)) + } + } + } + @Test + fun `property with array type`() { + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |class MyTest { + | val isEmpty: Array<Boolean> + | get() = emptyList + | set(value) { + | field = value + | } + |} + | + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + val myTestClass = it.firstOrNull()?.packages?.firstOrNull()?.classlikes?.firstOrNull() + val property = myTestClass?.properties?.firstOrNull() + + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + property?.type) + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + property?.getter?.type) + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), + property?.setter?.parameters?.firstOrNull()?.type) + } + } + } + @Test + fun `typealias with array type`() { + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |typealias arr = Array<Int> + | + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + val arrTypealias = it.firstOrNull()?.packages?.firstOrNull()?.typealiases?.firstOrNull() + + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), + arrTypealias?.underlyingType?.firstOrNull()?.value) + } + } + } + @Test + fun `generic fun and class`() { + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |fun<T : Array<Int>> testFunction() { } + |class<T : Array<Int>> myTestClass{ } + | + | + """.trimMargin(), + configuration + ) { + preMergeDocumentablesTransformationStage = { + val testFun = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull() + val myTestClass = it.firstOrNull()?.packages?.firstOrNull()?.classlikes?.firstOrNull() as? DClass + + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin","IntArray"), emptyList()), + testFun?.generics?.firstOrNull()?.bounds?.firstOrNull()) + Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin","IntArray"), emptyList()), + myTestClass?.generics?.firstOrNull()?.bounds?.firstOrNull()) + } + } + } + @Test + fun `no jvm source set`() { + val configurationWithNoJVM = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + analysisPlatform = "jvm" + } + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/TestJS.kt") + analysisPlatform = "js" + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |fun testFunction(param: Array<Int>) + | + | + |/src/main/kotlin/basic/TestJS.kt + |package example + | + |fun testFunction(param: Array<Int>) + """.trimMargin(), + configurationWithNoJVM + ) { + preMergeDocumentablesTransformationStage = { + val paramsJS = it[1].packages.firstOrNull()?.functions?.firstOrNull()?.parameters + Assertions.assertNotEquals( + GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), + paramsJS?.firstOrNull()?.type) + + val paramsJVM = it.firstOrNull()?.packages?.firstOrNull()?.functions?.firstOrNull()?.parameters + Assertions.assertEquals( + GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), + paramsJVM?.firstOrNull()?.type) + } + } + } +}
\ No newline at end of file diff --git a/plugins/javadoc/build.gradle.kts b/plugins/javadoc/build.gradle.kts index 093f16e1..b549b0ac 100644 --- a/plugins/javadoc/build.gradle.kts +++ b/plugins/javadoc/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.registerDokkaArtifactPublication dependencies { - implementation("com.soywiz.korlibs.korte:korte-jvm:2.3.1") + implementation("com.soywiz.korlibs.korte:korte-jvm:2.3.3") implementation(project(":plugins:base")) implementation(project(":plugins:kotlin-as-java")) testImplementation(project(":plugins:base:base-test-utils")) diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt index 4d0a62c3..4dbd5ca7 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt @@ -4,7 +4,7 @@ import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter -import org.jetbrains.dokka.base.translators.documentables.briefFromContentNodes +import org.jetbrains.dokka.base.translators.documentables.firstSentenceBriefFromContentNodes import org.jetbrains.dokka.javadoc.pages.* import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.Description @@ -194,10 +194,10 @@ open class JavadocPageCreator(context: DokkaContext) { ?: throw IllegalStateException("No source set found for ${jvm.sourceSetID} ") private fun Documentable.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> = - briefFromContentNodes(descriptionToContentNodes(sourceSet)) + firstSentenceBriefFromContentNodes(descriptionToContentNodes(sourceSet)) private fun DParameter.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> = - briefFromContentNodes(paramsToContentNodes(sourceSet).dropWhile { it is ContentDRILink }) + firstSentenceBriefFromContentNodes(paramsToContentNodes(sourceSet).dropWhile { it is ContentDRILink }) private fun ContentNode.asJavadocNode(): JavadocSignatureContentNode = (this as ContentGroup).firstChildOfTypeOrNull<JavadocSignatureContentNode>() diff --git a/plugins/versioning/build.gradle.kts b/plugins/versioning/build.gradle.kts index 894ce77a..286c5a9d 100644 --- a/plugins/versioning/build.gradle.kts +++ b/plugins/versioning/build.gradle.kts @@ -17,5 +17,5 @@ dependencies { val jsoup_version: String by project implementation("org.jsoup:jsoup:$jsoup_version") - implementation("org.apache.maven:maven-artifact:3.6.3") + implementation("org.apache.maven:maven-artifact:3.8.2") }
\ No newline at end of file |