Skip to content

Commit

Permalink
Handle empty modules in package lists
Browse files Browse the repository at this point in the history
  • Loading branch information
kamildoleglo committed Jul 5, 2021
1 parent 53a6327 commit 8e4e226
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 21 deletions.
14 changes: 9 additions & 5 deletions plugins/base/src/main/kotlin/renderers/PackageListService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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.*
Expand Down Expand Up @@ -42,18 +43,21 @@ class PackageListService(val context: DokkaContext, val rootPage: RootPageNode)
}

visit(module)
return renderPackageList(nonStandardLocations, mapOf("" to packages), format.formatName, format.linkExtension)
return renderPackageList(nonStandardLocations, mapOf(SINGLE_MODULE_NAME to packages), format.formatName, format.linkExtension)
}

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")

modules.map { (module, packages) ->
"$MODULE_DELIMITER$module\n".takeIf { module.isNotBlank() }.orEmpty() + packages.filter(String::isNotBlank).sorted().joinToString(separator = "\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")
}
}
Expand Down
26 changes: 14 additions & 12 deletions plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data class PackageList(
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")
Expand Down Expand Up @@ -51,18 +52,19 @@ data class PackageList(
private fun splitLocations(locations: List<String>) = locations.map { it.split("\u001f", limit = 2) }
.associate { (key, value) -> key to value }

private fun splitPackages(packages: List<String>): Map<Module, Set<String>> {
var lastModule: Module = ""

return packages.fold(mutableMapOf()) { acc, el ->
if (el.startsWith(MODULE_DELIMITER)) {
lastModule = el.substringAfter(MODULE_DELIMITER)
} else if(el.isNotBlank()) {
acc[lastModule] = acc.getOrDefault(lastModule, emptySet()) + el
}
acc
}
}
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) }
Expand Down
65 changes: 65 additions & 0 deletions plugins/base/src/test/kotlin/packageList/PackageListTest.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,24 @@ class PackageListProcessingStrategy(val context: DokkaContext) : TemplateProcess
file.extension.isBlank() && file.nameWithoutExtension == PACKAGE_LIST_NAME && moduleContext != null

override fun process(input: File, output: File, moduleContext: DokkaModuleDescription?): Boolean {
if (canProcess(input, moduleContext)) {
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("", emptySet())),
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(input, moduleContext)
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")
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 }
Expand Down

0 comments on commit 8e4e226

Please sign in to comment.