From 8e5c63d035ef44a269b8c43430f43f5c8eebfb63 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Fri, 10 Nov 2023 11:46:54 +0100 Subject: Restructure the project to utilize included builds (#3174) * Refactor and simplify artifact publishing * Update Gradle to 8.4 * Refactor and simplify convention plugins and build scripts Fixes #3132 --------- Co-authored-by: Adam <897017+aSemy@users.noreply.github.com> Co-authored-by: Oleg Yukhnevich --- .../javadoc/JavadocDocumentableToPageTranslator.kt | 16 + .../jetbrains/dokka/javadoc/JavadocPageCreator.kt | 261 ++++++++++ .../org/jetbrains/dokka/javadoc/JavadocPlugin.kt | 113 +++++ .../javadoc/location/JavadocLocationProvider.kt | 147 ++++++ .../location/JavadocLocationProviderFactory.kt | 17 + .../dokka/javadoc/pages/JavadocContentNodes.kt | 206 ++++++++ .../dokka/javadoc/pages/JavadocIndexExtra.kt | 14 + .../dokka/javadoc/pages/JavadocPageNodes.kt | 529 +++++++++++++++++++++ .../dokka/javadoc/pages/htmlPreprocessors.kt | 200 ++++++++ .../org/jetbrains/dokka/javadoc/pages/utils.kt | 42 ++ .../renderer/JavadocContentToHtmlTranslator.kt | 90 ++++ .../JavadocContentToTemplateMapTranslator.kt | 312 ++++++++++++ .../dokka/javadoc/renderer/KorteJavadocRenderer.kt | 213 +++++++++ .../dokka/javadoc/renderer/SearchScriptsCreator.kt | 280 +++++++++++ .../javadoc/signatures/JavadocSignatureProvider.kt | 225 +++++++++ .../JavadocDocumentableJVMSourceSetFilter.kt | 27 ++ .../documentables/JavadocPageContentBuilder.kt | 83 ++++ .../kotlin/org/jetbrains/dokka/javadoc/utils.kt | 12 + .../validity/MultiplatformConfiguredChecker.kt | 28 ++ 19 files changed, 2815 insertions(+) create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocDocumentableToPageTranslator.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProvider.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProviderFactory.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocContentNodes.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocIndexExtra.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/utils.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/JavadocContentToHtmlTranslator.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/KorteJavadocRenderer.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/SearchScriptsCreator.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/signatures/JavadocSignatureProvider.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/transformers/documentables/JavadocDocumentableJVMSourceSetFilter.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/translators/documentables/JavadocPageContentBuilder.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/utils.kt create mode 100644 dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/validity/MultiplatformConfiguredChecker.kt (limited to 'dokka-subprojects/plugin-javadoc/src/main/kotlin') diff --git a/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocDocumentableToPageTranslator.kt b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocDocumentableToPageTranslator.kt new file mode 100644 index 00000000..595c307a --- /dev/null +++ b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocDocumentableToPageTranslator.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.javadoc + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator + +public class JavadocDocumentableToPageTranslator( + private val context: DokkaContext +) : DocumentableToPageTranslator { + override fun invoke(module: DModule): RootPageNode = JavadocPageCreator(context).pageForModule(module) +} diff --git a/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt new file mode 100644 index 00000000..cfdda649 --- /dev/null +++ b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPageCreator.kt @@ -0,0 +1,261 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.javadoc + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.base.translators.documentables.firstSentenceBriefFromContentNodes +import org.jetbrains.dokka.javadoc.pages.* +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import kotlin.reflect.KClass + +public open class JavadocPageCreator(context: DokkaContext) { + private val signatureProvider: SignatureProvider = context.plugin().querySingle { signatureProvider } + private val documentationVersion = context.configuration.moduleVersion + + public fun pageForModule(m: DModule): JavadocModulePageNode { + return JavadocModulePageNode( + name = m.name.ifEmpty { "root" }, + content = contentForModule(m), + children = m.packages.map { pageForPackage(it) }, + dri = setOf(m.dri), + extra = ((m as? WithExtraProperties)?.extra ?: PropertyContainer.empty()) + ) + } + + public fun pageForPackage(p: DPackage): JavadocPackagePageNode { + return JavadocPackagePageNode(p.name, contentForPackage(p), setOf(p.dri), listOf(p), + p.classlikes.mapNotNull { pageForClasslike(it) } + ) + } + + public fun pageForClasslike(c: DClasslike): JavadocClasslikePageNode? { + return c.highestJvmSourceSet?.let { jvm -> + @Suppress("UNCHECKED_CAST") + val extra = ((c as? WithExtraProperties)?.extra ?: PropertyContainer.empty()) + val children = c.classlikes.mapNotNull { pageForClasslike(it) } + + JavadocClasslikePageNode( + name = c.dri.classNames.orEmpty(), + content = contentForClasslike(c), + dri = setOf(c.dri), + brief = c.brief(), + signature = signatureForNode(c, jvm), + description = c.descriptionToContentNodes(), + constructors = (c as? WithConstructors)?.constructors?.mapNotNull { it.toJavadocFunction() }.orEmpty(), + methods = c.functions.mapNotNull { it.toJavadocFunction() }, + entries = (c as? DEnum)?.entries?.map { + JavadocEntryNode( + it.dri, + it.name, + signatureForNode(it, jvm), + it.descriptionToContentNodes(jvm), + PropertyContainer.withAll(it.indexesInDocumentation()) + ) + }.orEmpty(), + classlikes = children, + properties = c.properties.map { + JavadocPropertyNode( + it.dri, + it.name, + signatureForNode(it, jvm), + it.descriptionToContentNodes(jvm), + PropertyContainer.withAll(it.indexesInDocumentation()), + ) + }, + sinceTagContent = c.sinceToContentNodes(jvm), + authorTagContent = c.authorsToContentNodes(jvm), + documentables = listOf(c), + children = children, + extra = extra + c.indexesInDocumentation() + ) + } + } + + private fun contentForModule(m: DModule): JavadocContentNode = + JavadocContentGroup( + setOf(m.dri), + JavadocContentKind.OverviewSummary, + m.sourceSets.toDisplaySourceSets() + ) { + title(m.name, m.descriptionToContentNodes(), documentationVersion, dri = setOf(m.dri), kind = ContentKind.Main) + leafList(setOf(m.dri), + ContentKind.Packages, JavadocList( + "Packages", "Package", + m.packages.sortedBy { it.packageName }.map { p -> + RowJavadocListEntry( + LinkJavadocListEntry(p.name, setOf(p.dri), JavadocContentKind.PackageSummary, sourceSets), + p.brief() + ) + } + )) + } + + private fun contentForPackage(p: DPackage): JavadocContentNode = + JavadocContentGroup( + setOf(p.dri), + JavadocContentKind.PackageSummary, + p.sourceSets.toDisplaySourceSets() + ) { + title("Package ${p.name}", p.descriptionToContentNodes(), dri = setOf(p.dri), kind = ContentKind.Packages) + fun allClasslikes(c: DClasslike): List = c.classlikes.flatMap { allClasslikes(it) } + c + val rootList = p.classlikes.map { allClasslikes(it) }.flatten().groupBy { it::class }.map { (key, value) -> + JavadocList(key.tabTitle, key.colTitle, value.map { c -> + RowJavadocListEntry( + LinkJavadocListEntry(c.name ?: "", setOf(c.dri), JavadocContentKind.Class, sourceSets), + c.brief() + ) + }) + } + rootList(setOf(p.dri), JavadocContentKind.Class, rootList) + } + + private val KClass.colTitle: String + get() = when (this) { + DClass::class -> "Class" + DObject::class -> "Object" + DAnnotation::class -> "Annotation" + DEnum::class -> "Enum" + DInterface::class -> "Interface" + else -> "" + } + + private val KClass.tabTitle: String + get() = "$colTitle Summary" + + private fun contentForClasslike(c: DClasslike): JavadocContentNode = + JavadocContentGroup( + setOf(c.dri), + JavadocContentKind.Class, + c.sourceSets.toDisplaySourceSets() + ) { + title( + c.name.orEmpty(), + c.brief(), + documentationVersion, + parent = c.dri.packageName, + dri = setOf(c.dri), + kind = JavadocContentKind.Class + ) + } + + private fun DFunction.toJavadocFunction() = highestJvmSourceSet?.let { jvm -> + JavadocFunctionNode( + name = name, + dri = dri, + signature = signatureForNode(this, jvm), + brief = brief(jvm), + description = descriptionToContentNodes(jvm), + parameters = parameters.mapNotNull { + val signature = signatureForNode(it, jvm) + signature.modifiers?.let { type -> + JavadocParameterNode( + name = it.name.orEmpty(), + type = type, + description = it.brief(), + typeBound = it.type, + dri = it.dri, + extra = PropertyContainer.withAll(it.indexesInDocumentation()) + ) + } + }, + returnTagContent = returnToContentNodes(jvm), + sinceTagContent = sinceToContentNodes(jvm), + extra = extra + indexesInDocumentation() + ) + } + + private val Documentable.highestJvmSourceSet + get() = sourceSets.let { sources -> + sources.firstOrNull { it != expectPresentInSet } ?: sources.firstOrNull() + } + + private inline fun Documentable.findNodeInDocumentation(sourceSetData: DokkaSourceSet?): T? = + documentation[sourceSetData]?.firstChildOfTypeOrNull() + + private inline fun Documentable.findAllNodesInDocumentation(sourceSetData: DokkaSourceSet?): List = + documentation[sourceSetData]?.childrenOfType() ?: emptyList() + + private fun Documentable.descriptionToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType(sourceSet) + + private fun DParameter.paramsToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType(sourceSet) + + private inline fun Documentable.contentNodesFromType(sourceSet: DokkaSourceSet?) = + findNodeInDocumentation(sourceSet)?.let { + DocTagToContentConverter().buildContent( + it.root, + DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets.toSet() + ) + }.orEmpty() + + private inline fun Documentable.allContentNodesFromType(sourceSet: DokkaSourceSet?) = + findAllNodesInDocumentation(sourceSet).map { + DocTagToContentConverter().buildContent( + it.root, + DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets.toSet() + ) + } + + public fun List.nodeForJvm(jvm: DokkaSourceSet): ContentNode { + return firstOrNull { jvm.sourceSetID in it.sourceSets.computeSourceSetIds() } + ?: throw IllegalStateException("No source set found for ${jvm.sourceSetID} ") + } + + private fun Documentable.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List = + firstSentenceBriefFromContentNodes(descriptionToContentNodes(sourceSet)) + + private fun DParameter.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List = + firstSentenceBriefFromContentNodes(paramsToContentNodes(sourceSet).dropWhile { it is ContentDRILink }) + + private fun ContentNode.asJavadocNode(): JavadocSignatureContentNode = + (this as ContentGroup).firstChildOfTypeOrNull() + ?: throw IllegalStateException("No content for javadoc signature found") + + private fun signatureForNode(documentable: Documentable, sourceSet: DokkaSourceSet): JavadocSignatureContentNode = + signatureProvider.signature(documentable).nodeForJvm(sourceSet).asJavadocNode() + + private fun Documentable.indexesInDocumentation(): JavadocIndexExtra { + val indexes = + documentation[highestJvmSourceSet]?.withDescendants()?.filterIsInstance()?.toList().orEmpty() + return JavadocIndexExtra( + indexes.map { + ContentGroup( + children = DocTagToContentConverter().buildContent( + it, + DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets.toSet() + ), + dci = DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets = sourceSets.toDisplaySourceSets(), + style = emptySet(), + extra = PropertyContainer.empty() + ) + } + ) + } + + private fun Documentable.authorsToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + allContentNodesFromType(sourceSet) + + private fun Documentable.sinceToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + allContentNodesFromType(sourceSet) + + private fun Documentable.returnToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType(sourceSet) +} + diff --git a/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt new file mode 100644 index 00000000..6a5749ab --- /dev/null +++ b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.javadoc + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.PackageListCreator +import org.jetbrains.dokka.base.renderers.RootCreator +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.PACKAGE_LIST_NAME +import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.javadoc.location.JavadocLocationProviderFactory +import org.jetbrains.dokka.javadoc.pages.* +import org.jetbrains.dokka.javadoc.renderer.KorteJavadocRenderer +import org.jetbrains.dokka.javadoc.signatures.JavadocSignatureProvider +import org.jetbrains.dokka.javadoc.transformers.documentables.JavadocDocumentableJVMSourceSetFilter +import org.jetbrains.dokka.javadoc.validity.MultiplatformConfiguredChecker +import org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin +import org.jetbrains.dokka.plugability.* +import org.jetbrains.dokka.renderers.PostAction +import org.jetbrains.dokka.renderers.Renderer +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.dokka.validity.PreGenerationChecker + +public class JavadocPlugin : DokkaPlugin() { + + private val dokkaBasePlugin: DokkaBase by lazy { plugin() } + private val kotinAsJavaPlugin: KotlinAsJavaPlugin by lazy { plugin() } + + public val locationProviderFactory: ExtensionPoint by lazy { dokkaBasePlugin.locationProviderFactory } + public val javadocPreprocessors: ExtensionPoint by extensionPoint() + + public val dokkaJavadocPlugin: Extension by extending { + CoreExtensions.renderer providing { ctx -> KorteJavadocRenderer(ctx, "views") } override dokkaBasePlugin.htmlRenderer + } + + public val javadocMultiplatformCheck: Extension by extending { + CoreExtensions.preGenerationCheck providing ::MultiplatformConfiguredChecker + } + + public val pageTranslator: Extension by extending { + CoreExtensions.documentableToPageTranslator providing ::JavadocDocumentableToPageTranslator override + kotinAsJavaPlugin.kotlinAsJavaDocumentableToPageTranslator + } + + public val documentableSourceSetFilter: Extension by extending { + dokkaBasePlugin.preMergeDocumentableTransformer providing ::JavadocDocumentableJVMSourceSetFilter + } + + public val javadocLocationProviderFactory: Extension by extending { + dokkaBasePlugin.locationProviderFactory providing ::JavadocLocationProviderFactory override dokkaBasePlugin.locationProvider + } + + public val javadocSignatureProvider: Extension by extending { + dokkaBasePlugin.signatureProvider providing ::JavadocSignatureProvider override kotinAsJavaPlugin.javaSignatureProvider + } + + public val rootCreator: Extension by extending { + javadocPreprocessors with RootCreator + } + + public val packageListCreator: Extension by extending { + javadocPreprocessors providing { + PackageListCreator( + context = it, + format = RecognizedLinkFormat.DokkaJavadoc, + outputFilesNames = listOf(PACKAGE_LIST_NAME, "element-list") + ) + } order { after(rootCreator) } + } + + public val resourcesInstaller: Extension by extending { + javadocPreprocessors with ResourcesInstaller order { after(rootCreator) } + } + + public val treeViewInstaller: Extension by extending { + javadocPreprocessors providing ::TreeViewInstaller order { after(rootCreator) } + } + + public val allClassessPageInstaller: Extension by extending { + javadocPreprocessors with AllClassesPageInstaller order { before(rootCreator) } + } + + public val indexGenerator: Extension by extending { + javadocPreprocessors with IndexGenerator order { before(rootCreator) } + } + + public val deprecatedPageCreator: Extension by extending { + javadocPreprocessors with DeprecatedPageCreator order { before(rootCreator) } + } + + internal val alphaVersionNotifier by extending { + CoreExtensions.postActions providing { ctx -> + PostAction { + ctx.logger.info( + "The Javadoc output format is still in Alpha so you may find bugs and experience migration " + + "issues when using it. Successful integration with tools that accept Java's Javadoc " + + "HTML as input is not guaranteed. You use it at your own risk." + ) + } + } + } + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = + PluginApiPreviewAcknowledgement +} + diff --git a/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProvider.kt b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProvider.kt new file mode 100644 index 00000000..6de4c808 --- /dev/null +++ b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProvider.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.javadoc.location + +import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProvider +import org.jetbrains.dokka.javadoc.pages.* +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.Nullable +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.dokka.model.DisplaySourceSet +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import java.util.* + +public class JavadocLocationProvider( + pageRoot: RootPageNode, + dokkaContext: DokkaContext +) : DefaultLocationProvider(pageRoot, dokkaContext) { + + private val pathIndex = IdentityHashMap>().apply { + fun registerPath(page: PageNode, prefix: List = emptyList()) { + val packagePath = page.takeIf { it is JavadocPackagePageNode }?.name.orEmpty() + .replace(".", "/") + val newPathPrefix = prefix + packagePath + + val path = (prefix + when (page) { + is AllClassesPage -> listOf("allclasses") + is TreeViewPage -> if (page.classes == null) + listOf("overview-tree") + else + listOf("package-tree") + is ContentPage -> if (page.dri.isNotEmpty() && page.dri.first().classNames != null) + listOfNotNull(page.dri.first().classNames) + else if (page is JavadocPackagePageNode) + listOf(packagePath, "package-summary") + else if (page is IndexPage) + listOf("index-files", page.name) + else if (page is DeprecatedPage) + listOf("deprecated") + else + listOf("index") + else -> emptyList() + }).filterNot { it.isEmpty() } + + put(page, path) + page.children.forEach { registerPath(it, newPathPrefix) } + + } + put(pageRoot, listOf("index")) + pageRoot.children.forEach { registerPath(it) } + } + + private val parentPageIndex = HashMap() + private val nodeIndex = HashMap().apply { + fun registerNode(node: PageNode) { + if (node is ContentPage) put(node.dri.first(), node) + (node as? JavadocClasslikePageNode)?.getAnchorables()?.forEach { navigableNode -> + parentPageIndex[navigableNode.getDRI()] = node + } + node.children.forEach(::registerNode) + } + registerNode(pageRoot) + } + + private operator fun IdentityHashMap>.get(dri: DRI) = this[nodeIndex[dri]] + + private fun List.relativeTo(context: List): String { + val contextPath = context.dropLast(1).flatMap { it.split("/") } + val commonPathElements = flatMap { it.split("/") }.zip(contextPath).takeWhile { (a, b) -> a == b }.count() + return (List(contextPath.size - commonPathElements) { ".." } + this.flatMap { it.split("/") }.drop(commonPathElements)).joinToString("/") + } + + private fun JavadocClasslikePageNode.findAnchorableByDRI(dri: DRI): AnchorableJavadocNode? = + (constructors + methods + entries + properties).firstOrNull { it.dri == dri } + + override fun resolve(dri: DRI, sourceSets: Set, context: PageNode?): String? = + getLocalLocation(dri, context) + ?: getLocalLocation(dri.copy(target = PointingToDeclaration), context) + // Not found in PageGraph, that means it's an external link + ?: getExternalLocation(dri, sourceSets) + ?: getExternalLocation(dri.copy(target = PointingToDeclaration), sourceSets) + + private fun getLocalLocation(dri: DRI, context: PageNode?): String? = + nodeIndex[dri]?.let { resolve(it, context) } + ?: parentPageIndex[dri]?.let { + val anchor = when (val anchorElement = (it as? JavadocClasslikePageNode)?.findAnchorableByDRI(dri)) { + is JavadocFunctionNode -> anchorElement.getAnchor() + is JavadocEntryNode -> anchorElement.name + is JavadocPropertyNode -> anchorElement.name + else -> anchorForDri(dri) + } + "${resolve(it, context, skipExtension = true)}.html#$anchor" + } + + private fun anchorForDri(dri: DRI): String = + dri.callable?.let { callable -> + "${callable.name}(${ + callable.params.joinToString(",") { + ((it as? Nullable)?.wrapped ?: it).toString() + } + })" + } ?: dri.classNames.orEmpty() + + override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String { + return pathIndex[node]?.relativeTo(pathIndex[context].orEmpty())?.let { + if (skipExtension) it.removeSuffix(".html") else it + } ?: run { + throw IllegalStateException("Path for ${node::class.java.canonicalName}:${node.name} not found") + } + } + + public fun resolve(link: LinkJavadocListEntry, contextRoot: PageNode? = null, skipExtension: Boolean = true): String { + return pathIndex[link.dri.first()]?.let { + when (link.kind) { + JavadocContentKind.Class -> it + JavadocContentKind.OverviewSummary -> it.dropLast(1) + "index" + JavadocContentKind.PackageSummary -> it.dropLast(1) + "package-summary" + JavadocContentKind.AllClasses -> it.dropLast(1) + "allclasses" + JavadocContentKind.OverviewTree -> it.dropLast(1) + "overview-tree" + JavadocContentKind.PackageTree -> it.dropLast(1) + "package-tree" + JavadocContentKind.IndexPage -> it.dropLast(1) + "index-1" + else -> it + } + }?.relativeTo(pathIndex[contextRoot].orEmpty())?.let { if (skipExtension) "$it.html" else it }.orEmpty() + } + + override fun pathToRoot(from: PageNode): String { + TODO("Not yet implemented") + } + + override fun ancestors(node: PageNode): List { + TODO("Not yet implemented") + } + + override fun expectedLocationForDri(dri: DRI): String { + if (dri.packageName?.isNotEmpty() == true && dri.classNames == null) + return (dri.packageName?.split(".").orEmpty() + "package-summary").joinToString("/") + + return (dri.packageName?.split(".").orEmpty() + + dri.classNames?.split(".").orEmpty() // Top-level methods will always be relocated which is fine + ).joinToString("/") + } +} diff --git a/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProviderFactory.kt b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProviderFactory.kt new file mode 100644 index 00000000..10e7d416 --- /dev/null +++ b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationProviderFactory.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.javadoc.location + +import org.jetbrains.dokka.base.resolvers.local.LocationProvider +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +public class JavadocLocationProviderFactory( + private val context: DokkaContext +) : LocationProviderFactory { + override fun getLocationProvider(pageNode: RootPageNode): LocationProvider = + JavadocLocationProvider(pageNode, context) +} diff --git a/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocContentNodes.kt b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocContentNodes.kt new file mode 100644 index 00000000..427ad98e --- /dev/null +++ b/dokka-subprojects/plugin-javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocContentNodes.kt @@ -0,0 +1,206 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.javadoc.pages + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.DisplaySourceSet +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* + +public enum class JavadocContentKind : Kind { + AllClasses, OverviewSummary, PackageSummary, Class, OverviewTree, PackageTree, IndexPage +} + +public abstract class JavadocContentNode( + dri: Set, + kind: Kind, + override val sourceSets: Set +) : ContentNode { + override val dci: DCI = DCI(dri, kind) + override val style: Set