From 0bf1d0f5491a62c56393a06cdfb4168778d9829e Mon Sep 17 00:00:00 2001 From: Kamil Doległo <9080183+kamildoleglo@users.noreply.github.com> Date: Mon, 5 Jul 2021 14:10:23 +0200 Subject: 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 --- plugins/base/api/base.api | 23 ++++--- plugins/base/src/main/kotlin/DokkaBase.kt | 2 +- .../main/kotlin/renderers/PackageListService.kt | 29 +++++---- .../kotlin/renderers/html/htmlPreprocessors.kt | 10 +-- .../src/main/kotlin/renderers/preprocessors.kt | 18 +++--- .../external/DefaultExternalLocationProvider.kt | 14 ++++- .../javadoc/JavadocExternalLocationProvider.kt | 27 +++++--- .../resolvers/local/DefaultLocationProvider.kt | 28 ++++----- .../main/kotlin/resolvers/shared/PackageList.kt | 43 ++++++++++--- .../AndroidExternalLocationProviderTest.kt | 4 +- .../locationProvider/DokkaLocationProviderTest.kt | 0 .../locationProvider/MultiModuleLinkingTest.kt | 71 ++++++++++++++++++++++ .../src/test/kotlin/packageList/PackageListTest.kt | 65 ++++++++++++++++++++ .../locationProvider/multi-module-package-list | 8 +++ 14 files changed, 270 insertions(+), 72 deletions(-) delete mode 100644 plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt create mode 100644 plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt create mode 100644 plugins/base/src/test/kotlin/packageList/PackageListTest.kt create mode 100644 plugins/base/src/test/resources/locationProvider/multi-module-package-list (limited to 'plugins/base') 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 (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;Z)V - public synthetic fun (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lorg/jetbrains/dokka/plugability/DokkaContext;Lorg/jetbrains/dokka/base/resolvers/shared/LinkFormat;Ljava/util/List;)V + public synthetic fun (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 (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 (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 (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() val nonStandardLocations = mutableMapOf() @@ -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, modules: Map>, 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().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.toRenderSpecificResourcePage(): List 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 = listOf("package-list"), - val removeModulePrefix: Boolean = true + val outputFilesNames: List = 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 { 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().query { externalLocationProviderFactory } protected val externalLocationProviders: Map = 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 = 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, + val modules: Map>, val locations: Map, val url: URL ) { + val packages: Set + 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) = 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) = 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): Map> = + packages.fold(("" to mutableMapOf>())) { (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 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() + 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 -- cgit