aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/base/api/base.api44
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt4
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt228
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt63
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt28
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt28
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt39
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt15
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt5
-rw-r--r--plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt330
-rw-r--r--plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt199
-rw-r--r--plugins/javadoc/build.gradle.kts2
-rw-r--r--plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt6
-rw-r--r--plugins/versioning/build.gradle.kts2
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.&nbsp;"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