aboutsummaryrefslogtreecommitdiff
path: root/plugins/base
diff options
context:
space:
mode:
authorKamil Doległo <9080183+kamildoleglo@users.noreply.github.com>2021-07-05 14:10:23 +0200
committerGitHub <noreply@github.com>2021-07-05 14:10:23 +0200
commit0bf1d0f5491a62c56393a06cdfb4168778d9829e (patch)
tree808f631e72b652dc2c3d5929f85f677968bc56f6 /plugins/base
parenta1d44ab80df217196fe5ee9455c7cf1c135e3b07 (diff)
downloaddokka-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/base')
-rw-r--r--plugins/base/api/base.api23
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt2
-rw-r--r--plugins/base/src/main/kotlin/renderers/PackageListService.kt29
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt10
-rw-r--r--plugins/base/src/main/kotlin/renderers/preprocessors.kt18
-rw-r--r--plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt14
-rw-r--r--plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt27
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt28
-rw-r--r--plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt43
-rw-r--r--plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt4
-rw-r--r--plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt0
-rw-r--r--plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt71
-rw-r--r--plugins/base/src/test/kotlin/packageList/PackageListTest.kt65
-rw-r--r--plugins/base/src/test/resources/locationProvider/multi-module-package-list8
14 files changed, 270 insertions, 72 deletions
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