diff options
author | Kamil Doległo <9080183+kamildoleglo@users.noreply.github.com> | 2021-07-05 14:10:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-05 14:10:23 +0200 |
commit | 0bf1d0f5491a62c56393a06cdfb4168778d9829e (patch) | |
tree | 808f631e72b652dc2c3d5929f85f677968bc56f6 /plugins | |
parent | a1d44ab80df217196fe5ee9455c7cf1c135e3b07 (diff) | |
download | dokka-0bf1d0f5491a62c56393a06cdfb4168778d9829e.tar.gz dokka-0bf1d0f5491a62c56393a06cdfb4168778d9829e.tar.bz2 dokka-0bf1d0f5491a62c56393a06cdfb4168778d9829e.zip |
Flatten multi-module structure (#1980)
* Add support for multimodule package lists
* Merge package-lists in multi-module generation
* Remove double-wrapping of modules in multi-module generation
* Handle empty modules in package lists
Diffstat (limited to 'plugins')
37 files changed, 413 insertions, 142 deletions
diff --git a/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt b/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt index e6556b07..da44c840 100644 --- a/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt +++ b/plugins/all-modules-page/src/main/kotlin/AllModulesPagePlugin.kt @@ -45,4 +45,4 @@ class AllModulesPagePlugin : DokkaPlugin() { val multiModuleLinkResolver by extending { externalModuleLinkResolver providing ::DefaultExternalModuleLinkResolver } -}
\ No newline at end of file +} diff --git a/plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt b/plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt index 0fde629c..e7925ead 100644 --- a/plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt +++ b/plugins/all-modules-page/src/main/kotlin/ExternalModuleLinkResolver.kt @@ -1,10 +1,10 @@ package org.jetbrains.dokka.allModulesPage -import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider.Companion.identifierToFilename import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.PACKAGE_LIST_NAME import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.plugability.DokkaContext @@ -42,10 +42,10 @@ class DefaultExternalModuleLinkResolver(val context: DokkaContext) : ExternalMod this } - private fun loadPackageListForModule(module: DokkaConfiguration.DokkaModuleDescription) = - module.sourceOutputDirectory.resolve(File(identifierToFilename(module.name))).let { + private fun loadPackageListForModule(module: DokkaModuleDescription) = + module.sourceOutputDirectory.walkTopDown().maxDepth(3).firstOrNull { it.name == PACKAGE_LIST_NAME }?.let { PackageList.load( - URL("file:" + it.resolve("package-list").path), + URL("file:" + it.path), 8, true ) diff --git a/plugins/all-modules-page/src/test/kotlin/templates/MultiModuleDocumentationTest.kt b/plugins/all-modules-page/src/test/kotlin/templates/MultiModuleDocumentationTest.kt index 11a03bc4..57f7027f 100644 --- a/plugins/all-modules-page/src/test/kotlin/templates/MultiModuleDocumentationTest.kt +++ b/plugins/all-modules-page/src/test/kotlin/templates/MultiModuleDocumentationTest.kt @@ -62,4 +62,4 @@ class MultiModuleDocumentationTest : MultiModuleAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt index 2637714f..b7487c16 100644 --- a/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt +++ b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkCommandResolutionTest.kt @@ -50,7 +50,7 @@ class ResolveLinkCommandResolutionTest : MultiModuleAbstractTest() { } val expected = createHTML().a { - href = "../../module2/package2/-sample/index.html" + href = "../module2/package2/-sample/index.html" span { +"Sample" } @@ -97,12 +97,12 @@ class ResolveLinkCommandResolutionTest : MultiModuleAbstractTest() { fun setup(content: String): File { folder.create() - val innerModule1 = folder.newFolder("module1", "module1") - val innerModule2 = folder.newFolder("module2", "module2") + val innerModule1 = folder.newFolder("module1") + val innerModule2 = folder.newFolder("module2") val packageList = innerModule2.resolve("package-list") packageList.writeText(mockedPackageListForPackages(RecognizedLinkFormat.DokkaHtml, "package2")) val contentFile = innerModule1.resolve("index.html") contentFile.writeText(content) return contentFile } -}
\ No newline at end of file +} diff --git a/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt index 185a179d..975d3183 100644 --- a/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt +++ b/plugins/all-modules-page/src/test/kotlin/templates/ResolveLinkGfmCommandResolutionTest.kt @@ -33,7 +33,7 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() { sourceOutputDirectory = folder.root.resolve("module2"), ) ) - this.outputDir = folder.root + outputDir = folder.root } @Test @@ -49,7 +49,7 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() { } }.toString() - val expected = "[Sample text inside](../../module2/package2/-sample/index.md)" + val expected = "[Sample text inside](../module2/package2/-sample/index.md)" val content = setup(link) val configuration = configuration() @@ -63,8 +63,8 @@ class ResolveLinkGfmCommandResolutionTest : MultiModuleAbstractTest() { private fun setup(content: String): File { folder.create() - val innerModule1 = folder.newFolder("module1", "module1") - val innerModule2 = folder.newFolder("module2", "module2") + val innerModule1 = folder.newFolder( "module1") + val innerModule2 = folder.newFolder( "module2") val packageList = innerModule2.resolve("package-list") packageList.writeText(mockedPackageListForPackages(RecognizedLinkFormat.DokkaGFM, "package2")) val contentFile = innerModule1.resolve("index.md") diff --git a/plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt b/plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt index 7a10041b..3386ae2c 100644 --- a/plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt +++ b/plugins/all-modules-page/src/test/kotlin/templates/mockedPackageListFactory.kt @@ -1,12 +1,12 @@ package org.jetbrains.dokka.allModulesPage.templates -import org.jetbrains.dokka.base.renderers.PackageListService +import org.jetbrains.dokka.base.resolvers.shared.PackageList import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat internal fun mockedPackageListForPackages(format: RecognizedLinkFormat, vararg packages: String): String = """ - ${PackageListService.DOKKA_PARAM_PREFIX}.format:${format.formatName} - ${PackageListService.DOKKA_PARAM_PREFIX}.linkExtension:${format.linkExtension} + ${PackageList.DOKKA_PARAM_PREFIX}.format:${format.formatName} + ${PackageList.DOKKA_PARAM_PREFIX}.linkExtension:${format.linkExtension} ${packages.sorted().joinToString(separator = "\n", postfix = "\n") { it }} - """.trimIndent()
\ No newline at end of file + """.trimIndent() diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index bfb83e6e..6bf4de15 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -277,25 +277,24 @@ public abstract interface class org/jetbrains/dokka/base/renderers/OutputWriter } public final class org/jetbrains/dokka/base/renderers/PackageListCreator : org/jetbrains/dokka/transformers/pages/PageTransformer { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;Z)V - public synthetic fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;)V + public synthetic fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; public final fun getFormat ()Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat; public final fun getOutputFilesNames ()Ljava/util/List; - public final fun getRemoveModulePrefix ()Z public fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode; } public final class org/jetbrains/dokka/base/renderers/PackageListService { public static final field Companion Lorg/jetbrains/dokka/base/renderers/PackageListService$Companion; - public static final field DOKKA_PARAM_PREFIX Ljava/lang/String; public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/pages/RootPageNode;)V - public final fun createPackageList (Lorg/jetbrains/dokka/pages/ModulePage;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + public final fun createPackageList (Lorg/jetbrains/dokka/pages/ModulePage;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;)Ljava/lang/String; public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; public final fun getRootPage ()Lorg/jetbrains/dokka/pages/RootPageNode; } public final class org/jetbrains/dokka/base/renderers/PackageListService$Companion { + public final fun renderPackageList (Ljava/util/Map;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; } public final class org/jetbrains/dokka/base/renderers/RootCreator : org/jetbrains/dokka/transformers/pages/PageTransformer { @@ -734,19 +733,25 @@ public abstract interface class org/jetbrains/dokka/base/resolvers/shared/LinkFo public final class org/jetbrains/dokka/base/resolvers/shared/PackageList { public static final field Companion Lorg/jetbrains/dokka/base/resolvers/shared/PackageList$Companion; - public fun <init> (Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat;Ljava/util/Set;Ljava/util/Map;Ljava/net/URL;)V + public static final field DOKKA_PARAM_PREFIX Ljava/lang/String; + public static final field MODULE_DELIMITER Ljava/lang/String; + public static final field PACKAGE_LIST_NAME Ljava/lang/String; + public static final field SINGLE_MODULE_NAME Ljava/lang/String; + public fun <init> (Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat;Ljava/util/Map;Ljava/util/Map;Ljava/net/URL;)V public final fun component1 ()Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat; - public final fun component2 ()Ljava/util/Set; + public final fun component2 ()Ljava/util/Map; public final fun component3 ()Ljava/util/Map; public final fun component4 ()Ljava/net/URL; - public final fun copy (Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat;Ljava/util/Set;Ljava/util/Map;Ljava/net/URL;)Lorg/jetbrains/dokka/base/resolvers/shared/PackageList; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/resolvers/shared/PackageList;Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat;Ljava/util/Set;Ljava/util/Map;Ljava/net/URL;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/resolvers/shared/PackageList; + public final fun copy (Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat;Ljava/util/Map;Ljava/util/Map;Ljava/net/URL;)Lorg/jetbrains/dokka/base/resolvers/shared/PackageList; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/resolvers/shared/PackageList;Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat;Ljava/util/Map;Ljava/util/Map;Ljava/net/URL;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/resolvers/shared/PackageList; public fun equals (Ljava/lang/Object;)Z public final fun getLinkFormat ()Lorg/jetbrains/dokka/base/resolvers/shared/RecognizedLinkFormat; public final fun getLocations ()Ljava/util/Map; + public final fun getModules ()Ljava/util/Map; public final fun getPackages ()Ljava/util/Set; public final fun getUrl ()Ljava/net/URL; public fun hashCode ()I + public final fun moduleFor (Ljava/lang/String;)Ljava/lang/String; public fun toString ()Ljava/lang/String; } diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 0a18c3b1..c0e512c5 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -188,7 +188,7 @@ class DokkaBase : DokkaPlugin() { } val rootCreator by extending { - htmlPreprocessors with RootCreator + htmlPreprocessors with RootCreator applyIf { !delayTemplateSubstitution } } val defaultSamplesTransformer by extending { diff --git a/plugins/base/src/main/kotlin/renderers/PackageListService.kt b/plugins/base/src/main/kotlin/renderers/PackageListService.kt index 9b753cb1..2bf66ebf 100644 --- a/plugins/base/src/main/kotlin/renderers/PackageListService.kt +++ b/plugins/base/src/main/kotlin/renderers/PackageListService.kt @@ -1,6 +1,10 @@ package org.jetbrains.dokka.base.renderers import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.resolvers.shared.LinkFormat +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.DOKKA_PARAM_PREFIX +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.SINGLE_MODULE_NAME +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.MODULE_DELIMITER import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext @@ -10,7 +14,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs class PackageListService(val context: DokkaContext, val rootPage: RootPageNode) { - fun createPackageList(module: ModulePage, format: String, linkExtension: String): String { + fun createPackageList(module: ModulePage, format: LinkFormat): String { val packages = mutableSetOf<String>() val nonStandardLocations = mutableMapOf<String, String>() @@ -31,7 +35,7 @@ class PackageListService(val context: DokkaContext, val rootPage: RootPageNode) ?: run { context.logger.error("Cannot resolve path for ${node.name}!"); null } if (dri != DRI.topLevel && locationProvider.expectedLocationForDri(dri) != nodeLocation) { - nonStandardLocations[dri.toString()] = "$nodeLocation.$linkExtension" + nonStandardLocations[dri.toString()] = "$nodeLocation.${format.linkExtension}" } } @@ -39,19 +43,22 @@ class PackageListService(val context: DokkaContext, val rootPage: RootPageNode) } visit(module) + return renderPackageList(nonStandardLocations, mapOf(SINGLE_MODULE_NAME to packages), format.formatName, format.linkExtension) + } - return buildString { + companion object { + fun renderPackageList(nonStandardLocations: Map<String, String>, modules: Map<String, Set<String>>, format: String, linkExtension: String): String = buildString { appendLine("$DOKKA_PARAM_PREFIX.format:${format}") appendLine("$DOKKA_PARAM_PREFIX.linkExtension:${linkExtension}") - nonStandardLocations.map { (signature, location) -> "$DOKKA_PARAM_PREFIX.location:$signature\u001f$location" } - .sorted().joinTo(this, separator = "\n", postfix = "\n") + nonStandardLocations.map { (signature, location) -> + "$DOKKA_PARAM_PREFIX.location:$signature\u001f$location" + }.sorted().joinTo(this, separator = "\n", postfix = "\n") - packages.sorted().joinTo(this, separator = "\n", postfix = "\n") + modules.mapNotNull { (module, packages) -> + ("$MODULE_DELIMITER$module\n".takeIf { module != SINGLE_MODULE_NAME }.orEmpty() + + packages.filter(String::isNotBlank).sorted().joinToString(separator = "\n")) + .takeIf { packages.isNotEmpty() } + }.joinTo(this, separator = "\n", postfix = "\n") } - - } - - companion object { - const val DOKKA_PARAM_PREFIX = "\$dokka" } } diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index 92f7324c..ff724f02 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -15,8 +15,8 @@ import org.jetbrains.dokka.plugability.configuration import org.jetbrains.dokka.transformers.pages.PageTransformer abstract class NavigationDataProvider { - open fun navigableChildren(input: RootPageNode): NavigationNode = - input.children.filterIsInstance<ContentPage>().single().let { visit(it) } + open fun navigableChildren(input: RootPageNode): NavigationNode = input.withDescendants() + .first { it is ModulePage || it is MultimoduleRootPage }.let { visit(it as ContentPage) } open fun visit(page: ContentPage): NavigationNode = NavigationNode( @@ -167,9 +167,9 @@ private fun List<String>.toRenderSpecificResourcePage(): List<RendererSpecificRe class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer { private val name = "scripts/sourceset_dependencies.js" override fun invoke(input: RootPageNode): RootPageNode { - val dependenciesMap = context.configuration.sourceSets.map { + val dependenciesMap = context.configuration.sourceSets.associate { it.sourceSetID to it.dependentSourceSets - }.toMap() + } fun createDependenciesJson(): String = dependenciesMap.map { (key, values) -> key.toString() to values.map { it.toString() } }.toMap() @@ -191,4 +191,4 @@ class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer { children = input.children + deps ).transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + name) } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/renderers/preprocessors.kt b/plugins/base/src/main/kotlin/renderers/preprocessors.kt index b64d2e1f..1a41162d 100644 --- a/plugins/base/src/main/kotlin/renderers/preprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/preprocessors.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.base.renderers import org.jetbrains.dokka.base.resolvers.shared.LinkFormat +import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -13,25 +14,20 @@ object RootCreator : PageTransformer { class PackageListCreator( val context: DokkaContext, val format: LinkFormat, - val outputFilesNames: List<String> = listOf("package-list"), - val removeModulePrefix: Boolean = true + val outputFilesNames: List<String> = listOf("package-list") ) : PageTransformer { - override fun invoke(input: RootPageNode) = - input.modified(children = input.children.map { - it.takeUnless { it is ModulePage } - ?: it.modified(children = it.children + packageList(input, it as ModulePage)) - }) - + override fun invoke(input: RootPageNode) = input.transformPageNodeTree { pageNode -> + pageNode.takeIf { it is ModulePage }?.let { it.modified(children = it.children + packageList(input, it as ModulePage)) } ?: pageNode + } private fun packageList(rootPageNode: RootPageNode, module: ModulePage): List<RendererSpecificPage> { val content = PackageListService(context, rootPageNode).createPackageList( module, - format.formatName, - format.linkExtension + format ) return outputFilesNames.map { fileName -> RendererSpecificResourcePage( - "${rootPageNode.name}/${fileName}", + fileName, emptyList(), RenderingStrategy.Write(content) ) diff --git a/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt index 09eb7cc4..fc7f57f4 100644 --- a/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt @@ -22,11 +22,21 @@ open class DefaultExternalLocationProvider( } protected open fun DRI.constructPath(): String { - val classNamesChecked = classNames ?: return "$docURL${packageName ?: ""}/index$extension" + val modulePart = packageName?.let { packageName -> + externalDocumentation.packageList.moduleFor(packageName)?.let { + if (it.isNotBlank()) + "$it/" + else + "" + } + }.orEmpty() + + val docWithModule = docURL + modulePart + val classNamesChecked = classNames ?: return "$docWithModule${packageName ?: ""}/index$extension" val classLink = (listOfNotNull(packageName) + classNamesChecked.split('.')) .joinToString("/", transform = ::identifierToFilename) val fileName = callable?.let { identifierToFilename(it.name) } ?: "index" - return "$docURL$classLink/$fileName$extension" + return "$docWithModule$classLink/$fileName$extension" } } diff --git a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt index b0398cd7..f1a32cb4 100644 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt @@ -8,22 +8,33 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.htmlEscape open class JavadocExternalLocationProvider( - externalDocumentation: ExternalDocumentation, - val brackets: String, - val separator: String, - dokkaContext: DokkaContext + externalDocumentation: ExternalDocumentation, + val brackets: String, + val separator: String, + dokkaContext: DokkaContext ) : DefaultExternalLocationProvider(externalDocumentation, ".html", dokkaContext) { override fun DRI.constructPath(): String { val packageLink = packageName?.replace(".", "/") + val modulePart = packageName?.let { packageName -> + externalDocumentation.packageList.moduleFor(packageName)?.let { + if (it.isNotBlank()) + "$it/" + else + "" + } + }.orEmpty() + + val docWithModule = docURL + modulePart + if (classNames == null) { - return "$docURL$packageLink/package-summary$extension".htmlEscape() + return "$docWithModule$packageLink/package-summary$extension".htmlEscape() } val classLink = - if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension" - val callableChecked = callable ?: return "$docURL$classLink".htmlEscape() + if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension" + val callableChecked = callable ?: return "$docWithModule$classLink".htmlEscape() - return ("$docURL$classLink#" + anchorPart(callableChecked)).htmlEscape() + return ("$docWithModule$classLink#" + anchorPart(callableChecked)).htmlEscape() } protected open fun anchorPart(callable: Callable) = callable.name + diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt index 87683414..3647bfa7 100644 --- a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt @@ -23,22 +23,20 @@ abstract class DefaultLocationProvider( dokkaContext.plugin<DokkaBase>().query { externalLocationProviderFactory } protected val externalLocationProviders: Map<ExternalDocumentation, ExternalLocationProvider?> = dokkaContext - .configuration - .sourceSets - .flatMap { sourceSet -> - sourceSet.externalDocumentationLinks.map { - PackageList.load(it.packageListUrl, sourceSet.jdkVersion, dokkaContext.configuration.offlineMode) - ?.let { packageList -> ExternalDocumentation(it.url, packageList) } + .configuration + .sourceSets + .flatMap { sourceSet -> + sourceSet.externalDocumentationLinks.map { + PackageList.load(it.packageListUrl, sourceSet.jdkVersion, dokkaContext.configuration.offlineMode) + ?.let { packageList -> ExternalDocumentation(it.url, packageList) } + } + } + .filterNotNull().associateWith { extDocInfo -> + externalLocationProviderFactories + .mapNotNull { it.getExternalLocationProvider(extDocInfo) } + .firstOrNull() + ?: run { dokkaContext.logger.error("No ExternalLocationProvider for '${extDocInfo.packageList.url}' found"); null } } - } - .filterNotNull() - .map { extDocInfo -> - val externalLocationProvider = (externalLocationProviderFactories.asSequence() - .mapNotNull { it.getExternalLocationProvider(extDocInfo) }.firstOrNull() - ?: run { dokkaContext.logger.error("No ExternalLocationProvider for '${extDocInfo.packageList.url}' found"); null }) - extDocInfo to externalLocationProvider - } - .toMap() protected val packagesIndex: Map<String, ExternalLocationProvider?> = externalLocationProviders diff --git a/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt b/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt index a06365eb..469904bd 100644 --- a/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt +++ b/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt @@ -1,20 +1,33 @@ package org.jetbrains.dokka.base.resolvers.shared -import org.jetbrains.dokka.base.renderers.PackageListService import java.net.URL +typealias Module = String + data class PackageList( val linkFormat: RecognizedLinkFormat, - val packages: Set<String>, + val modules: Map<Module, Set<String>>, val locations: Map<String, String>, val url: URL ) { + val packages: Set<String> + get() = modules.values.flatten().toSet() + + fun moduleFor(packageName: String) = modules.asSequence() + .filter { it.value.contains(packageName) } + .firstOrNull()?.key + companion object { + const val PACKAGE_LIST_NAME = "package-list" + const val MODULE_DELIMITER = "module:" + const val DOKKA_PARAM_PREFIX = "\$dokka" + const val SINGLE_MODULE_NAME = "" + fun load(url: URL, jdkVersion: Int, offlineMode: Boolean = false): PackageList? { if (offlineMode && url.protocol.toLowerCase() != "file") return null - val packageListStream = kotlin.runCatching { url.readContent() }.onFailure { + val packageListStream = runCatching { url.readContent() }.onFailure { println("Failed to download package-list from $url, this might suggest that remote resource is not available," + " module is empty or dokka output got corrupted") return null @@ -22,22 +35,36 @@ data class PackageList( val (params, packages) = packageListStream .bufferedReader() - .useLines { lines -> lines.partition { it.startsWith(PackageListService.DOKKA_PARAM_PREFIX) } } + .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } } val paramsMap = splitParams(params) val format = linkFormat(paramsMap["format"]?.singleOrNull(), jdkVersion) val locations = splitLocations(paramsMap["location"].orEmpty()).filterKeys(String::isNotEmpty) - return PackageList(format, packages.filter(String::isNotBlank).toSet(), locations, url) + val modulesMap = splitPackages(packages) + return PackageList(format, modulesMap, locations, url) } private fun splitParams(params: List<String>) = params.asSequence() - .map { it.removePrefix("${PackageListService.DOKKA_PARAM_PREFIX}.").split(":", limit = 2) } + .map { it.removePrefix("$DOKKA_PARAM_PREFIX.").split(":", limit = 2) } .groupBy({ (key, _) -> key }, { (_, value) -> value }) private fun splitLocations(locations: List<String>) = locations.map { it.split("\u001f", limit = 2) } - .map { (key, value) -> key to value } - .toMap() + .associate { (key, value) -> key to value } + + private fun splitPackages(packages: List<String>): Map<Module, Set<String>> = + packages.fold(("" to mutableMapOf<Module, Set<String>>())) { (lastModule, acc), el -> + val currentModule : String + when { + el.startsWith(MODULE_DELIMITER) -> currentModule = el.substringAfter(MODULE_DELIMITER) + el.isNotBlank() -> { + currentModule = lastModule + acc[currentModule] = acc.getOrDefault(lastModule, emptySet()) + el + } + else -> currentModule = lastModule + } + currentModule to acc + }.second private fun linkFormat(formatName: String?, jdkVersion: Int) = formatName?.let { RecognizedLinkFormat.fromString(it) } diff --git a/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt index c85b5946..071997fc 100644 --- a/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt @@ -19,7 +19,7 @@ class AndroidExternalLocationProviderTest : BaseAbstractTest() { URL("https://developer.android.com/reference/kotlin"), PackageList( RecognizedLinkFormat.DokkaHtml, - setOf("android.content", "android.net"), + mapOf("" to setOf("android.content", "android.net")), emptyMap(), URL("file://not-used") ) @@ -28,7 +28,7 @@ class AndroidExternalLocationProviderTest : BaseAbstractTest() { URL("https://developer.android.com/reference/kotlin"), PackageList( RecognizedLinkFormat.DokkaHtml, - setOf("androidx.appcompat.app"), + mapOf("" to setOf("androidx.appcompat.app")), emptyMap(), URL("file://not-used") ) diff --git a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt deleted file mode 100644 index e69de29b..00000000 --- a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt +++ /dev/null diff --git a/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt b/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt new file mode 100644 index 00000000..aefe913c --- /dev/null +++ b/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt @@ -0,0 +1,71 @@ +package locationProvider + +import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider +import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation +import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.net.URL + +class MultiModuleLinkingTest : BaseAbstractTest() { + private val testDataDir = + getTestDataDir("locationProvider").toAbsolutePath().toString().removePrefix("/").let { "/$it" } + private val exampleDomain = "https://example.com" + private val packageListURL = URL("file://$testDataDir/multi-module-package-list") + private val kotlinLang = "https://kotlinlang.org/api/latest/jvm/stdlib" + private val stdlibPackageListURL = URL("file://$testDataDir/stdlib-package-list") + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath += jvmStdlibPath!! + } + } + } + + private fun getTestLocationProvider(context: DokkaContext? = null): DefaultExternalLocationProvider { + val dokkaContext = context ?: DokkaContext.create(configuration, logger, emptyList()) + val packageList = PackageList.load(packageListURL, 8, true)!! + val externalDocumentation = + ExternalDocumentation(URL(exampleDomain), packageList) + return DefaultExternalLocationProvider(externalDocumentation, ".html", dokkaContext) + } + + private fun getStdlibTestLocationProvider(context: DokkaContext? = null): DefaultExternalLocationProvider { + val dokkaContext = context ?: DokkaContext.create(configuration, logger, emptyList()) + val packageList = PackageList.load(stdlibPackageListURL, 8, true)!! + val externalDocumentation = + ExternalDocumentation(URL(kotlinLang), packageList) + return DefaultExternalLocationProvider(externalDocumentation, ".html", dokkaContext) + } + + @Test + fun `should link to a multi-module declaration`() { + val locationProvider = getTestLocationProvider() + val dri = DRI("baz", "BazClass") + + assertEquals("$exampleDomain/moduleB/baz/-baz-class/index.html", locationProvider.resolve(dri)) + } + + @Test + fun `should not fail on non-present package`() { + val stdlibLocationProvider = getStdlibTestLocationProvider() + val locationProvider = getTestLocationProvider() + val dri = DRI("baz", "BazClass") + + assertEquals(null, stdlibLocationProvider.resolve(dri)) + assertEquals("$exampleDomain/moduleB/baz/-baz-class/index.html", locationProvider.resolve(dri)) + } + + @Test + fun `should handle relocations`() { + val locationProvider = getTestLocationProvider() + val dri = DRI("", "NoPackageClass") + + assertEquals("$exampleDomain/moduleB/[root]/-no-package-class/index.html", locationProvider.resolve(dri)) + } +} diff --git a/plugins/base/src/test/kotlin/packageList/PackageListTest.kt b/plugins/base/src/test/kotlin/packageList/PackageListTest.kt new file mode 100644 index 00000000..310a23c6 --- /dev/null +++ b/plugins/base/src/test/kotlin/packageList/PackageListTest.kt @@ -0,0 +1,65 @@ +package packageList + +import org.jetbrains.dokka.base.renderers.PackageListService +import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class PackageListTest { + @Test + fun `one module package list is created correctly`() { + val nonStandardLocations = mapOf("//longArrayWithFun/#kotlin.Int#kotlin.Function1[kotlin.Int,kotlin.Long]/PointingToDeclaration/" to "[JS root]/long-array-with-fun.html") + val modules = mapOf("" to setOf("foo", "bar", "baz")) + val format = RecognizedLinkFormat.DokkaHtml + val output = PackageListService.renderPackageList(nonStandardLocations, modules, format.formatName, format.linkExtension) + val expected = """ + |${'$'}dokka.format:html-v1 + |${'$'}dokka.linkExtension:html + |${'$'}dokka.location://longArrayWithFun/#kotlin.Int#kotlin.Function1[kotlin.Int,kotlin.Long]/PointingToDeclaration/[JS root]/long-array-with-fun.html + |bar + |baz + |foo + |""".trimMargin() + assertEquals(expected, output) + } + + @Test + fun `multi-module package list is created correctly`() { + val nonStandardLocations = mapOf("//longArrayWithFun/#kotlin.Int#kotlin.Function1[kotlin.Int,kotlin.Long]/PointingToDeclaration/" to "[JS root]/long-array-with-fun.html") + val modules = mapOf("moduleA" to setOf("foo", "bar"), "moduleB" to setOf("baz"), "moduleC" to setOf("qux")) + val format = RecognizedLinkFormat.DokkaHtml + val output = PackageListService.renderPackageList(nonStandardLocations, modules, format.formatName, format.linkExtension) + val expected = """ + |${'$'}dokka.format:html-v1 + |${'$'}dokka.linkExtension:html + |${'$'}dokka.location://longArrayWithFun/#kotlin.Int#kotlin.Function1[kotlin.Int,kotlin.Long]/PointingToDeclaration/[JS root]/long-array-with-fun.html + |module:moduleA + |bar + |foo + |module:moduleB + |baz + |module:moduleC + |qux + |""".trimMargin() + assertEquals(expected, output) + } + + @Test + fun `empty package set in module`() { + val nonStandardLocations = emptyMap<String, String>() + val modules = mapOf("moduleA" to setOf("foo", "bar"), "moduleB" to emptySet(), "moduleC" to setOf("qux")) + val format = RecognizedLinkFormat.DokkaHtml + val output = PackageListService.renderPackageList(nonStandardLocations, modules, format.formatName, format.linkExtension) + val expected = """ + |${'$'}dokka.format:html-v1 + |${'$'}dokka.linkExtension:html + | + |module:moduleA + |bar + |foo + |module:moduleC + |qux + |""".trimMargin() + assertEquals(expected, output) + } +} diff --git a/plugins/base/src/test/resources/locationProvider/multi-module-package-list b/plugins/base/src/test/resources/locationProvider/multi-module-package-list new file mode 100644 index 00000000..03f33d9a --- /dev/null +++ b/plugins/base/src/test/resources/locationProvider/multi-module-package-list @@ -0,0 +1,8 @@ +$dokka.format:html-v1 +$dokka.linkExtension:html +$dokka.location:/NoPackageClass///PointingToDeclaration/moduleB/[root]/-no-package-class/index.html +module:moduleA +foo +bar +module:moduleB +baz diff --git a/plugins/gfm/gfm-template-processing/api/gfm-template-processing.api b/plugins/gfm/gfm-template-processing/api/gfm-template-processing.api index c3f22dc3..e94d666e 100644 --- a/plugins/gfm/gfm-template-processing/api/gfm-template-processing.api +++ b/plugins/gfm/gfm-template-processing/api/gfm-template-processing.api @@ -9,6 +9,6 @@ public final class org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcess public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun finish (Ljava/io/File;)V public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; - public fun process (Ljava/io/File;Ljava/io/File;)Z + public fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z } diff --git a/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt b/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt index 93ce9659..844f72a2 100644 --- a/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt +++ b/plugins/gfm/gfm-template-processing/src/main/kotlin/org/jetbrains/dokka/gfm/templateProcessing/GfmTemplateProcessingStrategy.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.gfm.templateProcessing +import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin import org.jetbrains.dokka.base.templating.parseJson import org.jetbrains.dokka.gfm.GfmCommand @@ -20,7 +21,7 @@ class GfmTemplateProcessingStrategy(val context: DokkaContext) : TemplateProcess private val externalModuleLinkResolver = context.plugin<AllModulesPagePlugin>().querySingle { externalModuleLinkResolver } - override fun process(input: File, output: File): Boolean = + override fun process(input: File, output: File, moduleContext: DokkaConfiguration.DokkaModuleDescription?): Boolean = if (input.extension == "md") { input.bufferedReader().use { reader -> //This should also work whenever we have a misconfigured dokka and output is pointing to the input @@ -62,4 +63,4 @@ class GfmTemplateProcessingStrategy(val context: DokkaContext) : TemplateProcess externalModuleLinkResolver.resolve(dri, fileContext)?.let { address -> "[$label]($address)" } ?: label -}
\ No newline at end of file +} diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt index 0bbbbf86..7f90f1ce 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt @@ -7,6 +7,7 @@ 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.shared.PackageList.Companion.PACKAGE_LIST_NAME import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat import org.jetbrains.dokka.javadoc.pages.* import org.jetbrains.dokka.javadoc.transformers.documentables.JavadocDocumentableJVMSourceSetFilter @@ -56,7 +57,7 @@ class JavadocPlugin : DokkaPlugin() { PackageListCreator( context = it, format = RecognizedLinkFormat.DokkaJavadoc, - outputFilesNames = listOf("package-list", "element-list") + outputFilesNames = listOf(PACKAGE_LIST_NAME, "element-list") ) } order { after(rootCreator) } } diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt index aee38596..6d04093e 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt @@ -270,10 +270,10 @@ class AllClassesPage(val classes: List<JavadocClasslikePageNode>) : JavadocPageN dri: Set<DRI>, embeddedResources: List<String>, children: List<PageNode> - ): ContentPage = TODO() + ): ContentPage = this override fun modified(name: String, children: List<PageNode>): PageNode = - TODO() + this override val children: List<PageNode> = emptyList() diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt index 431f4464..89e4c535 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt @@ -32,8 +32,8 @@ internal class JavadocPackageListTest : AbstractJavadocTemplateMapTest() { package0 package1 """.trimIndent() - assertEquals(expected, contents["/element-list"]?.trimIndent()) - assertEquals(expected, contents["/package-list"]?.trimIndent()) + assertEquals(expected, contents["element-list"]?.trimIndent()) + assertEquals(expected, contents["package-list"]?.trimIndent()) } } @@ -69,8 +69,8 @@ internal class JavadocPackageListTest : AbstractJavadocTemplateMapTest() { package0.package0Inner package1.package1Inner.package1InnerInner """.trimIndent() - assertEquals(expected, contents["/element-list"]?.trimIndent()) - assertEquals(expected, contents["/package-list"]?.trimIndent()) + assertEquals(expected, contents["element-list"]?.trimIndent()) + assertEquals(expected, contents["package-list"]?.trimIndent()) } } -}
\ No newline at end of file +} diff --git a/plugins/templating/api/templating.api b/plugins/templating/api/templating.api index c779feef..ecf4a0b6 100644 --- a/plugins/templating/api/templating.api +++ b/plugins/templating/api/templating.api @@ -5,7 +5,7 @@ public abstract class org/jetbrains/dokka/allModulesPage/templates/BaseJsonNavig public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; public abstract fun getNavigationFileNameWithoutExtension ()Ljava/lang/String; public abstract fun getPath ()Ljava/lang/String; - public fun process (Ljava/io/File;Ljava/io/File;)Z + public fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z } public final class org/jetbrains/dokka/allModulesPage/templates/NavigationSearchTemplateStrategy : org/jetbrains/dokka/allModulesPage/templates/BaseJsonNavigationTemplateProcessingStrategy { @@ -15,6 +15,13 @@ public final class org/jetbrains/dokka/allModulesPage/templates/NavigationSearch public fun getPath ()Ljava/lang/String; } +public final class org/jetbrains/dokka/allModulesPage/templates/PackageListProcessingStrategy : org/jetbrains/dokka/templates/TemplateProcessingStrategy { + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun finish (Ljava/io/File;)V + public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; + public fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z +} + public final class org/jetbrains/dokka/allModulesPage/templates/PagesSearchTemplateStrategy : org/jetbrains/dokka/allModulesPage/templates/BaseJsonNavigationTemplateProcessingStrategy { public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public final fun getDokkaContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; @@ -42,6 +49,7 @@ public final class org/jetbrains/dokka/templates/CommandHandler$DefaultImpls { public final class org/jetbrains/dokka/templates/DefaultMultiModuleTemplateProcessor : org/jetbrains/dokka/templates/MultiModuleTemplateProcessor { public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; public fun process (Lorg/jetbrains/dokka/pages/RootPageNode;)V } @@ -54,13 +62,13 @@ public final class org/jetbrains/dokka/templates/DirectiveBasedHtmlTemplateProce public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun finish (Ljava/io/File;)V public final fun handleCommand (Lorg/jsoup/nodes/Element;Lorg/jetbrains/dokka/base/templating/Command;Ljava/io/File;Ljava/io/File;)V - public fun process (Ljava/io/File;Ljava/io/File;)Z + public fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z } public final class org/jetbrains/dokka/templates/FallbackTemplateProcessingStrategy : org/jetbrains/dokka/templates/TemplateProcessingStrategy { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V + public fun <init> ()V public fun finish (Ljava/io/File;)V - public fun process (Ljava/io/File;Ljava/io/File;)Z + public fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z } public abstract interface class org/jetbrains/dokka/templates/MultiModuleTemplateProcessor : org/jetbrains/dokka/templates/TemplateProcessor { @@ -89,7 +97,7 @@ public abstract interface class org/jetbrains/dokka/templates/Substitutor { public abstract interface class org/jetbrains/dokka/templates/TemplateProcessingStrategy { public abstract fun finish (Ljava/io/File;)V - public abstract fun process (Ljava/io/File;Ljava/io/File;)Z + public abstract fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z } public final class org/jetbrains/dokka/templates/TemplateProcessingStrategy$DefaultImpls { @@ -126,6 +134,7 @@ public final class org/jetbrains/dokka/templates/TemplatingPlugin : org/jetbrain public final fun getFallbackProcessingStrategy ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getMultimoduleTemplateProcessor ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getNavigationSearchTemplateStrategy ()Lorg/jetbrains/dokka/plugability/Extension; + public final fun getPackageListProcessingStrategy ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getPagesSearchTemplateStrategy ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getPathToRootSubstitutor ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getSourcesetDependencyProcessingStrategy ()Lorg/jetbrains/dokka/plugability/Extension; @@ -153,6 +162,6 @@ public final class templates/SourcesetDependencyProcessingStrategy : org/jetbrai public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun finish (Ljava/io/File;)V public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; - public fun process (Ljava/io/File;Ljava/io/File;)Z + public fun process (Ljava/io/File;Ljava/io/File;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Z } diff --git a/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt b/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt index c3b9aa53..2b4951a1 100644 --- a/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt +++ b/plugins/templating/src/main/kotlin/templates/DirectiveBasedTemplateProcessing.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.templates +import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.templating.Command import org.jetbrains.dokka.base.templating.parseJson import org.jetbrains.dokka.plugability.DokkaContext @@ -15,7 +16,7 @@ class DirectiveBasedHtmlTemplateProcessingStrategy(private val context: DokkaCon private val directiveBasedCommandHandlers = context.plugin<TemplatingPlugin>().query { directiveBasedCommandHandlers } - override fun process(input: File, output: File): Boolean = + override fun process(input: File, output: File, moduleContext: DokkaConfiguration.DokkaModuleDescription?): Boolean = if (input.isFile && input.extension == "html") { val document = Jsoup.parse(input, "UTF-8") document.outputSettings().indentAmount(0).prettyPrint(false) diff --git a/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt b/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt index 4e88c318..fdd9059d 100644 --- a/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt +++ b/plugins/templating/src/main/kotlin/templates/FallbackTemplateProcessingStrategy.kt @@ -1,13 +1,13 @@ package org.jetbrains.dokka.templates +import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.plugability.DokkaContext import java.io.File -import java.nio.file.Files -class FallbackTemplateProcessingStrategy(dokkaContext: DokkaContext) : TemplateProcessingStrategy { +class FallbackTemplateProcessingStrategy : TemplateProcessingStrategy { - override fun process(input: File, output: File): Boolean { - if(input != output) input.copyTo(output, overwrite = true) + override fun process(input: File, output: File, moduleContext: DokkaConfiguration.DokkaModuleDescription?): Boolean { + if (input != output) input.copyTo(output, overwrite = true) return true } -}
\ No newline at end of file +} diff --git a/plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt b/plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt index dec37799..9e064a6a 100644 --- a/plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt +++ b/plugins/templating/src/main/kotlin/templates/JsonElementBasedTemplateProcessingStrategy.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.allModulesPage.templates +import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription import org.jetbrains.dokka.base.renderers.html.SearchRecord import org.jetbrains.dokka.base.templating.AddToSearch import org.jetbrains.dokka.base.templating.parseJson @@ -18,11 +19,11 @@ abstract class BaseJsonNavigationTemplateProcessingStrategy(val context: DokkaCo open fun canProcess(file: File): Boolean = file.extension == "json" && file.nameWithoutExtension == navigationFileNameWithoutExtension - override fun process(input: File, output: File): Boolean { + override fun process(input: File, output: File, moduleContext: DokkaModuleDescription?): Boolean { val canProcess = canProcess(input) if (canProcess) { runCatching { parseJson<AddToSearch>(input.readText()) }.getOrNull()?.let { command -> - context.configuration.modules.find { it.name == command.moduleName }?.relativePathToOutputDirectory + moduleContext?.relativePathToOutputDirectory ?.relativeToOrSelf(context.configuration.outputDir) ?.let { key -> fragments[key.toString()] = command.elements @@ -43,7 +44,7 @@ abstract class BaseJsonNavigationTemplateProcessingStrategy(val context: DokkaCo } private fun fallbackToCopy(input: File, output: File) { - context.logger.warn("Falling back to just copying file for ${input.name} even thought it should process it") + context.logger.warn("Falling back to just copying ${input.name} file even though it should have been processed") input.copyTo(output) } @@ -62,4 +63,4 @@ class PagesSearchTemplateStrategy(val dokkaContext: DokkaContext) : BaseJsonNavigationTemplateProcessingStrategy(dokkaContext) { override val navigationFileNameWithoutExtension: String = "pages" override val path: String = "scripts" -}
\ No newline at end of file +} diff --git a/plugins/templating/src/main/kotlin/templates/PackageListProcessingStrategy.kt b/plugins/templating/src/main/kotlin/templates/PackageListProcessingStrategy.kt new file mode 100644 index 00000000..07238ea7 --- /dev/null +++ b/plugins/templating/src/main/kotlin/templates/PackageListProcessingStrategy.kt @@ -0,0 +1,51 @@ +package org.jetbrains.dokka.allModulesPage.templates + +import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription +import org.jetbrains.dokka.base.renderers.PackageListService +import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider +import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.PACKAGE_LIST_NAME +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.templates.TemplateProcessingStrategy +import java.io.File + +class PackageListProcessingStrategy(val context: DokkaContext) : TemplateProcessingStrategy { + private val fragments = mutableSetOf<PackageList>() + + private fun canProcess(file: File, moduleContext: DokkaModuleDescription?): Boolean = + file.extension.isBlank() && file.nameWithoutExtension == PACKAGE_LIST_NAME && moduleContext != null + + override fun process(input: File, output: File, moduleContext: DokkaModuleDescription?): Boolean { + val canProcess = canProcess(input, moduleContext) + if (canProcess) { + val packageList = PackageList.load(input.toURI().toURL(), 8, true) + val moduleFilename = moduleContext?.name?.let { "$it/" } + packageList?.copy( + modules = mapOf(moduleContext?.name.orEmpty() to packageList.modules.getOrDefault(PackageList.SINGLE_MODULE_NAME, emptySet())), + locations = packageList.locations.entries.associate { it.key to "$moduleFilename${it.value}" } + )?.let { fragments.add(it) } ?: fallbackToCopy(input, output) + } + return canProcess + } + + override fun finish(output: File) { + if (fragments.isNotEmpty()) { + val linkFormat = fragments.first().linkFormat + + if (!fragments.all { it.linkFormat == linkFormat }) { + context.logger.error("Link format is inconsistent between modules: " + fragments.joinToString { it.linkFormat.formatName } ) + } + + val locations: Map<String, String> = fragments.map { it.locations }.fold(emptyMap()) { acc, el -> acc + el } + val modules: Map<String, Set<String>> = fragments.map { it.modules }.fold(emptyMap()) { acc, el -> acc + el } + val mergedPackageList = PackageListService.renderPackageList(locations, modules, linkFormat.formatName, linkFormat.linkExtension) + output.mkdirs() + output.resolve(PACKAGE_LIST_NAME).writeText(mergedPackageList) + } + } + + private fun fallbackToCopy(input: File, output: File) { + context.logger.warn("Falling back to just copying ${input.name} file even though it should have been processed") + input.copyTo(output) + } +} diff --git a/plugins/templating/src/main/kotlin/templates/SourcesetDependencyProcessingStrategy.kt b/plugins/templating/src/main/kotlin/templates/SourcesetDependencyProcessingStrategy.kt index 11bbc39a..ddebe160 100644 --- a/plugins/templating/src/main/kotlin/templates/SourcesetDependencyProcessingStrategy.kt +++ b/plugins/templating/src/main/kotlin/templates/SourcesetDependencyProcessingStrategy.kt @@ -1,5 +1,6 @@ package templates +import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.templating.AddToSourcesetDependencies import org.jetbrains.dokka.base.templating.parseJson import org.jetbrains.dokka.base.templating.toJsonString @@ -23,11 +24,11 @@ class SourcesetDependencyProcessingStrategy(val context: DokkaContext) : Templat } } - override fun process(input: File, output: File): Boolean = + override fun process(input: File, output: File, moduleContext: DokkaConfiguration.DokkaModuleDescription?): Boolean = input.takeIf { it.name == fileName } ?.runCatching { parseJson<AddToSourcesetDependencies>(input.readText()) } ?.getOrNull() ?.also { (moduleName, content) -> fragments += (moduleName to content) } != null -}
\ No newline at end of file +} diff --git a/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt b/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt index c67654ec..5f36530b 100644 --- a/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt +++ b/plugins/templating/src/main/kotlin/templates/TemplateProcessor.kt @@ -1,7 +1,9 @@ package org.jetbrains.dokka.templates -import kotlinx.coroutines.* -import org.jetbrains.dokka.DokkaConfiguration +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.runBlocking +import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.templating.Command import org.jetbrains.dokka.model.withDescendants @@ -16,7 +18,7 @@ import java.io.File interface TemplateProcessor interface SubmoduleTemplateProcessor : TemplateProcessor { - fun process(modules: List<DokkaConfiguration.DokkaModuleDescription>): TemplatingResult + fun process(modules: List<DokkaModuleDescription>): TemplatingResult } interface MultiModuleTemplateProcessor : TemplateProcessor { @@ -24,7 +26,7 @@ interface MultiModuleTemplateProcessor : TemplateProcessor { } interface TemplateProcessingStrategy { - fun process(input: File, output: File): Boolean + fun process(input: File, output: File, moduleContext: DokkaModuleDescription?): Boolean fun finish(output: File) {} } @@ -36,18 +38,18 @@ class DefaultSubmoduleTemplateProcessor( context.plugin<TemplatingPlugin>().query { templateProcessingStrategy } private val configuredModulesPaths = - context.configuration.modules.map { it.sourceOutputDirectory.absolutePath to it.name }.toMap() + context.configuration.modules.associate { it.sourceOutputDirectory.absolutePath to it.name } - override fun process(modules: List<DokkaConfiguration.DokkaModuleDescription>) = + override fun process(modules: List<DokkaModuleDescription>) = runBlocking(Dispatchers.Default) { coroutineScope { modules.fold(TemplatingResult()) { acc, module -> - acc + module.sourceOutputDirectory.visit(context.configuration.outputDir.resolve(module.relativePathToOutputDirectory)) + acc + module.sourceOutputDirectory.visit(context.configuration.outputDir.resolve(module.relativePathToOutputDirectory), module) } } } - private suspend fun File.visit(target: File, acc: TemplatingResult = TemplatingResult()): TemplatingResult = + private suspend fun File.visit(target: File, module: DokkaModuleDescription, acc: TemplatingResult = TemplatingResult()): TemplatingResult = coroutineScope { val source = this@visit if (source.isDirectory) { @@ -59,17 +61,17 @@ class DefaultSubmoduleTemplateProcessor( ?: acc files.fold(accWithSelf) { acc, path -> - source.resolve(path).visit(target.resolve(path), acc) + source.resolve(path).visit(target.resolve(path), module, acc) } } else { - strategies.first { it.process(source, target) } + strategies.first { it.process(source, target, module) } acc } } } class DefaultMultiModuleTemplateProcessor( - context: DokkaContext, + val context: DokkaContext, ) : MultiModuleTemplateProcessor { private val strategies: List<TemplateProcessingStrategy> = context.plugin<TemplatingPlugin>().query { templateProcessingStrategy } @@ -78,10 +80,9 @@ class DefaultMultiModuleTemplateProcessor( override fun process(generatedPagesTree: RootPageNode) { val locationProvider = locationProviderFactory.getLocationProvider(generatedPagesTree) - generatedPagesTree.withDescendants().mapNotNull { locationProvider.resolve(it)?.let { File(it) } } - .forEach { location -> strategies.first { it.process(location, location) } } + generatedPagesTree.withDescendants().mapNotNull { pageNode -> locationProvider.resolve(pageNode)?.let { File(it) } } + .forEach { location -> strategies.first { it.process(location, location, null) } } } - } data class TemplatingContext<out T : Command>( @@ -93,4 +94,4 @@ data class TemplatingContext<out T : Command>( data class TemplatingResult(val modules: List<String> = emptyList()) { operator fun plus(rhs: TemplatingResult): TemplatingResult = TemplatingResult((modules + rhs.modules).distinct()) -}
\ No newline at end of file +} diff --git a/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt b/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt index 562b12c6..0842bb2b 100644 --- a/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt +++ b/plugins/templating/src/main/kotlin/templates/TemplatingPlugin.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.templates import org.jetbrains.dokka.allModulesPage.templates.NavigationSearchTemplateStrategy +import org.jetbrains.dokka.allModulesPage.templates.PackageListProcessingStrategy import org.jetbrains.dokka.allModulesPage.templates.PagesSearchTemplateStrategy import org.jetbrains.dokka.plugability.DokkaPlugin import templates.SourcesetDependencyProcessingStrategy @@ -45,8 +46,14 @@ class TemplatingPlugin : DokkaPlugin() { } } + val packageListProcessingStrategy by extending { + templateProcessingStrategy providing ::PackageListProcessingStrategy order { + before(fallbackProcessingStrategy) + } + } + val fallbackProcessingStrategy by extending { - templateProcessingStrategy providing ::FallbackTemplateProcessingStrategy + templateProcessingStrategy with FallbackTemplateProcessingStrategy() } val pathToRootSubstitutor by extending { @@ -59,4 +66,4 @@ class TemplatingPlugin : DokkaPlugin() { val substitutionCommandHandler by extending { directiveBasedCommandHandlers providing ::SubstitutionCommandHandler } -}
\ No newline at end of file +} diff --git a/plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt index a1e11dfe..fb229643 100644 --- a/plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt +++ b/plugins/templating/src/test/kotlin/templates/AddToNavigationCommandResolutionTest.kt @@ -134,4 +134,4 @@ class AddToNavigationCommandResolutionTest : TemplatingAbstractTest() { } private data class ModuleWithPrefix(val moduleName: String, val prefix: String? = null) -}
\ No newline at end of file +} diff --git a/plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt index bb4fb1c4..aba668f7 100644 --- a/plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt +++ b/plugins/templating/src/test/kotlin/templates/AddToSearchCommandResolutionTest.kt @@ -80,4 +80,4 @@ class AddToSearchCommandResolutionTest : TemplatingAbstractTest() { return listOf(module1Navigation, module2Navigation) } -}
\ No newline at end of file +} diff --git a/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt b/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt index 9d211876..ce2a8afd 100644 --- a/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt +++ b/plugins/templating/src/test/kotlin/templates/SubstitutionCommandResolutionTest.kt @@ -66,4 +66,4 @@ class SubstitutionCommandResolutionTest : TemplatingAbstractTest() { return module1Content } -}
\ No newline at end of file +} diff --git a/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt b/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt index da7ebdc1..1cc584b7 100644 --- a/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt +++ b/plugins/versioning/src/main/kotlin/versioning/VersioningHandler.kt @@ -85,7 +85,7 @@ class DefaultVersioningHandler(val context: DokkaContext) : VersioningHandler { overwrite = true ) else processingStrategies.first { - it.process(versionRootContent, targetParent.resolve(versionRootContent.name)) + it.process(versionRootContent, targetParent.resolve(versionRootContent.name), null) } } } @@ -101,4 +101,4 @@ class DefaultVersioningHandler(val context: DokkaContext) : VersioningHandler { private const val OLDER_VERSIONS_DIR = "older" private const val VERSIONS_FILE = "version.json" } -}
\ No newline at end of file +} |