aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/transformers
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2019-11-26 13:44:27 +0100
committerPaweł Marks <pmarks@virtuslab.com>2019-11-26 21:02:20 +0100
commit49439594f86217d8a25e8df2580b8ef29d836230 (patch)
tree50dc1b0793c4804b382c6b75fd7a3a0d27cb869a /core/src/main/kotlin/transformers
parentc185e420971950747535e98f96a12e480159dd83 (diff)
downloaddokka-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.kt20
-rw-r--r--core/src/main/kotlin/transformers/PageNodeTransformer.kt8
-rw-r--r--core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt208
-rw-r--r--core/src/main/kotlin/transformers/descriptors/DescriptorToDocumentationTranslator.kt15
-rw-r--r--core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt86
-rw-r--r--core/src/main/kotlin/transformers/documentation/DefaultDocumentationToPageTranslator.kt24
-rw-r--r--core/src/main/kotlin/transformers/documentation/DocumentationNodeMerger.kt8
-rw-r--r--core/src/main/kotlin/transformers/documentation/DocumentationNodeTransformer.kt8
-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.kt8
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