diff options
Diffstat (limited to 'plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt')
-rw-r--r-- | plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt new file mode 100644 index 00000000..37e0ea83 --- /dev/null +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -0,0 +1,382 @@ +package org.jetbrains.dokka.base.signatures + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.dri +import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.driOrNull +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Nullable +import org.jetbrains.dokka.model.TypeConstructor +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.TextStyle +import org.jetbrains.dokka.utilities.DokkaLogger +import kotlin.text.Typography.nbsp + +class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider, + JvmSignatureUtils by KotlinSignatureUtils { + private val contentBuilder = PageContentBuilder(ctcc, this, logger) + + private val ignoredVisibilities = setOf(JavaVisibility.Public, KotlinVisibility.Public) + private val ignoredModifiers = setOf(JavaModifier.Final, KotlinModifier.Final) + private val ignoredExtraModifiers = setOf( + ExtraModifiers.KotlinOnlyModifiers.TailRec, + ExtraModifiers.KotlinOnlyModifiers.External + ) + private val platformSpecificModifiers: Map<ExtraModifiers, Set<Platform>> = mapOf( + ExtraModifiers.KotlinOnlyModifiers.External to setOf(Platform.js) + ) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is DFunction -> functionSignature(documentable) + is DProperty -> propertySignature(documentable) + is DClasslike -> classlikeSignature(documentable) + is DTypeParameter -> signature(documentable) + is DEnumEntry -> signature(documentable) + is DTypeAlias -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun <T> PageContentBuilder.DocumentableContentBuilder.processExtraModifiers(t: T) + where T : Documentable, T : WithExtraProperties<T> { + sourceSetDependentText( + t.modifiers() + .mapValues { entry -> + entry.value.filter { + it !in ignoredExtraModifiers || entry.key.analysisPlatform in (platformSpecificModifiers[it] + ?: emptySet()) + } + } + ) { + it.toSignatureString() + } + } + + private fun signature(e: DEnumEntry): List<ContentNode> = + e.sourceSets.map { + contentBuilder.contentFor( + e, + ContentKind.Symbol, + setOf(TextStyle.Monospace, TextStyle.Block) + e.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + group(styles = setOf(TextStyle.Block)) { + annotationsBlock(e) + link(e.name, e.dri, styles = emptySet()) + e.extra[ConstructorValues]?.let { constructorValues -> + constructorValues.values[it] + text(constructorValues.values[it]?.joinToString(prefix = "(", postfix = ")") ?: "") + } + } + } + } + + private fun actualTypealiasedSignature(c: DClasslike, sourceSet: DokkaSourceSet, aliasedType: Bound) = + contentBuilder.contentFor( + c, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(sourceSet) + ?: emptySet()), + sourceSets = setOf(sourceSet) + ) { + text("actual typealias ") + link(c.name.orEmpty(), c.dri) + text(" = ") + signatureForProjection(aliasedType) + } + + @Suppress("UNCHECKED_CAST") + private fun <T : DClasslike> classlikeSignature(c: T): List<ContentNode> = + c.sourceSets.map { sourceSetData -> + (c as? WithExtraProperties<out DClasslike>)?.extra?.get(ActualTypealias)?.underlyingType?.get(sourceSetData) + ?.let { + actualTypealiasedSignature(c, sourceSetData, it) + } ?: regularSignature(c, sourceSetData) + } + + + private fun regularSignature(c: DClasslike, sourceSet: DokkaSourceSet) = + contentBuilder.contentFor( + c, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(sourceSet) + ?: emptySet()), + sourceSets = setOf(sourceSet) + ) { + annotationsBlock(c) + text(c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + if (c is DClass) { + text( + if (c.modifier[sourceSet] !in ignoredModifiers) + when { + c.extra[AdditionalModifiers]?.content?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> "" + c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} " + else -> c.modifier[sourceSet]?.name?.let { "$it " } ?: "" + } + else + "" + ) + } + if (c is DInterface) { + c.extra[AdditionalModifiers]?.content?.let { additionalModifiers -> + sourceSetDependentText(additionalModifiers, setOf(sourceSet)) { extraModifiers -> + if (ExtraModifiers.KotlinOnlyModifiers.Fun in extraModifiers) "fun " + else "" + } + } + } + when (c) { + is DClass -> { + processExtraModifiers(c) + text("class ") + } + is DInterface -> { + processExtraModifiers(c) + text("interface ") + } + is DEnum -> { + processExtraModifiers(c) + text("enum ") + } + is DObject -> { + processExtraModifiers(c) + text("object ") + } + is DAnnotation -> { + processExtraModifiers(c) + text("annotation class ") + } + } + link(c.name!!, c.dri) + if (c is WithGenerics) { + list(c.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + } + if (c is WithConstructors) { + val pConstructor = c.constructors.singleOrNull { it.extra[PrimaryConstructorExtra] != null } + if (pConstructor?.sourceSets?.contains(sourceSet) == true) { + if (pConstructor.annotations().values.any { it.isNotEmpty() }) { + text(nbsp.toString()) + annotationsInline(pConstructor) + text("constructor") + } + list( + pConstructor.parameters, + "(", + ")", + ",", + pConstructor.sourceSets.toSet() + ) { + annotationsInline(it) + text(it.name ?: "", styles = mainStyles.plus(TextStyle.Bold)) + text(": ") + signatureForProjection(it.type) + } + } + } + if (c is WithSupertypes) { + c.supertypes.filter { it.key == sourceSet }.map { (s, dris) -> + list(dris, prefix = " : ", sourceSets = setOf(s)) { + link(it.dri.sureClassNames, it.dri, sourceSets = setOf(s)) + } + } + } + } + + private fun propertySignature(p: DProperty) = + p.sourceSets.map { + contentBuilder.contentFor( + p, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + p.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + annotationsBlock(p) + text(p.visibility[it].takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + text( + p.modifier[it].takeIf { it !in ignoredModifiers }?.let { + if (it is JavaModifier.Empty) KotlinModifier.Open else it + }?.name?.let { "$it " } ?: "" + ) + text(p.modifiers()[it]?.toSignatureString() ?: "") + p.setter?.let { text("var ") } ?: text("val ") + list(p.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + p.receiver?.also { + signatureForProjection(it.type) + text(".") + } + link(p.name, p.dri) + text(": ") + signatureForProjection(p.type) + } + } + + private fun functionSignature(f: DFunction) = + f.sourceSets.map { + contentBuilder.contentFor( + f, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + f.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + annotationsBlock(f) + text(f.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.let { + if (it is JavaModifier.Empty) KotlinModifier.Open else it + }?.name?.let { "$it " } ?: "" + ) + text(f.modifiers()[it]?.toSignatureString() ?: "") + text("fun ") + list(f.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + f.receiver?.also { + signatureForProjection(it.type) + text(".") + } + link(f.name, f.dri) + text("(") + list(f.parameters) { + annotationsInline(it) + processExtraModifiers(it) + text(it.name!!) + text(": ") + signatureForProjection(it.type) + } + text(")") + if (f.documentReturnType()) { + text(": ") + signatureForProjection(f.type) + } + } + } + + private fun DFunction.documentReturnType() = when { + this.isConstructor -> false + this.type is TypeConstructor && (this.type as TypeConstructor).dri == DriOfUnit -> false + this.type is Void -> false + else -> true + } + + private fun signature(t: DTypeAlias) = + t.sourceSets.map { + contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { + t.underlyingType.entries.groupBy({ it.value }, { it.key }).map { (type, platforms) -> + +contentBuilder.contentFor( + t, + ContentKind.Symbol, + setOf(TextStyle.Monospace), + sourceSets = platforms.toSet() + ) { + text(t.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + processExtraModifiers(t) + text("typealias ") + signatureForProjection(t.type) + text(" = ") + signatureForTypealiasTarget(t, type) + } + } + } + } + + private fun signature(t: DTypeParameter) = + t.sourceSets.map { + contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { + link(t.name, t.dri.withTargetToDeclaration()) + list(t.bounds, prefix = " : ") { + signatureForProjection(it) + } + } + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForTypealiasTarget( + typeAlias: DTypeAlias, bound: Bound + ) { + signatureForProjection( + p = bound, + showFullyQualifiedName = + bound.driOrNull?.packageName != typeAlias.dri.packageName && + bound.driOrNull?.packageName != "kotlin" + ) + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection( + p: Projection, showFullyQualifiedName: Boolean = false + ): Unit = + when (p) { + is OtherParameter -> link(p.name, p.declarationDRI) + + is TypeConstructor -> if (p.function) + +funType(mainDRI.single(), mainSourcesetData, p) + else + group(styles = emptySet()) { + val linkText = if (showFullyQualifiedName && p.dri.packageName != null) { + "${p.dri.packageName}.${p.dri.classNames.orEmpty()}" + } else p.dri.classNames.orEmpty() + + link(linkText, p.dri) + list(p.projections, prefix = "<", suffix = ">") { + signatureForProjection(it, showFullyQualifiedName) + } + } + + is Variance -> group(styles = emptySet()) { + text(p.kind.toString() + " ") + signatureForProjection(p.inner, showFullyQualifiedName) + } + + is Star -> text("*") + + is Nullable -> group(styles = emptySet()) { + signatureForProjection(p.inner, showFullyQualifiedName) + text("?") + } + + is JavaObject -> link("Any", DriOfAny) + is Void -> link("Unit", DriOfUnit) + is PrimitiveJavaType -> signatureForProjection(p.translateToKotlin(), showFullyQualifiedName) + is Dynamic -> text("dynamic") + is UnresolvedBound -> text(p.name) + } + + private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: TypeConstructor) = + contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) { + if (type.extension) { + signatureForProjection(type.projections.first()) + text(".") + } + + val args = if (type.extension) + type.projections.drop(1) + else + type.projections + + text("(") + args.subList(0, args.size - 1).forEachIndexed { i, arg -> + signatureForProjection(arg) + if (i < args.size - 2) text(", ") + } + text(") -> ") + signatureForProjection(args.last()) + } +} + +private fun PrimitiveJavaType.translateToKotlin() = TypeConstructor( + dri = dri, + projections = emptyList() +) + +val TypeConstructor.function + get() = modifier == FunctionModifiers.FUNCTION || modifier == FunctionModifiers.EXTENSION + +val TypeConstructor.extension + get() = modifier == FunctionModifiers.EXTENSION |