diff options
author | Andrzej Ratajczak <andrzej.ratajczak98@gmail.com> | 2020-09-22 11:08:16 +0200 |
---|---|---|
committer | Andrzej Ratajczak <32793002+BarkingBad@users.noreply.github.com> | 2020-10-02 12:40:50 +0200 |
commit | 8de5296b18083361055d11acf6d522c1eef821a4 (patch) | |
tree | 5e4f4c42bf801f33c40c4efeb5601e0fc4523e36 /plugins/base/src | |
parent | 71b03f6d311c6ebfdf67c593e97a7483a64844f4 (diff) | |
download | dokka-8de5296b18083361055d11acf6d522c1eef821a4.tar.gz dokka-8de5296b18083361055d11acf6d522c1eef821a4.tar.bz2 dokka-8de5296b18083361055d11acf6d522c1eef821a4.zip |
Make translators run in parallel.
Diffstat (limited to 'plugins/base/src')
3 files changed, 664 insertions, 533 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt index 47180042..5c0f54d8 100644 --- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt @@ -228,4 +228,4 @@ fun ContentPage.sourceSets() = this.content.sourceSets fun ContentEmbeddedResource.isImage(): Boolean { val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg") return File(address).extension.toLowerCase() in imageExtensions -}
\ 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 ecfd5172..d64a7b29 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -1,5 +1,9 @@ package org.jetbrains.dokka.base.translators.descriptors +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.withContext import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.analysis.DescriptorDocumentableSource import org.jetbrains.dokka.analysis.DokkaResolutionFacade @@ -18,8 +22,9 @@ import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType +import org.jetbrains.dokka.utilities.parallelMap +import org.jetbrains.dokka.utilities.parallelMapNotNull import org.jetbrains.kotlin.builtins.isExtensionFunctionType -import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.codegen.isJvmStaticInObjectOrClassOrInterface import org.jetbrains.kotlin.descriptors.* @@ -27,7 +32,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies import org.jetbrains.kotlin.idea.kdoc.findKDoc import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.load.kotlin.toSourceElement @@ -40,7 +44,6 @@ import org.jetbrains.kotlin.resolve.constants.ConstantValue import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.PsiSourceElement @@ -49,6 +52,7 @@ import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.lang.IllegalStateException import java.nio.file.Paths import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue import org.jetbrains.kotlin.resolve.constants.ArrayValue as ConstantsArrayValue @@ -59,7 +63,7 @@ class DefaultDescriptorToDocumentableTranslator( private val kotlinAnalysis: KotlinAnalysis ) : SourceToDocumentableTranslator { - override fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { + override suspend fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { val (environment, facade) = kotlinAnalysis[sourceSet] val packageFragments = environment.getSourceFiles().asSequence() .map { it.packageFqName } @@ -68,7 +72,7 @@ class DefaultDescriptorToDocumentableTranslator( .toList() return DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger).run { - packageFragments.mapNotNull { it.safeAs<PackageFragmentDescriptor>() }.map { + packageFragments.mapNotNull { it.safeAs<PackageFragmentDescriptor>() }.parallelMap { visitPackageFragmentDescriptor( it, DRIWithPlatformInfo(DRI.topLevel, emptyMap()) @@ -97,11 +101,7 @@ private class DokkaDescriptorVisitor( private val sourceSet: DokkaSourceSet, private val resolutionFacade: DokkaResolutionFacade, private val logger: DokkaLogger -) : DeclarationDescriptorVisitorEmptyBodies<Documentable, DRIWithPlatformInfo>() { - override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor, parent: DRIWithPlatformInfo): Nothing { - throw IllegalStateException("${javaClass.simpleName} should never enter ${descriptor.javaClass.simpleName}") - } - +) { private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter { it.toSourceElement.containingFile.toString().let { path -> path.isNotBlank() && sourceSet.sourceRoots.any { root -> @@ -112,26 +112,34 @@ private class DokkaDescriptorVisitor( private fun <T> T.toSourceSetDependent() = mapOf(sourceSet to this) - override fun visitPackageFragmentDescriptor( + suspend fun visitPackageFragmentDescriptor( descriptor: PackageFragmentDescriptor, parent: DRIWithPlatformInfo ): DPackage { val name = descriptor.fqName.asString().takeUnless { it.isBlank() } ?: "" val driWithPlatform = DRI(packageName = name).withEmptyInfo() val scope = descriptor.getMemberScope() - - return DPackage( - dri = driWithPlatform.dri, - functions = scope.functions(driWithPlatform, true), - properties = scope.properties(driWithPlatform, true), - classlikes = scope.classlikes(driWithPlatform, true), - typealiases = scope.typealiases(driWithPlatform, true), - documentation = descriptor.resolveDescriptorData(), - sourceSets = setOf(sourceSet) - ) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind(true) + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + val typealiases = async { descriptorsWithKind.typealiases.visitTypealiases(driWithPlatform) } + + DPackage( + dri = driWithPlatform.dri, + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + typealiases = typealiases.await(), + documentation = descriptor.resolveDescriptorData(), + sourceSets = setOf(sourceSet) + ) + } } - override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClasslike = + private suspend fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClasslike = when (descriptor.kind) { ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent) ClassKind.OBJECT -> objectDescriptor(descriptor, parent) @@ -140,37 +148,46 @@ private class DokkaDescriptorVisitor( else -> classDescriptor(descriptor, parent) } - private fun interfaceDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DInterface { + private suspend fun interfaceDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DInterface { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect val isActual = descriptor.isActual val info = descriptor.resolveClassDescriptionData() - return DInterface( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - functions = scope.functions(driWithPlatform), - properties = scope.properties(driWithPlatform), - classlikes = scope.classlikes(driWithPlatform), - sources = descriptor.createSources(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - documentation = info.docs, - generics = descriptor.declaredTypeParameters.map { it.toVariantTypeParameter() }, - companion = descriptor.companion(driWithPlatform), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind() + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + + DInterface( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + sources = descriptor.createSources(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + documentation = info.docs, + generics = generics.await(), + companion = descriptor.companion(driWithPlatform), + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) ) - ) + } } - private fun objectDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DObject { + private suspend fun objectDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DObject { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect @@ -178,109 +195,149 @@ private class DokkaDescriptorVisitor( val info = descriptor.resolveClassDescriptionData() - return DObject( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - functions = scope.functions(driWithPlatform), - properties = scope.properties(driWithPlatform), - classlikes = scope.classlikes(driWithPlatform), - sources = descriptor.createSources(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - documentation = info.docs, - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind() + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + + DObject( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + sources = descriptor.createSources(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + documentation = info.docs, + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) ) - ) + } + + } - private fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnum { + private suspend fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnum { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect val isActual = descriptor.isActual val info = descriptor.resolveClassDescriptionData() - return DEnum( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - entries = scope.enumEntries(driWithPlatform), - constructors = descriptor.constructors.map { visitConstructorDescriptor(it, driWithPlatform) }, - functions = scope.functions(driWithPlatform), - properties = scope.properties(driWithPlatform), - classlikes = scope.classlikes(driWithPlatform), - sources = descriptor.createSources(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - documentation = info.docs, - companion = descriptor.companion(driWithPlatform), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind() + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + val constructors = async { descriptor.constructors.parallelMap { visitConstructorDescriptor(it, driWithPlatform) } } + val entries = async { descriptorsWithKind.enumEntries.visitEnumEntries(driWithPlatform) } + + DEnum( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + entries = entries.await(), + constructors = constructors.await(), + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + sources = descriptor.createSources(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + documentation = info.docs, + companion = descriptor.companion(driWithPlatform), + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) ) - ) + } } - private fun enumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnumEntry { + private suspend fun visitEnumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnumEntry { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect - return DEnumEntry( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - documentation = descriptor.resolveDescriptorData(), - classlikes = scope.classlikes(driWithPlatform), - functions = scope.functions(driWithPlatform), - properties = scope.properties(driWithPlatform), - sourceSets = setOf(sourceSet), - expectPresentInSet = sourceSet.takeIf { isExpect }, - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ConstructorValues(descriptor.getAppliedConstructorParameters().toSourceSetDependent()) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind() + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + + DEnumEntry( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + documentation = descriptor.resolveDescriptorData(), + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + sourceSets = setOf(sourceSet), + expectPresentInSet = sourceSet.takeIf { isExpect }, + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ConstructorValues(descriptor.getAppliedConstructorParameters().toSourceSetDependent()) + ) ) - ) + } } - fun annotationDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DAnnotation { + private suspend fun annotationDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DAnnotation { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect val isActual = descriptor.isActual - return DAnnotation( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - documentation = descriptor.resolveDescriptorData(), - classlikes = scope.classlikes(driWithPlatform), - functions = scope.functions(driWithPlatform), - properties = scope.properties(driWithPlatform), - expectPresentInSet = sourceSet.takeIf { isExpect }, - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ), - companion = descriptor.companionObjectDescriptor?.let { objectDescriptor(it, driWithPlatform) }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - generics = descriptor.declaredTypeParameters.map { it.toVariantTypeParameter() }, - constructors = descriptor.constructors.map { visitConstructorDescriptor(it, driWithPlatform) }, - sources = descriptor.createSources() - ) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind() + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + val constructors = async { descriptor.constructors.parallelMap { visitConstructorDescriptor(it, driWithPlatform) } } + + DAnnotation( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + documentation = descriptor.resolveDescriptorData(), + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ), + companion = descriptor.companionObjectDescriptor?.let { objectDescriptor(it, driWithPlatform) }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + generics = generics.await(), + constructors = constructors.await(), + sources = descriptor.createSources() + ) + } + + } - private fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClass { + private suspend fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClass { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() val scope = descriptor.unsubstitutedMemberScope val isExpect = descriptor.isExpect @@ -288,163 +345,188 @@ private class DokkaDescriptorVisitor( val info = descriptor.resolveClassDescriptionData() val actual = descriptor.createSources() - return DClass( - dri = driWithPlatform.dri, - name = descriptor.name.asString(), - constructors = descriptor.constructors.map { - visitConstructorDescriptor( - it, - if (it.isPrimary) DRIWithPlatformInfo(driWithPlatform.dri, actual) - else DRIWithPlatformInfo(driWithPlatform.dri, emptyMap()) + return coroutineScope { + val descriptorsWithKind = scope.getDescriptorsWithKind() + + val functions = async { descriptorsWithKind.functions.visitFunctions(driWithPlatform) } + val properties = async { descriptorsWithKind.properties.visitProperties(driWithPlatform) } + val classlikes = async { descriptorsWithKind.classlikes.visitClasslikes(driWithPlatform) } + val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + val constructors = async { + descriptor.constructors.parallelMap { + visitConstructorDescriptor( + it, + if (it.isPrimary) DRIWithPlatformInfo(driWithPlatform.dri, actual) + else DRIWithPlatformInfo(driWithPlatform.dri, emptyMap()) + ) + } + } + + DClass( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + constructors = constructors.await(), + functions = functions.await(), + properties = properties.await(), + classlikes = classlikes.await(), + sources = actual, + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + generics = generics.await(), + documentation = info.docs, + modifier = descriptor.modifier().toSourceSetDependent(), + companion = descriptor.companion(driWithPlatform), + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll<DClass>( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) ) - }, - functions = scope.functions(driWithPlatform), - properties = scope.properties(driWithPlatform), - classlikes = scope.classlikes(driWithPlatform), - sources = actual, - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - supertypes = info.supertypes.toSourceSetDependent(), - generics = descriptor.declaredTypeParameters.map { it.toVariantTypeParameter() }, - documentation = info.docs, - modifier = descriptor.modifier().toSourceSetDependent(), - companion = descriptor.companion(driWithPlatform), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll<DClass>( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), - ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) ) - ) + } } - override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): DProperty { + private suspend fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): DProperty { val dri = parent.dri.copy(callable = Callable.from(descriptor)) val isExpect = descriptor.isExpect val isActual = descriptor.isActual val actual = descriptor.createSources() - return DProperty( - dri = dri, - name = descriptor.name.asString(), - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) - }, - sources = actual, - getter = descriptor.accessors.filterIsInstance<PropertyGetterDescriptor>().singleOrNull()?.let { - visitPropertyAccessorDescriptor(it, descriptor, dri) - }, - setter = descriptor.accessors.filterIsInstance<PropertySetterDescriptor>().singleOrNull()?.let { - visitPropertyAccessorDescriptor(it, descriptor, dri) - }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = descriptor.resolveDescriptorData(), - modifier = descriptor.modifier().toSourceSetDependent(), - type = descriptor.returnType!!.toBound(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - sourceSets = setOf(sourceSet), - generics = descriptor.typeParameters.map { it.toVariantTypeParameter() }, - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll(listOfNotNull( - (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() - .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations(), - descriptor.getDefaultValue()?.let { DefaultValue(it) } - )) - ) + + return coroutineScope { + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + + DProperty( + dri = dri, + name = descriptor.name.asString(), + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + }, + sources = actual, + getter = descriptor.accessors.filterIsInstance<PropertyGetterDescriptor>().singleOrNull()?.let { + visitPropertyAccessorDescriptor(it, descriptor, dri) + }, + setter = descriptor.accessors.filterIsInstance<PropertySetterDescriptor>().singleOrNull()?.let { + visitPropertyAccessorDescriptor(it, descriptor, dri) + }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = descriptor.resolveDescriptorData(), + modifier = descriptor.modifier().toSourceSetDependent(), + type = descriptor.returnType!!.toBound(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + sourceSets = setOf(sourceSet), + generics = generics.await(), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll(listOfNotNull( + (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() + .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations(), + descriptor.getDefaultValue()?.let { DefaultValue(it) } + )) + ) + } } - fun CallableMemberDescriptor.createDRI(wasOverridenBy: DRI? = null): Pair<DRI, DRI?> = + private fun CallableMemberDescriptor.createDRI(wasOverridenBy: DRI? = null): Pair<DRI, DRI?> = if (kind == CallableMemberDescriptor.Kind.DECLARATION || overriddenDescriptors.isEmpty()) Pair(DRI.from(this), wasOverridenBy) else overriddenDescriptors.first().createDRI(DRI.from(this)) - override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): DFunction { + private suspend fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): DFunction { val (dri, inheritedFrom) = descriptor.createDRI() val isExpect = descriptor.isExpect val isActual = descriptor.isActual val actual = descriptor.createSources() - return DFunction( - dri = dri, - name = descriptor.name.asString(), - isConstructor = false, - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) - }, - parameters = descriptor.valueParameters.mapIndexed { index, desc -> - parameter(index, desc, DRIWithPlatformInfo(dri, actual)) - }, - expectPresentInSet = sourceSet.takeIf { isExpect }, - sources = actual, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - generics = descriptor.typeParameters.map { it.toVariantTypeParameter() }, - documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED } - ?.resolveDescriptorData() ?: emptyMap(), - modifier = descriptor.modifier().toSourceSetDependent(), - type = descriptor.returnType!!.toBound(), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - InheritedFunction(inheritedFrom.toSourceSetDependent()), - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + return coroutineScope { + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + + DFunction( + dri = dri, + name = descriptor.name.asString(), + isConstructor = false, + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + }, + parameters = descriptor.valueParameters.mapIndexed { index, desc -> + parameter(index, desc, DRIWithPlatformInfo(dri, actual)) + }, + expectPresentInSet = sourceSet.takeIf { isExpect }, + sources = actual, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + generics = generics.await(), + documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED } + ?.resolveDescriptorData() ?: emptyMap(), + modifier = descriptor.modifier().toSourceSetDependent(), + type = descriptor.returnType!!.toBound(), + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll( + InheritedFunction(inheritedFrom.toSourceSetDependent()), + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ) ) - ) + } } - override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): DFunction { + suspend fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): DFunction { val name = descriptor.constructedClass.name.toString() val dri = parent.dri.copy(callable = Callable.from(descriptor, name)) val actual = descriptor.createSources() val isExpect = descriptor.isExpect val isActual = descriptor.isActual - return DFunction( - dri = dri, - name = name, - isConstructor = true, - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) - }, - parameters = descriptor.valueParameters.mapIndexed { index, desc -> - parameter(index, desc, DRIWithPlatformInfo(dri, actual)) - }, - sources = actual, - expectPresentInSet = sourceSet.takeIf { isExpect }, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = descriptor.resolveDescriptorData().let { sourceSetDependent -> - if (descriptor.isPrimary) { - sourceSetDependent.map { entry -> - Pair( - entry.key, - entry.value.copy(children = (entry.value.children.find { it is Constructor }?.root?.let { constructor -> - listOf(Description(constructor)) - } ?: emptyList<TagWrapper>()) + entry.value.children.filterIsInstance<Param>())) - }.toMap() - } else { - sourceSetDependent + return coroutineScope { + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + + DFunction( + dri = dri, + name = name, + isConstructor = true, + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + }, + parameters = descriptor.valueParameters.mapIndexed { index, desc -> + parameter(index, desc, DRIWithPlatformInfo(dri, actual)) + }, + sources = actual, + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = descriptor.resolveDescriptorData().let { sourceSetDependent -> + if (descriptor.isPrimary) { + sourceSetDependent.map { entry -> + Pair( + entry.key, + entry.value.copy(children = (entry.value.children.find { it is Constructor }?.root?.let { constructor -> + listOf(Description(constructor)) + } ?: emptyList<TagWrapper>()) + entry.value.children.filterIsInstance<Param>())) + }.toMap() + } else { + sourceSetDependent + } + }, + type = descriptor.returnType.toBound(), + modifier = descriptor.modifier().toSourceSetDependent(), + generics = generics.await(), + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll<DFunction>( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ).let { + if (descriptor.isPrimary) { + it + PrimaryConstructorExtra + } else it } - }, - type = descriptor.returnType.toBound(), - modifier = descriptor.modifier().toSourceSetDependent(), - generics = descriptor.typeParameters.map { it.toVariantTypeParameter() }, - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll<DFunction>( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() - ).let { - if (descriptor.isPrimary) { - it + PrimaryConstructorExtra - } else it - } - ) + ) + } } - override fun visitReceiverParameterDescriptor( + private suspend fun visitReceiverParameterDescriptor( descriptor: ReceiverParameterDescriptor, parent: DRIWithPlatformInfo ) = DParameter( @@ -457,7 +539,7 @@ private class DokkaDescriptorVisitor( extra = PropertyContainer.withAll(descriptor.getAnnotations().toSourceSetDependent().toAnnotations()) ) - private fun visitPropertyAccessorDescriptor( + private suspend fun visitPropertyAccessorDescriptor( descriptor: PropertyAccessorDescriptor, propertyDescriptor: PropertyDescriptor, parent: DRI @@ -467,7 +549,7 @@ private class DokkaDescriptorVisitor( val isExpect = descriptor.isExpect val isActual = descriptor.isActual - fun PropertyDescriptor.asParameter(parent: DRI) = + suspend fun PropertyDescriptor.asParameter(parent: DRI) = DParameter( parent.copy(target = PointingToCallableParameters(parameterIndex = 1)), this.name.asString(), @@ -494,52 +576,60 @@ private class DokkaDescriptorVisitor( listOf(propertyDescriptor.asParameter(dri)) } - return DFunction( - dri, - name, - isConstructor = false, - parameters = parameters, - visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = descriptor.resolveDescriptorData(), - type = descriptor.returnType!!.toBound(), - generics = descriptor.typeParameters.map { it.toVariantTypeParameter() }, - modifier = descriptor.modifier().toSourceSetDependent(), - expectPresentInSet = sourceSet.takeIf { isExpect }, - receiver = descriptor.extensionReceiverParameter?.let { - visitReceiverParameterDescriptor( - it, - DRIWithPlatformInfo(dri, descriptor.createSources()) + return coroutineScope { + val generics = async { descriptor.typeParameters.parallelMap { it.toVariantTypeParameter() } } + + DFunction( + dri, + name, + isConstructor = false, + parameters = parameters, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = descriptor.resolveDescriptorData(), + type = descriptor.returnType!!.toBound(), + generics = generics.await(), + modifier = descriptor.modifier().toSourceSetDependent(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor( + it, + DRIWithPlatformInfo(dri, descriptor.createSources()) + ) + }, + sources = descriptor.createSources(), + sourceSets = setOf(sourceSet), + isExpectActual = (isExpect || isActual), + extra = PropertyContainer.withAll<DFunction>( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() ) - }, - sources = descriptor.createSources(), - sourceSets = setOf(sourceSet), - isExpectActual = (isExpect || isActual), - extra = PropertyContainer.withAll( - descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() ) - ) + } } - override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, parent: DRIWithPlatformInfo?) = + private suspend fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, parent: DRIWithPlatformInfo?) = with(descriptor) { - DTypeAlias( - dri = DRI.from(this), - name = name.asString(), - type = defaultType.toBound(), - expectPresentInSet = null, - underlyingType = underlyingType.toBound().toSourceSetDependent(), - visibility = visibility.toDokkaVisibility().toSourceSetDependent(), - documentation = resolveDescriptorData(), - sourceSets = setOf(sourceSet), - generics = descriptor.declaredTypeParameters.map { it.toVariantTypeParameter() }, - extra = PropertyContainer.withAll( - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + coroutineScope { + val generics = async { descriptor.declaredTypeParameters.parallelMap { it.toVariantTypeParameter() } } + + DTypeAlias( + dri = DRI.from(this@with), + name = name.asString(), + type = defaultType.toBound(), + expectPresentInSet = null, + underlyingType = underlyingType.toBound().toSourceSetDependent(), + visibility = visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = resolveDescriptorData(), + sourceSets = setOf(sourceSet), + generics = generics.await(), + extra = PropertyContainer.withAll( + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ) ) - ) + } } - private fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) = + private suspend fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) = DParameter( dri = parent.dri.copy(target = PointingToCallableParameters(index)), name = descriptor.name.asString(), @@ -554,77 +644,100 @@ private class DokkaDescriptorVisitor( )) ) - private fun MemberScope.getContributedDescriptors(kindFilter: DescriptorKindFilter, shouldFilter: Boolean) = - getContributedDescriptors(kindFilter) { true }.let { + private data class DescriptorsWithKind( + val functions: List<FunctionDescriptor>, + val properties: List<PropertyDescriptor>, + val classlikes: List<ClassDescriptor>, + val typealiases: List<TypeAliasDescriptor>, + val enumEntries: List<ClassDescriptor> + ) + + private suspend fun MemberScope.getDescriptorsWithKind(shouldFilter: Boolean = false): DescriptorsWithKind { + val descriptors = getContributedDescriptors { true }.let { if (shouldFilter) it.filterDescriptorsInSourceSet() else it } - private fun MemberScope.functions(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DFunction> = - getContributedDescriptors(DescriptorKindFilter.FUNCTIONS, packageLevel) - .filterIsInstance<FunctionDescriptor>() - .map { visitFunctionDescriptor(it, parent) } + class EnumEntryDescriptor + + val groupedDescriptors = descriptors.groupBy { when { + it is FunctionDescriptor -> FunctionDescriptor::class + it is PropertyDescriptor -> PropertyDescriptor::class + it is ClassDescriptor && it.kind != ClassKind.ENUM_ENTRY -> ClassDescriptor::class + it is TypeAliasDescriptor -> TypeAliasDescriptor::class + it is ClassDescriptor && it.kind == ClassKind.ENUM_ENTRY -> EnumEntryDescriptor::class + else -> IllegalStateException::class + } } + + return DescriptorsWithKind( + (groupedDescriptors[FunctionDescriptor::class] ?: emptyList()) as List<FunctionDescriptor>, + (groupedDescriptors[PropertyDescriptor::class] ?: emptyList()) as List<PropertyDescriptor>, + (groupedDescriptors[ClassDescriptor::class] ?: emptyList()) as List<ClassDescriptor>, + (groupedDescriptors[TypeAliasDescriptor::class] ?: emptyList()) as List<TypeAliasDescriptor>, + (groupedDescriptors[EnumEntryDescriptor::class] ?: emptyList()) as List<ClassDescriptor> + ) + } - private fun MemberScope.properties(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DProperty> = - getContributedDescriptors(DescriptorKindFilter.VALUES, packageLevel) - .filterIsInstance<PropertyDescriptor>() - .map { visitPropertyDescriptor(it, parent) } + private suspend fun List<FunctionDescriptor>.visitFunctions(parent: DRIWithPlatformInfo): List<DFunction> = + coroutineScope { parallelMap { visitFunctionDescriptor(it, parent) } } - private fun MemberScope.classlikes(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DClasslike> = - getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS, packageLevel) - .filter { it is ClassDescriptor && it.kind != ClassKind.ENUM_ENTRY } - .map { visitClassDescriptor(it as ClassDescriptor, parent) } + private suspend fun List<PropertyDescriptor>.visitProperties(parent: DRIWithPlatformInfo): List<DProperty> = + coroutineScope { parallelMap { visitPropertyDescriptor(it, parent) } } - private fun MemberScope.typealiases(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DTypeAlias> = - getContributedDescriptors(DescriptorKindFilter.TYPE_ALIASES, packageLevel) - .filterIsInstance<TypeAliasDescriptor>() - .map { visitTypeAliasDescriptor(it, parent) } + private suspend fun List<ClassDescriptor>.visitClasslikes(parent: DRIWithPlatformInfo): List<DClasslike> = + coroutineScope { parallelMap { visitClassDescriptor(it, parent) } } - private fun MemberScope.enumEntries(parent: DRIWithPlatformInfo): List<DEnumEntry> = - this.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) { true } - .filterIsInstance<ClassDescriptor>() - .filter { it.kind == ClassKind.ENUM_ENTRY } - .map { enumEntryDescriptor(it, parent) } + private suspend fun List<TypeAliasDescriptor>.visitTypealiases(parent: DRIWithPlatformInfo): List<DTypeAlias> = + coroutineScope { parallelMap { visitTypeAliasDescriptor(it, parent) } } + private suspend fun List<ClassDescriptor>.visitEnumEntries(parent: DRIWithPlatformInfo): List<DEnumEntry> = + coroutineScope { parallelMap { visitEnumEntryDescriptor(it, parent) } } - private fun DeclarationDescriptor.resolveDescriptorData(): SourceSetDependent<DocumentationNode> = + private suspend fun DeclarationDescriptor.resolveDescriptorData(): SourceSetDependent<DocumentationNode> = getDocumentation()?.toSourceSetDependent() ?: emptyMap() - private fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo { - fun toTypeConstructor(kt: KotlinType) = - GenericTypeConstructor( - DRI.from(kt.constructor.declarationDescriptor as DeclarationDescriptor), - kt.arguments.map { it.toProjection() }) + private suspend fun toTypeConstructor(kt: KotlinType) = + GenericTypeConstructor( + DRI.from(kt.constructor.declarationDescriptor as DeclarationDescriptor), + kt.arguments.map { it.toProjection() }) - tailrec fun buildAncestryInformation( - supertypes: Collection<KotlinType>, - level: Int = 0, - ancestryInformation: Set<AncestryLevel> = emptySet() - ): Set<AncestryLevel> { - if (supertypes.isEmpty()) return ancestryInformation - val (interfaces, superclass) = supertypes.partition { - (it.constructor.declarationDescriptor as ClassDescriptor).kind == ClassKind.INTERFACE - } + private tailrec suspend fun buildAncestryInformation( + supertypes: Collection<KotlinType>, + level: Int = 0, + ancestryInformation: Set<AncestryLevel> = emptySet() + ): Set<AncestryLevel> { + if (supertypes.isEmpty()) return ancestryInformation + + val (interfaces, superclass) = supertypes.partition { + (it.constructor.declarationDescriptor as ClassDescriptor).kind == ClassKind.INTERFACE + } - val updated = ancestryInformation + AncestryLevel( + val updated = coroutineScope { + ancestryInformation + AncestryLevel( level, - superclass.map(::toTypeConstructor).singleOrNull(), - interfaces.map(::toTypeConstructor) - ) - return buildAncestryInformation( - supertypes = supertypes.flatMap { it.supertypes() }, - level = level + 1, - ancestryInformation = updated + superclass.parallelMap(::toTypeConstructor).singleOrNull(), + interfaces.parallelMap(::toTypeConstructor) ) } - return ClassInfo( - buildAncestryInformation(this.typeConstructor.supertypes.filterNot { it.isAnyOrNullableAny() }).sortedBy { it.level }, - resolveDescriptorData() + + return buildAncestryInformation( + supertypes = supertypes.flatMap { it.supertypes() }, + level = level + 1, + ancestryInformation = updated ) } - private fun TypeParameterDescriptor.toVariantTypeParameter() = + private suspend fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo { + return coroutineScope { + ClassInfo( + buildAncestryInformation(this@resolveClassDescriptionData.typeConstructor.supertypes.filterNot { it.isAnyOrNullableAny() }).sortedBy { it.level }, + resolveDescriptorData() + ) + } + } + + private suspend fun TypeParameterDescriptor.toVariantTypeParameter() = DTypeParameter( variantTypeParameter( TypeParameter(DRI.from(this), name.identifier, annotations.getPresentableName()) @@ -636,11 +749,12 @@ private class DokkaDescriptorVisitor( extra = PropertyContainer.withAll(additionalExtras().toSourceSetDependent().toAdditionalModifiers()) ) - private fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? = + private suspend fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? = map { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name") .safeAs<StringValue>()?.value?.drop(1)?.dropLast(1) // Dropping enclosing doublequotes because we don't want to have it in our custom signature serializer - private fun KotlinType.toBound(): Bound = when (this) { + private suspend fun KotlinType.toBound(): Bound = when (this) { + is DynamicType -> Dynamic is AbbreviatedType -> TypeAliased( abbreviation.toBound(), @@ -669,20 +783,23 @@ private class DokkaDescriptorVisitor( } } - private fun TypeProjection.toProjection(): Projection = + private suspend fun TypeProjection.toProjection(): Projection = if (isStarProjection) Star else formPossiblyVariant() - private fun TypeProjection.formPossiblyVariant(): Projection = type.toBound().wrapWithVariance(projectionKind) + private suspend fun TypeProjection.formPossiblyVariant(): Projection = + type.toBound().wrapWithVariance(projectionKind) - private fun TypeParameterDescriptor.variantTypeParameter(type: TypeParameter) = type.wrapWithVariance(variance) + private suspend fun TypeParameterDescriptor.variantTypeParameter(type: TypeParameter) = + type.wrapWithVariance(variance) - private fun <T : Bound> T.wrapWithVariance(variance: org.jetbrains.kotlin.types.Variance) = when (variance) { - org.jetbrains.kotlin.types.Variance.INVARIANT -> Invariance(this) - org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Contravariance(this) - org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Covariance(this) - } + private fun <T : Bound> T.wrapWithVariance(variance: org.jetbrains.kotlin.types.Variance) = + when (variance) { + org.jetbrains.kotlin.types.Variance.INVARIANT -> Invariance(this) + org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Contravariance(this) + org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Covariance(this) + } - private fun DeclarationDescriptor.getDocumentation() = findKDoc().let { + private suspend fun DeclarationDescriptor.getDocumentation() = findKDoc().let { MarkdownParser.parseFromKDocTag( kDocTag = it, externalDri = { link: String -> @@ -702,7 +819,7 @@ private class DokkaDescriptorVisitor( ) }.takeIf { it.children.isNotEmpty() } - private fun ClassDescriptor.companion(dri: DRIWithPlatformInfo): DObject? = companionObjectDescriptor?.let { + private suspend fun ClassDescriptor.companion(dri: DRIWithPlatformInfo): DObject? = companionObjectDescriptor?.let { objectDescriptor(it, dri) } @@ -756,9 +873,9 @@ private class DokkaDescriptorVisitor( ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { DescriptorUtils.isOverride(this) } ) - private fun Annotated.getAnnotations() = annotations.mapNotNull { it.toAnnotation() } + private suspend fun Annotated.getAnnotations() = annotations.parallelMapNotNull { it.toAnnotation() } - private fun ConstantValue<*>.toValue(): AnnotationParameterValue? = when (this) { + private suspend fun ConstantValue<*>.toValue(): AnnotationParameterValue? = when (this) { is ConstantsAnnotationValue -> value.toAnnotation()?.let { AnnotationValue(it) } is ConstantsArrayValue -> ArrayValue(value.mapNotNull { it.toValue() }) is ConstantsEnumValue -> EnumValue( @@ -782,7 +899,7 @@ private class DokkaDescriptorVisitor( else -> StringValue(toString()) } - private fun AnnotationDescriptor.toAnnotation(): Annotations.Annotation { + private suspend fun AnnotationDescriptor.toAnnotation(): Annotations.Annotation { return Annotations.Annotation( DRI.from(annotationClass as DeclarationDescriptor), allValueArguments.map { it.key.asString() to it.value.toValue() }.filter { @@ -792,7 +909,7 @@ private class DokkaDescriptorVisitor( ) } - private fun PropertyDescriptor.getAnnotationsWithBackingField(): List<Annotations.Annotation> = + private suspend fun PropertyDescriptor.getAnnotationsWithBackingField(): List<Annotations.Annotation> = getAnnotations() + (backingField?.getAnnotations() ?: emptyList()) private fun List<Annotations.Annotation>.toAdditionalExtras() = mapNotNull { @@ -803,24 +920,25 @@ private class DokkaDescriptorVisitor( } } - private fun ValueParameterDescriptor.getDefaultValue(): String? = + private suspend fun ValueParameterDescriptor.getDefaultValue(): String? = (source as? KotlinSourceElement)?.psi?.children?.find { it is KtExpression }?.text - private fun PropertyDescriptor.getDefaultValue(): String? = + private suspend fun PropertyDescriptor.getDefaultValue(): String? = (source as? KotlinSourceElement)?.psi?.children?.find { it is KtConstantExpression }?.text - private fun ClassDescriptor.getAppliedConstructorParameters() = + private suspend fun ClassDescriptor.getAppliedConstructorParameters() = (source as PsiSourceElement).psi?.children?.flatMap { it.safeAs<KtInitializerList>()?.initializersAsText().orEmpty() }.orEmpty() - private fun KtInitializerList.initializersAsText() = + private suspend fun KtInitializerList.initializersAsText() = initializers.firstIsInstanceOrNull<KtCallElement>() ?.getValueArgumentsInParentheses() ?.flatMap { it.childrenAsText() } .orEmpty() - private fun ValueArgument.childrenAsText() = this.safeAs<KtValueArgument>()?.children?.map { it.text }.orEmpty() + private fun ValueArgument.childrenAsText() = + this.safeAs<KtValueArgument>()?.children?.map { it.text }.orEmpty() private data class AncestryLevel( val level: Int, @@ -853,5 +971,4 @@ private class DokkaDescriptorVisitor( private fun ConstantsEnumValue.fullEnumEntryName() = "${this.enumClassId.relativeClassName.asString()}.${this.enumEntryName.identifier}" - } diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 39cb3c89..cbd02158 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -8,6 +8,8 @@ import com.intellij.psi.* import com.intellij.psi.impl.source.PsiClassReferenceType import com.intellij.psi.impl.source.PsiImmediateClassType import com.intellij.psi.javadoc.PsiDocComment +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.analysis.KotlinAnalysis import org.jetbrains.dokka.analysis.PsiDocumentableSource @@ -22,6 +24,9 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.dokka.utilities.parallelForEach +import org.jetbrains.dokka.utilities.parallelMap +import org.jetbrains.dokka.utilities.parallelMapNotNull import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys @@ -44,42 +49,45 @@ class DefaultPsiToDocumentableTranslator( private val kotlinAnalysis: KotlinAnalysis ) : SourceToDocumentableTranslator { - override fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { + override suspend fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { + return coroutineScope { + fun isFileInSourceRoots(file: File): Boolean = + sourceSet.sourceRoots.any { root -> file.startsWith(root) } - fun isFileInSourceRoots(file: File): Boolean = - sourceSet.sourceRoots.any { root -> file.startsWith(root) } + val (environment, _) = kotlinAnalysis[sourceSet] - val (environment, _) = kotlinAnalysis[sourceSet] + val sourceRoots = environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) + ?.filterIsInstance<JavaSourceRoot>() + ?.mapNotNull { it.file.takeIf(::isFileInSourceRoots) } + ?: listOf() + val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") - val sourceRoots = environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<JavaSourceRoot>() - ?.mapNotNull { it.file.takeIf(::isFileInSourceRoots) } - ?: listOf() - val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") + val psiFiles = sourceRoots.parallelMap { sourceRoot -> + sourceRoot.absoluteFile.walkTopDown().mapNotNull { + localFileSystem.findFileByPath(it.path)?.let { vFile -> + PsiManager.getInstance(environment.project).findFile(vFile) as? PsiJavaFile + } + }.toList() + }.flatten() - val psiFiles = sourceRoots.map { sourceRoot -> - sourceRoot.absoluteFile.walkTopDown().mapNotNull { - localFileSystem.findFileByPath(it.path)?.let { vFile -> - PsiManager.getInstance(environment.project).findFile(vFile) as? PsiJavaFile - } - }.toList() - }.flatten() + val docParser = + DokkaPsiParser( + sourceSet, + context.logger + ) - val docParser = - DokkaPsiParser( - sourceSet, - context.logger + DModule( + context.configuration.moduleName, + psiFiles.parallelMapNotNull { it.safeAs<PsiJavaFile>() }.groupBy { it.packageName }.toList() + .parallelMap { (packageName: String, psiFiles: List<PsiJavaFile>) -> + docParser.parsePackage(packageName, psiFiles) + }, + emptyMap(), + null, + setOf(sourceSet) ) - return DModule( - context.configuration.moduleName, - psiFiles.mapNotNull { it.safeAs<PsiJavaFile>() }.groupBy { it.packageName }.map { (packageName, psiFiles) -> - docParser.parsePackage(packageName, psiFiles) - }, - emptyMap(), - null, - setOf(sourceSet) - ) + } } class DokkaPsiParser( @@ -117,19 +125,21 @@ class DefaultPsiToDocumentableTranslator( private fun <T> T.toSourceSetDependent() = mapOf(sourceSetData to this) - fun parsePackage(packageName: String, psiFiles: List<PsiJavaFile>): DPackage { + suspend fun parsePackage(packageName: String, psiFiles: List<PsiJavaFile>): DPackage = coroutineScope { val dri = DRI(packageName = packageName) val documentation = psiFiles.firstOrNull { it.name == "package-info.java" }?.let { javadocParser.parseDocumentation(it).toSourceSetDependent() } ?: emptyMap() - return DPackage( + DPackage( dri, emptyList(), emptyList(), - psiFiles.flatMap { psiFile -> - psiFile.classes.map { parseClasslike(it, dri) } - }, + psiFiles.parallelMap { psiFile -> + coroutineScope { + psiFile.classes.asIterable().parallelMap { parseClasslike(it, dri) } + } + }.flatten(), emptyList(), documentation, null, @@ -137,85 +147,144 @@ class DefaultPsiToDocumentableTranslator( ) } - - private fun parseClasslike(psi: PsiClass, parent: DRI): DClasslike = with(psi) { - val dri = parent.withClass(name.toString()) - val ancestryTree = mutableListOf<AncestryLevel>() - val superMethodsKeys = hashSetOf<Int>() - val superMethods = mutableListOf<Pair<PsiMethod, DRI>>() - methods.forEach { superMethodsKeys.add(it.hash) } - fun parseSupertypes(superTypes: Array<PsiClassType>, level: Int = 0) { - if (superTypes.isEmpty()) return - val parsedClasses = superTypes.filter { !it.shouldBeIgnored }.mapNotNull { psi -> - psi.resolve()?.let { psiClass -> - val (dri, javaClassKind) = when { - psiClass.isInterface -> DRI.from(psiClass) to JavaClassKindTypes.INTERFACE - else -> DRI.from(psiClass) to JavaClassKindTypes.CLASS + private suspend fun parseClasslike(psi: PsiClass, parent: DRI): DClasslike = coroutineScope { + with(psi) { + val dri = parent.withClass(name.toString()) + val ancestryTree = mutableListOf<AncestryLevel>() + val superMethodsKeys = hashSetOf<Int>() + val superMethods = mutableListOf<Pair<PsiMethod, DRI>>() + methods.asIterable().parallelForEach { superMethodsKeys.add(it.hash) } + fun parseSupertypes(superTypes: Array<PsiClassType>, level: Int = 0) { // TODO: Rewrite it + if (superTypes.isEmpty()) return + val parsedClasses = superTypes.filter { !it.shouldBeIgnored }.mapNotNull { psi -> + psi.resolve()?.let { psiClass -> + val (dri, javaClassKind) = when { + psiClass.isInterface -> DRI.from(psiClass) to JavaClassKindTypes.INTERFACE + else -> DRI.from(psiClass) to JavaClassKindTypes.CLASS + } + GenericTypeConstructor( + dri, + psi.parameters.map(::getProjection) + ) to javaClassKind } - GenericTypeConstructor( - dri, - psi.parameters.map(::getProjection) - ) to javaClassKind } - } - val (classes, interfaces) = parsedClasses.partition { it.second == JavaClassKindTypes.CLASS } - ancestryTree.add(AncestryLevel(level, classes.firstOrNull()?.first, interfaces.map { it.first })) - - superTypes.forEach { type -> - (type as? PsiClassType)?.takeUnless { type.shouldBeIgnored }?.resolve()?.let { - val definedAt = DRI.from(it) - it.methods.forEach { method -> - val hash = method.hash - if (!method.isConstructor && !superMethodsKeys.contains(hash) && - method.getVisibility() != Visibilities.PRIVATE - ) { - superMethodsKeys.add(hash) - superMethods.add(Pair(method, definedAt)) + val (classes, interfaces) = parsedClasses.partition { it.second == JavaClassKindTypes.CLASS } + ancestryTree.add(AncestryLevel(level, classes.firstOrNull()?.first, interfaces.map { it.first })) + + superTypes.forEach { type -> + (type as? PsiClassType)?.takeUnless { type.shouldBeIgnored }?.resolve()?.let { + val definedAt = DRI.from(it) + it.methods.forEach { method -> + val hash = method.hash + if (!method.isConstructor && !superMethodsKeys.contains(hash) && + method.getVisibility() != Visibilities.PRIVATE + ) { + superMethodsKeys.add(hash) + superMethods.add(Pair(method, definedAt)) + } } + parseSupertypes(it.superTypes, level + 1) } - parseSupertypes(it.superTypes, level + 1) } } - } - parseSupertypes(superTypes) - val (regularFunctions, accessors) = splitFunctionsAndAccessors() - val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent() - val allFunctions = regularFunctions.mapNotNull { if (!it.isConstructor) parseFunction(it) else null } + - superMethods.map { parseFunction(it.first, inheritedFrom = it.second) } - val source = PsiDocumentableSource(this).toSourceSetDependent() - val classlikes = innerClasses.map { parseClasslike(it, dri) } - val visibility = getVisibility().toSourceSetDependent() - val ancestors = ancestryTree.filter { it.level == 0 }.flatMap { - listOfNotNull(it.superclass?.let { - TypeConstructorWithKind( - typeConstructor = it, - kind = JavaClassKindTypes.CLASS - ) - }) + it.interfaces.map { - TypeConstructorWithKind( - typeConstructor = it, - kind = JavaClassKindTypes.INTERFACE - ) - } - }.toSourceSetDependent() - val modifiers = getModifier().toSourceSetDependent() - val implementedInterfacesExtra = - ImplementedInterfaces(ancestryTree.flatMap { it.interfaces }.distinct().toSourceSetDependent()) - return when { - isAnnotationType -> - DAnnotation( + parseSupertypes(superTypes) + val (regularFunctions, accessors) = splitFunctionsAndAccessors() + val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent() + val allFunctions = async { regularFunctions.parallelMapNotNull { if (!it.isConstructor) parseFunction(it) else null } + + superMethods.parallelMap { parseFunction(it.first, inheritedFrom = it.second) } } + val source = PsiDocumentableSource(this).toSourceSetDependent() + val classlikes = async { innerClasses.asIterable().parallelMap { parseClasslike(it, dri) } } + val visibility = getVisibility().toSourceSetDependent() + val ancestors = ancestryTree.filter { it.level == 0 }.flatMap { + listOfNotNull(it.superclass?.let { + TypeConstructorWithKind( + typeConstructor = it, + kind = JavaClassKindTypes.CLASS + ) + }) + it.interfaces.map { + TypeConstructorWithKind( + typeConstructor = it, + kind = JavaClassKindTypes.INTERFACE + ) + } + }.toSourceSetDependent() + val modifiers = getModifier().toSourceSetDependent() + val implementedInterfacesExtra = + ImplementedInterfaces(ancestryTree.flatMap { it.interfaces }.distinct().toSourceSetDependent()) + when { + isAnnotationType -> + DAnnotation( + name.orEmpty(), + dri, + documentation, + null, + source, + allFunctions.await(), + fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes.await(), + visibility, + null, + constructors.map { parseFunction(it, true) }, + mapTypeParameters(dri), + setOf(sourceSetData), + false, + PropertyContainer.withAll( + implementedInterfacesExtra, + annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) + ) + isEnum -> DEnum( + dri, name.orEmpty(), + fields.filterIsInstance<PsiEnumConstant>().map { entry -> + DEnumEntry( + dri.withClass("${entry.name}"), + entry.name, + javadocParser.parseDocumentation(entry).toSourceSetDependent(), + null, + emptyList(), + emptyList(), + emptyList(), + setOf(sourceSetData), + PropertyContainer.withAll( + implementedInterfacesExtra, + annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) + ) + }, + documentation, + null, + source, + allFunctions.await(), + fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) }, + classlikes.await(), + visibility, + null, + constructors.map { parseFunction(it, true) }, + ancestors, + setOf(sourceSetData), + false, + PropertyContainer.withAll( + implementedInterfacesExtra, + annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) + ) + isInterface -> DInterface( dri, + name.orEmpty(), documentation, null, source, - allFunctions, + allFunctions.await(), fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, - classlikes, + classlikes.await(), visibility, null, - constructors.map { parseFunction(it, true) }, mapTypeParameters(dri), + ancestors, setOf(sourceSetData), false, PropertyContainer.withAll( @@ -224,85 +293,30 @@ class DefaultPsiToDocumentableTranslator( .toAnnotations() ) ) - isEnum -> DEnum( - dri, - name.orEmpty(), - fields.filterIsInstance<PsiEnumConstant>().map { entry -> - DEnumEntry( - dri.withClass("${entry.name}"), - entry.name, - javadocParser.parseDocumentation(entry).toSourceSetDependent(), - null, - emptyList(), - emptyList(), - emptyList(), - setOf(sourceSetData), - PropertyContainer.withAll( - implementedInterfacesExtra, - annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) + else -> DClass( + dri, + name.orEmpty(), + constructors.map { parseFunction(it, true) }, + allFunctions.await(), + fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes.await(), + source, + visibility, + null, + mapTypeParameters(dri), + ancestors, + documentation, + null, + modifiers, + setOf(sourceSetData), + false, + PropertyContainer.withAll( + implementedInterfacesExtra, + annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() ) - }, - documentation, - null, - source, - allFunctions, - fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) }, - classlikes, - visibility, - null, - constructors.map { parseFunction(it, true) }, - ancestors, - setOf(sourceSetData), - false, - PropertyContainer.withAll( - implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - isInterface -> DInterface( - dri, - name.orEmpty(), - documentation, - null, - source, - allFunctions, - fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, - classlikes, - visibility, - null, - mapTypeParameters(dri), - ancestors, - setOf(sourceSetData), - false, - PropertyContainer.withAll( - implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() ) - ) - else -> DClass( - dri, - name.orEmpty(), - constructors.map { parseFunction(it, true) }, - allFunctions, - fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, - classlikes, - source, - visibility, - null, - mapTypeParameters(dri), - ancestors, - documentation, - null, - modifiers, - setOf(sourceSetData), - false, - PropertyContainer.withAll( - implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) + } } } |