diff options
Diffstat (limited to 'core/src/main/kotlin/Generation')
-rw-r--r-- | core/src/main/kotlin/Generation/DokkaGenerator.kt | 232 |
1 files changed, 224 insertions, 8 deletions
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt index 9de4396b..0c4b3b7e 100644 --- a/core/src/main/kotlin/Generation/DokkaGenerator.kt +++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt @@ -2,6 +2,7 @@ package org.jetbrains.dokka import com.google.inject.Guice import com.google.inject.Injector +import com.intellij.openapi.application.PathManager import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiFile @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.config.KotlinSourceRoot import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer import org.jetbrains.kotlin.resolve.TopDownAnalysisMode @@ -32,28 +34,34 @@ class DokkaGenerator(val logger: DokkaLogger, val moduleName: String, val options: DocumentationOptions) { - private val documentationModule = DocumentationModule(moduleName) + private val documentationModules: MutableList<DocumentationModule> = mutableListOf() fun generate() { val sourcesGroupedByPlatform = sources.groupBy { it.platforms.firstOrNull() to it.analysisPlatform } for ((platformsInfo, roots) in sourcesGroupedByPlatform) { val (platform, analysisPlatform) = platformsInfo - appendSourceModule(platform, analysisPlatform, roots) + + val documentationModule = DocumentationModule(moduleName) + appendSourceModule(platform, analysisPlatform, roots, documentationModule) + documentationModules.add(documentationModule) } - documentationModule.prepareForGeneration(options) + + val totalDocumentationModule = DocumentationMerger(documentationModules).merge() + totalDocumentationModule.prepareForGeneration(options) val timeBuild = measureTimeMillis { logger.info("Generating pages... ") val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger)) - outputInjector.getInstance(Generator::class.java).buildAll(documentationModule) + outputInjector.getInstance(Generator::class.java).buildAll(totalDocumentationModule) } logger.info("done in ${timeBuild / 1000} secs") } - private fun appendSourceModule(defaultPlatform: String?, - analysisPlatform: Platform, - sourceRoots: List<SourceRoot> - + private fun appendSourceModule( + defaultPlatform: String?, + analysisPlatform: Platform, + sourceRoots: List<SourceRoot>, + documentationModule: DocumentationModule ) { val sourcePaths = sourceRoots.map { it.path } @@ -81,6 +89,12 @@ class DokkaGenerator(val logger: DokkaLogger, DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger)) buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes) + documentationModule.nodeRefGraph.nodeMapView.forEach { (_, node) -> + node.addReferenceTo( + DocumentationNode(analysisPlatform.key, Content.Empty, NodeKind.Platform), + RefKind.Platform + ) + } val timeAnalyse = System.currentTimeMillis() - startAnalyse logger.info("done in ${timeAnalyse / 1000} secs") @@ -214,3 +228,205 @@ fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> { } return result } + +class DocumentationMerger( + private val documentationModules: List<DocumentationModule> +) { + private val producedNodeRefGraph: NodeReferenceGraph + private val signatureMap: Map<DocumentationNode, String> + + init { + signatureMap = documentationModules + .flatMap { it.nodeRefGraph.nodeMapView.entries } + .map { (k, v) -> v to k } + .toMap() + + producedNodeRefGraph = NodeReferenceGraph() + documentationModules.map { it.nodeRefGraph } + .flatMap { it.references } + .distinct() + .forEach { producedNodeRefGraph.addReference(it) } + } + + private fun splitReferencesByKind( + source: List<DocumentationReference>, + kind: RefKind + ): Pair<List<DocumentationReference>, List<DocumentationReference>> = + Pair(source.filter { it.kind == kind }, source.filterNot { it.kind == kind }) + + + private fun mergePackageReferences( + from: DocumentationNode, + packages: List<DocumentationReference> + ): List<DocumentationReference> { + val packagesByName = packages + .map { it.to } + .groupBy { it.name } + + val mutableList: MutableList<DocumentationReference> = mutableListOf() + for ((name, listOfPackages) in packagesByName) { + val producedPackage = mergePackagesWithEqualNames(from, listOfPackages) + updatePendingReferences(name, producedPackage) + + mutableList.add( + DocumentationReference(from, producedPackage, RefKind.Member) + ) + } + + return mutableList + } + + private fun mergePackagesWithEqualNames( + from: DocumentationNode, + packages: List<DocumentationNode> + ): DocumentationNode { + val references = packages.flatMap { it.allReferences() } + + val mergedPackage = + DocumentationNode( + packages.first().name, + Content.Empty, + NodeKind.Package + ) + + val mergedReferences = mergeReferences(mergedPackage, references) + + for (packageNode in packages) { + mergedPackage.updateContent { + for (otherChild in packageNode.content.children) { + children.add(otherChild) + } + } + } + + mergedPackage.dropReferences { true } // clear() + for (ref in mergedReferences.distinct()) { + mergedPackage.addReference(ref) + } + + from.append(mergedPackage, RefKind.Member) + + return mergedPackage + } + + + private fun mergeMembers( + from: DocumentationNode, + refs: List<DocumentationReference> + ): List<DocumentationReference> { + val membersBySignature: Map<String, List<DocumentationNode>> = refs.map { it.to } + .filter { signatureMap.containsKey(it) } + .groupBy { signatureMap[it]!! } + + val mergedMembers: MutableList<DocumentationReference> = mutableListOf() + for ((signature, members) in membersBySignature) { + val newNode = mergeMembersWithEqualSignature(signature, from, members) + + producedNodeRefGraph.register(signature, newNode) + updatePendingReferences(signature, newNode) + from.append(newNode, RefKind.Member) + + mergedMembers.add(DocumentationReference(from, newNode, RefKind.Member)) + } + + return mergedMembers + } + + private fun mergeMembersWithEqualSignature( + signature: String, + from: DocumentationNode, + refs: List<DocumentationNode> + ): DocumentationNode { + if (refs.size == 1) { + val singleNode = refs.single() + singleNode.owner?.let { owner -> + singleNode.dropReferences { it.to == owner && it.kind == RefKind.Owner } + } + return singleNode + } + val groupNode = DocumentationNode(refs.first().name, Content.Empty, NodeKind.GroupNode) + groupNode.appendTextNode(signature, NodeKind.Signature, RefKind.Detail) + + for (node in refs) { + if (node != groupNode) { + node.owner?.let { owner -> + node.dropReferences { it.to == owner && it.kind == RefKind.Owner } + from.dropReferences { it.to == node && it.kind == RefKind.Member } + } + groupNode.append(node, RefKind.Member) + } + } + return groupNode + } + + + private fun mergeReferences( + from: DocumentationNode, + refs: List<DocumentationReference> + ): List<DocumentationReference> { + val allRefsToPackages = refs.map { it.to } + .all { it.kind == NodeKind.Package } + + if (allRefsToPackages) { + return mergePackageReferences(from, refs) + } + + val (memberRefs, notMemberRefs) = splitReferencesByKind(refs, RefKind.Member) + val mergedMembers = mergeMembers(from, memberRefs) + + return (mergedMembers + notMemberRefs).distinctBy { + it.kind to it.to.name + } + } + + + private fun updatePendingReferences( + signature: String, + nodeToUpdate: DocumentationNode + ) { + producedNodeRefGraph.references.forEach { + it.lazyNodeFrom.update(signature, nodeToUpdate) + it.lazyNodeTo.update(signature, nodeToUpdate) + } + } + + private fun NodeResolver.update(signature: String, nodeToUpdate: DocumentationNode) { + when (this) { + is NodeResolver.BySignature -> update(signature, nodeToUpdate) + is NodeResolver.Exact -> update(signature, nodeToUpdate) + } + } + + private fun NodeResolver.BySignature.update(signature: String, nodeToUpdate: DocumentationNode) { + if (signature == nodeToUpdate.name) { + nodeMap = producedNodeRefGraph.nodeMapView + } + } + + private fun NodeResolver.Exact.update(signature: String, nodeToUpdate: DocumentationNode) { + exactNode?.let { it -> + val equalSignature = + it.anyReference { ref -> ref.to.kind == NodeKind.Signature && ref.to.name == signature } + + if (equalSignature) { + exactNode = nodeToUpdate + } + } + } + + fun merge(): DocumentationModule { + val refs = documentationModules.flatMap { + it.allReferences() + } + val mergedDocumentationModule = DocumentationModule( + name = documentationModules.first().name, + nodeRefGraph = producedNodeRefGraph + ) + + mergeReferences(mergedDocumentationModule, refs) + + return mergedDocumentationModule + } + + +}
\ No newline at end of file |