diff options
8 files changed, 163 insertions, 26 deletions
diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt index dd6eab1b..6a1af187 100644 --- a/core/src/main/kotlin/CoreExtensions.kt +++ b/core/src/main/kotlin/CoreExtensions.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka +import org.jetbrains.dokka.model.SignatureProvider import org.jetbrains.dokka.plugability.ExtensionPoint import org.jetbrains.dokka.renderers.Renderer import org.jetbrains.dokka.transformers.descriptors.DescriptorToDocumentableTranslator @@ -18,8 +19,9 @@ object CoreExtensions { val documentableToPageTranslator by coreExtension<DocumentableToPageTranslator>() val pageTransformer by coreExtension<PageTransformer>() val renderer by coreExtension<Renderer>() + val signatureProvider by coreExtension<SignatureProvider>() - private fun <T: Any> coreExtension() = object { + private fun <T : Any> coreExtension() = object { operator fun provideDelegate(thisRef: CoreExtensions, property: KProperty<*>): Lazy<ExtensionPoint<T>> = lazy { ExtensionPoint<T>(thisRef::class.qualifiedName!!, property.name) } } diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt index 791d2b5e..57ac96e5 100644 --- a/core/src/main/kotlin/links/DRI.kt +++ b/core/src/main/kotlin/links/DRI.kt @@ -68,6 +68,9 @@ val DRI.parent: DRI else -> DRI.topLevel } +val DRI.sureClassNames + get() = classNames ?: throw IllegalStateException("Malformed DRI. It requires classNames in this context.") + data class Callable( val name: String, val receiver: TypeReference? = null, diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index c16f8a87..4190edc3 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -315,7 +315,7 @@ data class TypeParameter( override val dri: DRI, override val name: String, override val documentation: PlatformDependent<DocumentationNode>, - val bounds: List<Projection>, + val bounds: List<Bound>, override val platformData: List<PlatformData>, override val extra: PropertyContainer<TypeParameter> = PropertyContainer.empty() ) : Documentable(), WithExtraProperties<TypeParameter> { @@ -325,11 +325,14 @@ data class TypeParameter( override fun withNewExtras(newExtras: PropertyContainer<TypeParameter>) = copy(extra = newExtras) } -sealed class Projection { - data class OtherParameter(val name: String) : Projection() - object Star : Projection() - data class TypeConstructor(val dri: DRI, val projections: List<Projection>) : Projection() - data class Nullable(val inner: Projection) : Projection() +sealed class Projection +sealed class Bound : Projection() +data class OtherParameter(val name: String) : Bound() +object Star : Projection() +data class TypeConstructor(val dri: DRI, val projections: List<Projection>) : Bound() +data class Nullable(val inner: Bound) : Bound() +data class Variance(val kind: Kind, val inner: Bound): Projection() { + enum class Kind { In, Out } } enum class ExtraModifiers { diff --git a/core/src/main/kotlin/model/SignatureProvider.kt b/core/src/main/kotlin/model/SignatureProvider.kt new file mode 100644 index 00000000..2df0f4b6 --- /dev/null +++ b/core/src/main/kotlin/model/SignatureProvider.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.pages.ContentNode + +interface SignatureProvider { + fun signature(documentable: Documentable): List<ContentNode> +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/providers/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/providers/KotlinSignatureProvider.kt new file mode 100644 index 00000000..efee39fd --- /dev/null +++ b/plugins/base/src/main/kotlin/providers/KotlinSignatureProvider.kt @@ -0,0 +1,113 @@ +package org.jetbrains.dokka.base.providers + +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Enum +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.utilities.DokkaLogger + +class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider { + private val contentBuilder = PageContentBuilder(ctcc, logger) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is Function -> signature(documentable) + is Classlike -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun signature(f: Function) = f.platformData.map { signature(f, it) }.distinct() + + private fun signature(c: Classlike) = c.platformData.map { signature(c, it) }.distinct() + + private fun signature(c: Classlike, platform: PlatformData) = contentBuilder.contentFor(c, ContentKind.Symbol) { + text(c.visibility[platform]?.externalDisplayName ?: "") + if (c is Class) { + text(c.modifier.toString()) + } + when (c) { + is Class -> text(" class ") + is Interface -> text(" interface ") + is Enum -> text(" enum ") + is Object -> text(" object ") + } + text(c.name!!) + if (c is WithSupertypes) { + list(c.supertypes.getValue(platform), prefix = " : ") { + link(it.sureClassNames, it) + } + } + } + + private fun signature(f: Function, platform: PlatformData) = contentBuilder.contentFor(f, ContentKind.Symbol) { + text(f.visibility[platform]?.externalDisplayName ?: "") + text(f.modifier.toString()) + text(" fun ") + f.receiver?.also { + type(it.type) + text(".") + } + link(f.name, f.dri) + val generics = f.generics.filterOnPlatform(platform) + if (generics.isNotEmpty()) { + text("<") + generics.forEach { + signature(it) + } + text(">") + } + text("(") + list(f.parameters.filterOnPlatform(platform)) { + link(it.name!!, it.dri) + text(": ") + type(it.type) + } + text(")") + val returnType = f.type + if (!f.isConstructor && returnType.constructorFqName != Unit::class.qualifiedName) { + text(": ") + + type(returnType) + } + } + + private fun signature(t: TypeParameter) = contentBuilder.contentFor(t, ContentKind.Symbol) { + link(t.name, t.dri) + if (t.bounds.isNotEmpty()) { + text("<") + t.bounds.forEach { + signature(it, t.dri, t.platformData) + } + text(">") + } + } + + private fun signature(p: Projection, dri: DRI, platforms: List<PlatformData>): List<ContentNode> = when (p) { + is OtherParameter -> contentBuilder.contentFor(dri, platforms.toSet()) { text(p.name) }.children + + is TypeConstructor -> contentBuilder.contentFor(dri, platforms.toSet()) { + link(p.dri.classNames.orEmpty(), p.dri) + }.children + p.projections.flatMap { signature(it, dri, platforms) } + + is Variance -> contentBuilder.contentFor(dri, platforms.toSet()) { + text(p.kind.toString() + " ") + }.children + signature(p.inner, dri, platforms) + + is Star -> contentBuilder.contentFor(dri, platforms.toSet()) { text("*") }.children + + is Nullable -> signature(p.inner, dri, platforms) + contentBuilder.contentFor( + dri, + platforms.toSet() + ) { text("?") }.children + } + + private fun <T : Documentable> Collection<T>.filterOnPlatform(platformData: PlatformData) = + this.filter { it.platformData.contains(platformData) } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index 83d3d178..ad28a4bc 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -18,8 +18,6 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.idea.refactoring.fqName.fqName import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic @@ -31,6 +29,9 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeProjection +import org.jetbrains.dokka.model.Variance +import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf.fqName +import org.jetbrains.kotlin.idea.kdoc.findKDoc class DefaultDescriptorToDocumentableTranslator( private val context: DokkaContext @@ -134,7 +135,7 @@ open class DokkaDescriptorVisitor( // TODO: close this class and make it private ) } - fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Enum { + private fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Enum { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val info = descriptor.resolveClassDescriptionData(platformData) @@ -157,7 +158,7 @@ open class DokkaDescriptorVisitor( // TODO: close this class and make it private ) } - fun enumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): EnumEntry { + private fun enumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): EnumEntry { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope @@ -173,7 +174,7 @@ open class DokkaDescriptorVisitor( // TODO: close this class and make it private ) } - fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Class { + private fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Class { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val info = descriptor.resolveClassDescriptionData(platformData) @@ -395,26 +396,34 @@ open class DokkaDescriptorVisitor( // TODO: close this class and make it private DRI.from(this), fqNameSafe.asString(), PlatformDependent.from(platformData, getDocumentation()), - upperBounds.map { it.toProjection() }, + upperBounds.map { it.toBound() }, listOf(platformData), extra = additionalExtras() ) - private fun KotlinType.toProjection(): Projection = when (constructor.declarationDescriptor) { - is TypeParameterDescriptor -> Projection.OtherParameter(fqName.toString()).let { - if (isMarkedNullable) Projection.Nullable(it) else it + private fun KotlinType.toBound(): Bound = when (constructor.declarationDescriptor) { + is TypeParameterDescriptor -> OtherParameter(fqName.toString()).let { + if (isMarkedNullable) Nullable(it) else it } - else -> Projection.TypeConstructor( + else -> TypeConstructor( DRI.from(constructor.declarationDescriptor!!), // TODO: remove '!!' arguments.map { it.toProjection() } ) } private fun TypeProjection.toProjection(): Projection = - if (isStarProjection) Projection.Star else fromPossiblyNullable(type) + if (isStarProjection) Star else formPossiblyVariant() + + private fun TypeProjection.formPossiblyVariant(): Projection = type.fromPossiblyNullable().let { + when (projectionKind) { + org.jetbrains.kotlin.types.Variance.INVARIANT -> it + org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Variance(Variance.Kind.In, it) + org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Variance(Variance.Kind.Out, it) + } + } - private fun fromPossiblyNullable(t: KotlinType): Projection = - t.toProjection().let { if (t.isMarkedNullable) Projection.Nullable(it) else it } + private fun KotlinType.fromPossiblyNullable(): Bound = + toBound().let { if (isMarkedNullable) Nullable(it) else it } private fun DeclarationDescriptor.getDocumentation() = findKDoc().let { MarkdownParser(resolutionFacade, this).parseFromKDocTag(it) diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index 9d153a19..176aa87f 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -160,7 +160,7 @@ open class PageContentBuilder( elements: List<T>, prefix: String = "", suffix: String = "", - separator: String = ",", + separator: String = ", ", operation: DocumentableContentBuilder.(T) -> Unit ) { if (elements.isNotEmpty()) { diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 3e95865e..4b4cf8db 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -249,10 +249,10 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator { } private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List<TypeParameter> { - fun mapProjections(bounds: Array<JvmReferenceType>) = - if (bounds.isEmpty()) listOf(Projection.Star) else bounds.mapNotNull { - (it as PsiClassType).let { classType -> - Projection.Nullable(Projection.TypeConstructor(classType.resolve()!!.toDRI(), emptyList())) + fun mapBounds(bounds: Array<JvmReferenceType>): List<Bound> = + if (bounds.isEmpty()) emptyList() else bounds.mapNotNull { + (it as? PsiClassType)?.let { classType -> + Nullable(TypeConstructor(classType.resolve()!!.toDRI(), emptyList())) } } return typeParameters.mapIndexed { index, type -> @@ -260,7 +260,7 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator { dri.copy(genericTarget = index), type.name.orEmpty(), javadocParser.parseDocumentation(type).toPlatformDependant(), - mapProjections(type.bounds), + mapBounds(type.bounds), listOf(platformData) ) } |