diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2019-11-26 13:44:27 +0100 |
---|---|---|
committer | Paweł Marks <pmarks@virtuslab.com> | 2019-11-26 21:02:20 +0100 |
commit | 49439594f86217d8a25e8df2580b8ef29d836230 (patch) | |
tree | 50dc1b0793c4804b382c6b75fd7a3a0d27cb869a /core/src/main/kotlin/transformers | |
parent | c185e420971950747535e98f96a12e480159dd83 (diff) | |
download | dokka-49439594f86217d8a25e8df2580b8ef29d836230.tar.gz dokka-49439594f86217d8a25e8df2580b8ef29d836230.tar.bz2 dokka-49439594f86217d8a25e8df2580b8ef29d836230.zip |
Introduction of all important extension points and restructuring of DokkaGenerator
Diffstat (limited to 'core/src/main/kotlin/transformers')
-rw-r--r-- | core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt | 20 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/PageNodeTransformer.kt | 8 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt | 208 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/descriptors/DescriptorToDocumentationTranslator.kt | 15 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt | 86 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/documentation/DefaultDocumentationToPageTranslator.kt | 24 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/documentation/DocumentationNodeMerger.kt | 8 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/documentation/DocumentationNodeTransformer.kt | 8 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/documentation/DocumentationToPageTranslator.kt (renamed from core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt) | 7 | ||||
-rw-r--r-- | core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt | 8 |
10 files changed, 361 insertions, 31 deletions
diff --git a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt deleted file mode 100644 index b0877527..00000000 --- a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.jetbrains.dokka.transformers - -import org.jetbrains.dokka.DokkaLogger -import org.jetbrains.dokka.Model.Module -import org.jetbrains.dokka.pages.DefaultPageBuilder -import org.jetbrains.dokka.pages.DefaultPageContentBuilder -import org.jetbrains.dokka.pages.MarkdownToContentConverter -import org.jetbrains.dokka.pages.ModulePageNode - - -class DefaultDocumentationToPageTransformer( - private val markdownConverter: MarkdownToContentConverter, - private val logger: DokkaLogger -) : DocumentationToPageTransformer { - override fun transform(module: Module): ModulePageNode = - DefaultPageBuilder { node, kind, operation -> - DefaultPageContentBuilder.group(node.dri, node.platformData, kind, markdownConverter, logger, operation) - }.pageForModule(module) - -}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/PageNodeTransformer.kt b/core/src/main/kotlin/transformers/PageNodeTransformer.kt deleted file mode 100644 index b86e0b34..00000000 --- a/core/src/main/kotlin/transformers/PageNodeTransformer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.jetbrains.dokka.transformers - -import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.plugability.DokkaContext - -interface PageNodeTransformer { - fun invoke(input: ModulePageNode, dokkaContext: DokkaContext): ModulePageNode -}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt new file mode 100644 index 00000000..19aeef92 --- /dev/null +++ b/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt @@ -0,0 +1,208 @@ +package org.jetbrains.dokka.transformers.descriptors + +import org.jetbrains.dokka.DokkaResolutionFacade +import org.jetbrains.dokka.Model.* +import org.jetbrains.dokka.Model.ClassKind +import org.jetbrains.dokka.Model.Function +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.withClass +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.kotlin.descriptors.* +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.kdoc.psi.impl.KDocLink +import org.jetbrains.kotlin.kdoc.psi.impl.KDocName +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.KotlinType + +object DefaultDescriptorToDocumentationTranslator: DescriptorToDocumentationTranslator { + override fun invoke( + packageFragments: Iterable<PackageFragmentDescriptor>, + platformData: PlatformData, + context: DokkaContext + ) = DokkaDescriptorVisitor(platformData, context.platforms[platformData]?.facade!!).run { + packageFragments.map { visitPackageFragmentDescriptor(it, DRI.topLevel) } + }.let { Module(it) } + +} + +class DokkaDescriptorVisitor( + private val platformData: PlatformData, + private val resolutionFacade: DokkaResolutionFacade +) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DRI>() { + override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor, parent: DRI): Nothing { + throw IllegalStateException("${javaClass.simpleName} should never enter ${descriptor.javaClass.simpleName}") + } + + override fun visitPackageFragmentDescriptor( + descriptor: PackageFragmentDescriptor, + parent: DRI + ): Package { + val dri = DRI(packageName = descriptor.fqName.asString()) + val scope = descriptor.getMemberScope() + return Package( + dri, + scope.functions(dri), + scope.properties(dri), + scope.classes(dri) + ) + } + + override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRI): Class { + val dri = parent.withClass(descriptor.name.asString()) + val scope = descriptor.getMemberScope(emptyList()) + val descriptorData = descriptor.takeUnless { it.isExpect }?.resolveClassDescriptionData() + return Class( + dri, + descriptor.name.asString(), + KotlinClassKindTypes.valueOf(descriptor.kind.toString()), + descriptor.constructors.map { visitConstructorDescriptor(it, dri) }, + scope.functions(dri), + scope.properties(dri), + scope.classes(dri), + descriptor.takeIf { it.isExpect }?.resolveClassDescriptionData(), + listOfNotNull(descriptorData), + getXMLDRIs(descriptor, descriptorData).toMutableSet() + ) + } + + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRI): Property { + val dri = parent.copy(callable = Callable.from(descriptor)) + return Property( + dri, + descriptor.name.asString(), + descriptor.extensionReceiverParameter?.let { visitReceiverParameterDescriptor(it, dri) }, + descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), + listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()) + ) + } + + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRI): Function { + val dri = parent.copy(callable = Callable.from(descriptor)) + return Function( + dri, + descriptor.name.asString(), + descriptor.returnType?.let { KotlinTypeWrapper(it) }, + false, + descriptor.extensionReceiverParameter?.let { visitReceiverParameterDescriptor(it, dri) }, + descriptor.valueParameters.mapIndexed { index, desc -> parameter(index, desc, dri) }, + descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), + listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()) + ) + } + + override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRI): Function { + val dri = parent.copy(callable = Callable.from(descriptor)) + return Function( + dri, + "<init>", + KotlinTypeWrapper(descriptor.returnType), + true, + null, + descriptor.valueParameters.mapIndexed { index, desc -> parameter(index, desc, dri) }, + descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), + listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()) + ) + } + + override fun visitReceiverParameterDescriptor( + descriptor: ReceiverParameterDescriptor, + parent: DRI + ) = Parameter( + parent.copy(target = 0), + null, + KotlinTypeWrapper(descriptor.type), + listOf(descriptor.resolveDescriptorData()) + ) + + private fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRI) = + Parameter( + parent.copy(target = index + 1), + descriptor.name.asString(), + KotlinTypeWrapper(descriptor.type), + listOf(descriptor.resolveDescriptorData()) + ) + + private fun MemberScope.functions(parent: DRI): List<Function> = + getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) { true } + .filterIsInstance<FunctionDescriptor>() + .map { visitFunctionDescriptor(it, parent) } + + private fun MemberScope.properties(parent: DRI): List<Property> = + getContributedDescriptors(DescriptorKindFilter.VALUES) { true } + .filterIsInstance<PropertyDescriptor>() + .map { visitPropertyDescriptor(it, parent) } + + private fun MemberScope.classes(parent: DRI): List<Class> = + getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) { true } + .filterIsInstance<ClassDescriptor>() + .map { visitClassDescriptor(it, parent) } + + private fun DeclarationDescriptor.resolveDescriptorData(): PlatformInfo { + val doc = findKDoc() + val links = doc?.children?.filter { it is KDocLink }?.flatMap { link -> + val destination = link.children.first { it is KDocName }.text + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + this, + null, + destination.split('.') + ).map { Pair(destination, DRI.from(it)) } + }?.toMap() ?: emptyMap() + return BasePlatformInfo(doc, links, listOf(platformData)) + } + + private fun ClassDescriptor.resolveClassDescriptionData(): ClassPlatformInfo { + return ClassPlatformInfo(resolveDescriptorData(), + (getSuperInterfaces() + getAllSuperclassesWithoutAny()).map { DRI.from(it) }) + } + + private fun getXMLDRIs(descriptor: DeclarationDescriptor, platformInfo: PlatformInfo?) = + platformInfo?.docTag?.children + ?.filter { + it.text.contains("@attr") + }?.flatMap { ref -> + val matchResult = "@attr\\s+ref\\s+(.+)".toRegex().matchEntire(ref.text) + val toFind = matchResult?.groups?.last()?.value.orEmpty() + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + descriptor, + null, + toFind.split('.') + ).map { XMLMega("@attr ref", DRI.from(it)) } + }.orEmpty() +} + +data class XMLMega(val key: String, val dri: DRI) : Extra + +enum class KotlinClassKindTypes : ClassKind { + CLASS, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS, + OBJECT; +} + +class KotlinTypeWrapper(private val kotlinType: KotlinType) : TypeWrapper { + private val declarationDescriptor = kotlinType.constructor.declarationDescriptor + private val fqNameSafe = declarationDescriptor?.fqNameSafe + override val constructorFqName = fqNameSafe?.asString() + override val constructorNamePathSegments: List<String> = + fqNameSafe?.pathSegments()?.map { it.asString() } ?: emptyList() + override val arguments: List<KotlinTypeWrapper> by lazy { kotlinType.arguments.map { + KotlinTypeWrapper( + it.type + ) + } } + override val dri: DRI? by lazy { declarationDescriptor?.let { DRI.from(it) } } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/descriptors/DescriptorToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/descriptors/DescriptorToDocumentationTranslator.kt new file mode 100644 index 00000000..d08aba21 --- /dev/null +++ b/core/src/main/kotlin/transformers/descriptors/DescriptorToDocumentationTranslator.kt @@ -0,0 +1,15 @@ +package org.jetbrains.dokka.transformers.descriptors + +import org.jetbrains.dokka.Model.Module +import org.jetbrains.dokka.Model.Package +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor + +interface DescriptorToDocumentationTranslator { + fun invoke( + packageFragments: Iterable<PackageFragmentDescriptor>, + platformData: PlatformData, + context: DokkaContext + ): Module +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt new file mode 100644 index 00000000..46ba2816 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt @@ -0,0 +1,86 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.Model.* +import org.jetbrains.dokka.Model.Function +import org.jetbrains.dokka.plugability.DokkaContext + +internal object DefaultDocumentationNodeMerger : DocumentationNodeMerger { + override fun invoke(modules: Collection<Module>, context: DokkaContext): Module = + Module( + merge( + modules.flatMap { it.packages }, + Package::mergeWith + ) + ) +} + +private fun <T: DocumentationNode> merge(elements: List<T>, reducer: (T, T) -> T): List<T> = + elements.groupingBy { it.dri } + .reduce { _, left, right -> reducer(left, right)} + .values.toList() + +fun PlatformInfo.mergeWith(other: PlatformInfo?) = BasePlatformInfo( + docTag, + links, + (platformData + (other?.platformData ?: emptyList())).distinct() +) + +fun ClassPlatformInfo.mergeWith(other: ClassPlatformInfo?) = ClassPlatformInfo( + info.mergeWith(other?.info), + (inherited + (other?.inherited ?: emptyList())).distinct() +) + +fun List<ClassPlatformInfo>.mergeClassPlatformInfo() : List<ClassPlatformInfo> = + groupingBy { it.docTag.toString() + it.links + it.inherited}.reduce { + _, left, right -> left.mergeWith(right) + }.values.toList() + +fun List<PlatformInfo>.merge() : List<PlatformInfo> = + groupingBy { it.docTag.toString() + it.links }.reduce { + _, left, right -> left.mergeWith(right) + }.values.toList() + +fun Function.mergeWith(other: Function) = Function( + dri, + name, + returnType, + isConstructor, + if (receiver != null && other.receiver != null) receiver.mergeWith(other.receiver) else null, + merge(parameters + other.parameters, Parameter::mergeWith), + expected?.mergeWith(other.expected), + (actual + other.actual).merge() +) + +fun Property.mergeWith(other: Property) = Property( + dri, + name, + if (receiver != null && other.receiver != null) receiver.mergeWith(other.receiver) else null, + expected?.mergeWith(other.expected), + (actual + other.actual).merge() +) + +fun Class.mergeWith(other: Class) = Class( + dri, + name, + kind, + merge(constructors + other.constructors, Function::mergeWith), + merge(functions + other.functions, Function::mergeWith), + merge(properties + other.properties, Property::mergeWith), + merge(classes + other.classes, Class::mergeWith), + expected?.mergeWith(other.expected), + (actual + other.actual).mergeClassPlatformInfo() +) + +fun Parameter.mergeWith(other: Parameter) = Parameter( + dri, + name, + type, + (actual + other.actual).merge() +) + +fun Package.mergeWith(other: Package) = Package( + dri, + merge(functions + other.functions, Function::mergeWith), + merge(properties + other.properties, Property::mergeWith), + merge(classes + other.classes, Class::mergeWith) +)
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationToPageTranslator.kt b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationToPageTranslator.kt new file mode 100644 index 00000000..236d0864 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationToPageTranslator.kt @@ -0,0 +1,24 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.Model.Module +import org.jetbrains.dokka.pages.DefaultPageBuilder +import org.jetbrains.dokka.pages.DefaultPageContentBuilder +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.single + + +object DefaultDocumentationToPageTranslator : DocumentationToPageTranslator { + override fun invoke(module: Module, context: DokkaContext): ModulePageNode = + DefaultPageBuilder { node, kind, operation -> + DefaultPageContentBuilder.group( + node.dri, + node.platformData, + kind, + context.single(CoreExtensions.markdownToContentConverterFactory).invoke(context), + context.logger, + operation + ) + }.pageForModule(module) +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentationNodeMerger.kt b/core/src/main/kotlin/transformers/documentation/DocumentationNodeMerger.kt new file mode 100644 index 00000000..0423f47c --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentationNodeMerger.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.Model.Module +import org.jetbrains.dokka.plugability.DokkaContext + +interface DocumentationNodeMerger { + operator fun invoke(modules: Collection<Module>, context: DokkaContext): Module +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentationNodeTransformer.kt b/core/src/main/kotlin/transformers/documentation/DocumentationNodeTransformer.kt new file mode 100644 index 00000000..8ac4a7c2 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentationNodeTransformer.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.Model.Module +import org.jetbrains.dokka.plugability.DokkaContext + +interface DocumentationNodeTransformer { + operator fun invoke(original: Module, context: DokkaContext): Module +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/documentation/DocumentationToPageTranslator.kt index 19703025..ffe34226 100644 --- a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/documentation/DocumentationToPageTranslator.kt @@ -1,11 +1,12 @@ -package org.jetbrains.dokka.transformers +package org.jetbrains.dokka.transformers.documentation import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.Model.DocumentationNode import org.jetbrains.dokka.Model.Module import org.jetbrains.dokka.pages.ModulePageNode import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.plugability.DokkaContext -interface DocumentationToPageTransformer { - fun transform(module: Module): ModulePageNode // TODO refactor this... some more? +interface DocumentationToPageTranslator { + operator fun invoke(module: Module, context: DokkaContext): ModulePageNode }
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt b/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt new file mode 100644 index 00000000..286835f9 --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageNodeTransformer.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.plugability.DokkaContext + +interface PageNodeTransformer { + operator fun invoke(input: ModulePageNode, dokkaContext: DokkaContext): ModulePageNode +}
\ No newline at end of file |