aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src
diff options
context:
space:
mode:
authorAndrzej Ratajczak <andrzej.ratajczak98@gmail.com>2020-03-13 10:46:33 +0100
committerBłażej Kardyś <bkardys@virtuslab.com>2020-03-23 13:57:07 +0100
commitc09bde34ff729ef9b1f3bea602fb53cd4e6dca42 (patch)
tree3a744a16c133b813ab1849761aef1ec3544f401f /plugins/base/src
parentcedf8b7594deef2cd26e981865daa8aae0155520 (diff)
downloaddokka-c09bde34ff729ef9b1f3bea602fb53cd4e6dca42.tar.gz
dokka-c09bde34ff729ef9b1f3bea602fb53cd4e6dca42.tar.bz2
dokka-c09bde34ff729ef9b1f3bea602fb53cd4e6dca42.zip
Gradle Task supporting multimodular projects
Diffstat (limited to 'plugins/base/src')
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt15
-rw-r--r--plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt2
-rw-r--r--plugins/base/src/main/kotlin/resolvers/DefaultLocationProvider.kt116
-rw-r--r--plugins/base/src/main/kotlin/resolvers/ExternalLocationProvider.kt99
-rw-r--r--plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt35
-rw-r--r--plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt23
-rw-r--r--plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt37
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt222
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProviderFactory.kt (renamed from plugins/base/src/main/kotlin/resolvers/DefaultLocationProviderFactory.kt)5
-rw-r--r--plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt (renamed from plugins/base/src/main/kotlin/resolvers/LocationProvider.kt)3
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt12
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt1
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt7
-rw-r--r--plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt12
14 files changed, 354 insertions, 235 deletions
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt
index 448373ea..548bcb93 100644
--- a/plugins/base/src/main/kotlin/DokkaBase.kt
+++ b/plugins/base/src/main/kotlin/DokkaBase.kt
@@ -4,10 +4,12 @@ import org.jetbrains.dokka.CoreExtensions
import org.jetbrains.dokka.base.renderers.FileWriter
import org.jetbrains.dokka.base.renderers.OutputWriter
import org.jetbrains.dokka.base.renderers.html.*
-import org.jetbrains.dokka.base.resolvers.DefaultLocationProviderFactory
-import org.jetbrains.dokka.base.resolvers.LocationProviderFactory
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider
import org.jetbrains.dokka.base.signatures.SignatureProvider
+import org.jetbrains.dokka.base.resolvers.external.*
+import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory
import org.jetbrains.dokka.base.transformers.documentables.DefaultDocumentableMerger
import org.jetbrains.dokka.base.transformers.documentables.InheritorsExtractorTransformer
import org.jetbrains.dokka.base.transformers.pages.annotations.DeprecatedStrikethroughTransformer
@@ -29,6 +31,7 @@ class DokkaBase : DokkaPlugin() {
val commentsToContentConverter by extensionPoint<CommentsToContentConverter>()
val signatureProvider by extensionPoint<SignatureProvider>()
val locationProviderFactory by extensionPoint<LocationProviderFactory>()
+ val externalLocationProviderFactory by extensionPoint<ExternalLocationProviderFactory>()
val outputWriter by extensionPoint<OutputWriter>()
val htmlPreprocessors by extensionPoint<PageTransformer>()
@@ -98,6 +101,14 @@ class DokkaBase : DokkaPlugin() {
locationProviderFactory providing ::DefaultLocationProviderFactory
}
+ val javadocLocationProvider by extending {
+ externalLocationProviderFactory with JavadocExternalLocationProviderFactory()
+ }
+
+ val dokkaLocationProvider by extending {
+ externalLocationProviderFactory with DokkaExternalLocationProviderFactory()
+ }
+
val fileWriter by extending(isFallback = true) {
outputWriter providing ::FileWriter
}
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
index 0e0001b9..0ff0511a 100644
--- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt
@@ -1,7 +1,7 @@
package org.jetbrains.dokka.base.renderers
import org.jetbrains.dokka.base.DokkaBase
-import org.jetbrains.dokka.base.resolvers.LocationProvider
+import org.jetbrains.dokka.base.resolvers.local.LocationProvider
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
diff --git a/plugins/base/src/main/kotlin/resolvers/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/DefaultLocationProvider.kt
deleted file mode 100644
index 2238b0c3..00000000
--- a/plugins/base/src/main/kotlin/resolvers/DefaultLocationProvider.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-package org.jetbrains.dokka.base.resolvers
-
-import org.jetbrains.dokka.links.DRI
-import org.jetbrains.dokka.pages.*
-import org.jetbrains.dokka.plugability.DokkaContext
-import org.jetbrains.dokka.utilities.htmlEscape
-import java.util.*
-
-private const val PAGE_WITH_CHILDREN_SUFFIX = "index"
-
-open class DefaultLocationProvider(
- protected val pageGraphRoot: RootPageNode,
- protected val dokkaContext: DokkaContext
-) : LocationProvider {
- protected val extension = ".html"
-
- protected val pagesIndex: Map<DRI, ContentPage> = pageGraphRoot.asSequence().filterIsInstance<ContentPage>()
- .map { it.dri.map { dri -> dri to it } }.flatten()
- .groupingBy { it.first }
- .aggregate { dri, _, (_, page), first ->
- if (first) page else throw AssertionError("Multiple pages associated with dri: $dri")
- }
-
- protected val pathsIndex: Map<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>().apply {
- fun registerPath(page: PageNode, prefix: List<String>) {
- val newPrefix = prefix + page.pathName
- put(page, newPrefix)
- page.children.forEach { registerPath(it, newPrefix) }
- }
- put(pageGraphRoot, emptyList())
- pageGraphRoot.children.forEach { registerPath(it, emptyList()) }
- }
-
- override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String =
- pathTo(node, context) + if (!skipExtension) extension else ""
-
- override fun resolve(dri: DRI, platforms: List<PlatformData>, context: PageNode?): String =
- pagesIndex[dri]?.let { resolve(it, context) } ?:
- // Not found in PageGraph, that means it's an external link
- ExternalLocationProvider.getLocation(dri,
- this.dokkaContext.configuration.passesConfigurations
- .filter { passConfig ->
- platforms.toSet()
- .contains(PlatformData(passConfig.moduleName, passConfig.analysisPlatform, passConfig.targets))
- } // TODO: change targets to something better?
- .flatMap { it.externalDocumentationLinks }.distinct()
- )
-
- override fun resolveRoot(node: PageNode): String =
- pathTo(pageGraphRoot, node).removeSuffix(PAGE_WITH_CHILDREN_SUFFIX)
-
- override fun ancestors(node: PageNode): List<PageNode> =
- generateSequence(node) { it.parent() }.toList()
-
- protected open fun pathTo(node: PageNode, context: PageNode?): String {
- fun pathFor(page: PageNode) = pathsIndex[page] ?: throw AssertionError(
- "${page::class.simpleName}(${page.name}) does not belong to current page graph so it is impossible to compute its path"
- )
-
- val contextNode =
- if (context?.children?.isEmpty() == true && context.parent() != null) context.parent() else context
- val nodePath = pathFor(node)
- val contextPath = contextNode?.let { pathFor(it) }.orEmpty()
-
- val commonPathElements = nodePath.asSequence().zip(contextPath.asSequence())
- .takeWhile { (a, b) -> a == b }.count()
-
- return (List(contextPath.size - commonPathElements) { ".." } + nodePath.drop(commonPathElements) +
- if (node.children.isNotEmpty()) listOf(PAGE_WITH_CHILDREN_SUFFIX) else emptyList()).joinToString("/")
- }
-
- private fun PageNode.parent() = pageGraphRoot.parentMap[this]
-}
-
-fun DRI.toJavadocLocation(jdkVersion: Int): String { // TODO: classes without packages?
- val packageLink = packageName?.replace(".", "/")
- if (classNames == null) {
- return "$packageLink/package-summary.html".htmlEscape()
- }
- val classLink = if (packageLink == null) "$classNames.html" else "$packageLink/$classNames.html"
- val callableChecked = callable ?: return classLink.htmlEscape()
-
- val callableLink = "$classLink#${callableChecked.name}" + when {
- jdkVersion < 8 -> "(${callableChecked.params.joinToString(", ")})"
- jdkVersion < 10 -> "-${callableChecked.params.joinToString("-")}-"
- else -> "(${callableChecked.params.joinToString(",")})"
- }
-
- return callableLink.htmlEscape()
-}
-
-fun DRI.toDokkaLocation(extension: String): String { // TODO: classes without packages?
- val classNamesChecked = classNames ?: return "$packageName/index$extension"
-
- val classLink = if (packageName == null) {
- ""
- } else {
- "$packageName/"
- } + classNamesChecked.split('.').joinToString("/", transform = ::identifierToFilename)
-
- val callableChecked = callable ?: return "$classLink/index$extension"
-
- return "$classLink/${identifierToFilename(callableChecked.name)}$extension"
-}
-
-private val reservedFilenames = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out")
-
-private fun identifierToFilename(name: String): String {
- if (name.isEmpty()) return "--root--"
- val escaped = name.replace('<', '-').replace('>', '-')
- val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
- return if (lowercase in reservedFilenames) "--$lowercase--" else lowercase
-}
-
-private val PageNode.pathName: String
- get() = if (this is PackagePageNode) name else identifierToFilename(name)
diff --git a/plugins/base/src/main/kotlin/resolvers/ExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/ExternalLocationProvider.kt
deleted file mode 100644
index 7c0e9952..00000000
--- a/plugins/base/src/main/kotlin/resolvers/ExternalLocationProvider.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.jetbrains.dokka.base.resolvers
-
-import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink
-import org.jetbrains.dokka.links.DRI
-import java.net.HttpURLConnection
-import java.net.URL
-import java.net.URLConnection
-
-object ExternalLocationProvider { // TODO: Refactor this!!!
- private const val DOKKA_PARAM_PREFIX = "\$dokka."
-
- private val cache: MutableMap<URL, LocationInfo> = mutableMapOf()
-
- fun getLocation(dri: DRI, externalDocumentationLinks: List<ExternalDocumentationLink>): String {
- val toResolve: MutableList<ExternalDocumentationLink> = mutableListOf()
- for(link in externalDocumentationLinks){
- val info = cache[link.packageListUrl]
- if(info == null) {
- toResolve.add(link)
- } else if(info.packages.contains(dri.packageName)) {
- return link.url.toExternalForm() + getLink(dri, info)
- }
- }
- // Not in cache, resolve packageLists
- while (toResolve.isNotEmpty()){
- val link = toResolve.first().also { toResolve.remove(it) }
- val locationInfo = loadPackageList(link.packageListUrl)
- if(locationInfo.packages.contains(dri.packageName)) {
- return link.url.toExternalForm() + getLink(dri, locationInfo)
- }
- }
- return ""
- }
-
- private fun getLink(dri: DRI, locationInfo: LocationInfo): String = when(locationInfo.format) {
- "javadoc" -> dri.toJavadocLocation(8)
- "kotlin-website-html", "html" -> locationInfo.locations[dri.packageName + "." + dri.classNames] ?: dri.toDokkaLocation(".html")
- "markdown" -> locationInfo.locations[dri.packageName + "." + dri.classNames] ?: dri.toDokkaLocation(".md")
- // TODO: rework this
- else -> throw RuntimeException("Unrecognized format")
- }
-
-
- private fun loadPackageList(url: URL): LocationInfo {
- val packageListStream = url.doOpenConnectionToReadContent().getInputStream()
- val (params, packages) =
- packageListStream
- .bufferedReader()
- .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } }
-
- val paramsMap = params.asSequence()
- .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) }
- .groupBy({ (key, _) -> key }, { (_, value) -> value })
-
- val format = paramsMap["format"]?.singleOrNull() ?: "javadoc"
-
- val locations = paramsMap["location"].orEmpty()
- .map { it.split("\u001f", limit = 2) }
- .map { (key, value) -> key to value }
- .toMap()
-
- val info = LocationInfo(format, packages.toSet(), locations)
- cache[url] = info
- return info
- }
-
- private fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
- val connection = this.openConnection().apply {
- connectTimeout = timeout
- readTimeout = timeout
- }
-
- when (connection) {
- is HttpURLConnection -> {
- return when (connection.responseCode) {
- in 200..299 -> {
- connection
- }
- HttpURLConnection.HTTP_MOVED_PERM,
- HttpURLConnection.HTTP_MOVED_TEMP,
- HttpURLConnection.HTTP_SEE_OTHER -> {
- if (redirectsAllowed > 0) {
- val newUrl = connection.getHeaderField("Location")
- URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1)
- } else {
- throw RuntimeException("Too many redirects")
- }
- }
- else -> {
- throw RuntimeException("Unhandled http code: ${connection.responseCode}")
- }
- }
- }
- else -> return connection
- }
- }
- data class LocationInfo(val format: String, val packages: Set<String>, val locations: Map<String, String>)
-
-}
diff --git a/plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt
new file mode 100644
index 00000000..ff9186f7
--- /dev/null
+++ b/plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt
@@ -0,0 +1,35 @@
+package org.jetbrains.dokka.base.resolvers.external
+
+import org.jetbrains.dokka.base.resolvers.local.identifierToFilename
+import org.jetbrains.dokka.links.DRI
+
+
+class DokkaExternalLocationProviderFactory : ExternalLocationProviderFactory by ExternalLocationProviderFactoryWithCache(
+ object : ExternalLocationProviderFactory {
+ override fun getExternalLocationProvider(param: String): ExternalLocationProvider? =
+ when (param) {
+ "kotlin-website-html", "html" -> DokkaExternalLocationProvider(param, ".html")
+ "markdown" -> DokkaExternalLocationProvider(param, ".md")
+ else -> null
+ }
+ }
+)
+
+class DokkaExternalLocationProvider(override val param: String, val extension: String) : ExternalLocationProvider {
+
+ override fun DRI.toLocation(): String { // TODO: classes without packages?
+
+ val classNamesChecked = classNames ?: return "${packageName ?: ""}/index$extension"
+
+ val classLink = (listOfNotNull(packageName) + classNamesChecked.split('.')).joinToString(
+ "/",
+ transform = ::identifierToFilename
+ )
+
+ val callableChecked = callable ?: return "$classLink/index$extension"
+
+ return "$classLink/${identifierToFilename(
+ callableChecked.name
+ )}$extension"
+ }
+}
diff --git a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt
new file mode 100644
index 00000000..6fb05024
--- /dev/null
+++ b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt
@@ -0,0 +1,23 @@
+package org.jetbrains.dokka.base.resolvers.external
+
+import org.jetbrains.dokka.links.DRI
+
+
+interface ExternalLocationProvider {
+
+ val param: String
+ fun DRI.toLocation(): String
+}
+
+interface ExternalLocationProviderFactory {
+
+ fun getExternalLocationProvider(param: String): ExternalLocationProvider?
+}
+
+class ExternalLocationProviderFactoryWithCache(val ext: ExternalLocationProviderFactory) : ExternalLocationProviderFactory {
+
+ private val locationProviders: MutableList<ExternalLocationProvider> = mutableListOf()
+
+ override fun getExternalLocationProvider(param: String): ExternalLocationProvider? =
+ locationProviders.find { it.param == param } ?: ext.getExternalLocationProvider(param)?.also { locationProviders.add(it) }
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt
new file mode 100644
index 00000000..c52c9bbb
--- /dev/null
+++ b/plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt
@@ -0,0 +1,37 @@
+package org.jetbrains.dokka.base.resolvers.external
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.utilities.htmlEscape
+
+class JavadocExternalLocationProviderFactory : ExternalLocationProviderFactory by ExternalLocationProviderFactoryWithCache(
+ object : ExternalLocationProviderFactory {
+ override fun getExternalLocationProvider(param: String): ExternalLocationProvider? =
+ when(param) {
+ "javadoc1" -> JavadocExternalLocationProvider(param, "()", ", ") // Covers JDK 1 - 7
+ "javadoc8" -> JavadocExternalLocationProvider(param, "--", "-") // Covers JDK 8 - 9
+ "javadoc10" -> JavadocExternalLocationProvider(param, "()", ",") // Covers JDK 10
+ else -> null
+ }
+ }
+)
+
+class JavadocExternalLocationProvider(override val param: String, val brackets: String, val separator: String) : ExternalLocationProvider {
+
+ override fun DRI.toLocation(): String {
+
+ val packageLink = packageName?.replace(".", "/")
+ if (classNames == null) {
+ return "$packageLink/package-summary.html".htmlEscape()
+ }
+ val classLink = if (packageLink == null) "$classNames.html" else "$packageLink/$classNames.html"
+ val callableChecked = callable ?: return classLink.htmlEscape()
+
+ val callableLink = "$classLink#" +
+ callableChecked.name +
+ "${brackets.first()}" +
+ callableChecked.params.joinToString(separator) +
+ "${brackets.last()}"
+
+ return callableLink.htmlEscape()
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt
new file mode 100644
index 00000000..736367a9
--- /dev/null
+++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt
@@ -0,0 +1,222 @@
+package org.jetbrains.dokka.base.resolvers.local
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProvider
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import java.lang.IllegalStateException
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+import java.util.*
+
+private const val PAGE_WITH_CHILDREN_SUFFIX = "index"
+private const val DOKKA_PARAM_PREFIX = "\$dokka."
+
+open class DefaultLocationProvider(
+ protected val pageGraphRoot: RootPageNode,
+ protected val dokkaContext: DokkaContext
+) : LocationProvider {
+ protected val extension = ".html"
+
+ protected val externalLocationProviderFactories =
+ dokkaContext.plugin<DokkaBase>().query { externalLocationProviderFactory }
+
+ protected val pagesIndex: Map<DRI, ContentPage> = pageGraphRoot.asSequence().filterIsInstance<ContentPage>()
+ .map { it.dri.map { dri -> dri to it } }.flatten()
+ .groupingBy { it.first }
+ .aggregate { dri, _, (_, page), first ->
+ if (first) page else throw AssertionError("Multiple pages associated with dri: $dri")
+ }
+
+ protected val pathsIndex: Map<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>().apply {
+ fun registerPath(page: PageNode, prefix: List<String>) {
+ val newPrefix = prefix + page.pathName
+ put(page, newPrefix)
+ page.children.forEach { registerPath(it, newPrefix) }
+ }
+ put(pageGraphRoot, emptyList())
+ pageGraphRoot.children.forEach { registerPath(it, emptyList()) }
+ }
+
+ override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String =
+ pathTo(node, context) + if (!skipExtension) extension else ""
+
+ override fun resolve(dri: DRI, platforms: List<PlatformData>, context: PageNode?): String =
+ pagesIndex[dri]?.let { resolve(it, context) } ?:
+ // Not found in PageGraph, that means it's an external link
+ getLocation(dri,
+ this.dokkaContext.configuration.passesConfigurations
+ .filter { passConfig ->
+ platforms.toSet()
+ .contains(PlatformData(passConfig.moduleName, passConfig.analysisPlatform, passConfig.targets))
+ } // TODO: change targets to something better?
+ .groupBy({ it.jdkVersion }, { it.externalDocumentationLinks })
+ .map { it.key to it.value.flatten().distinct() }.toMap()
+ )
+
+ override fun resolveRoot(node: PageNode): String =
+ pathTo(pageGraphRoot, node).removeSuffix(PAGE_WITH_CHILDREN_SUFFIX)
+
+ override fun ancestors(node: PageNode): List<PageNode> =
+ generateSequence(node) { it.parent() }.toList()
+
+ protected open fun pathTo(node: PageNode, context: PageNode?): String {
+ fun pathFor(page: PageNode) = pathsIndex[page] ?: throw AssertionError(
+ "${page::class.simpleName}(${page.name}) does not belong to current page graph so it is impossible to compute its path"
+ )
+
+ val contextNode =
+ if (context?.children?.isEmpty() == true && context.parent() != null) context.parent() else context
+ val nodePath = pathFor(node)
+ val contextPath = contextNode?.let { pathFor(it) }.orEmpty()
+
+ val commonPathElements = nodePath.asSequence().zip(contextPath.asSequence())
+ .takeWhile { (a, b) -> a == b }.count()
+
+ return (List(contextPath.size - commonPathElements) { ".." } + nodePath.drop(commonPathElements) +
+ if (node.children.isNotEmpty()) listOf(PAGE_WITH_CHILDREN_SUFFIX) else emptyList()).joinToString("/")
+ }
+
+ private fun PageNode.parent() = pageGraphRoot.parentMap[this]
+
+ private val cache: MutableMap<URL, LocationInfo> = mutableMapOf()
+
+ private fun getLocation(
+ dri: DRI,
+ jdkToExternalDocumentationLinks: Map<Int, List<DokkaConfiguration.ExternalDocumentationLink>>
+ ): String {
+ val toResolve: MutableMap<Int, MutableList<DokkaConfiguration.ExternalDocumentationLink>> = mutableMapOf()
+ for ((jdk, links) in jdkToExternalDocumentationLinks) {
+ for (link in links) {
+ val info = cache[link.packageListUrl]
+ if (info == null) {
+ toResolve.getOrPut(jdk) { mutableListOf() }.add(link)
+ } else if (info.packages.contains(dri.packageName)) {
+ return link.url.toExternalForm() + getLink(
+ dri,
+ info
+ )
+ }
+ }
+ }
+ // Not in cache, resolve packageLists
+ for ((jdk, links) in toResolve) {
+ for (link in links) {
+ val locationInfo =
+ loadPackageList(
+ jdk,
+ link.packageListUrl
+ )
+ if (locationInfo.packages.contains(dri.packageName)) {
+ return link.url.toExternalForm() + getLink(
+ dri,
+ locationInfo
+ )
+ }
+ }
+ toResolve.remove(jdk)
+ }
+ return ""
+ }
+
+ private fun getLink(dri: DRI, locationInfo: LocationInfo): String =
+ locationInfo.locations[dri.packageName + "." + dri.classNames]
+ ?: // Not sure if it can be here, previously it shadowed only kotlin/dokka related sources, here it shadows both dokka/javadoc, cause I cannot distinguish what LocationProvider has been hypothetically chosen
+ if (locationInfo.externalLocationProvider != null)
+ with(locationInfo.externalLocationProvider) {
+ dri.toLocation()
+ }
+ else
+ throw IllegalStateException("Have not found any convenient ExternalLocationProvider for $dri DRI!")
+
+ private fun loadPackageList(jdk: Int, url: URL): LocationInfo {
+ val packageListStream = url.doOpenConnectionToReadContent().getInputStream()
+ val (params, packages) =
+ packageListStream
+ .bufferedReader()
+ .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } }
+
+ val paramsMap = params.asSequence()
+ .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) }
+ .groupBy({ (key, _) -> key }, { (_, value) -> value })
+
+ val format = paramsMap["format"]?.singleOrNull() ?: when {
+ jdk < 8 -> "javadoc1" // Covers JDK 1 - 7
+ jdk < 10 -> "javadoc8" // Covers JDK 8 - 9
+ else -> "javadoc10" // Covers JDK 10+
+ }
+
+ val locations = paramsMap["location"].orEmpty()
+ .map { it.split("\u001f", limit = 2) }
+ .map { (key, value) -> key to value }
+ .toMap()
+
+ val externalLocationProvider =
+ externalLocationProviderFactories.asSequence().map { it.getExternalLocationProvider(format) }
+ .filterNotNull().take(1).firstOrNull()
+
+ val info = LocationInfo(
+ externalLocationProvider,
+ packages.toSet(),
+ locations
+ )
+ cache[url] = info
+ return info
+ }
+
+ private fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
+ val connection = this.openConnection().apply {
+ connectTimeout = timeout
+ readTimeout = timeout
+ }
+
+ when (connection) {
+ is HttpURLConnection -> {
+ return when (connection.responseCode) {
+ in 200..299 -> {
+ connection
+ }
+ HttpURLConnection.HTTP_MOVED_PERM,
+ HttpURLConnection.HTTP_MOVED_TEMP,
+ HttpURLConnection.HTTP_SEE_OTHER -> {
+ if (redirectsAllowed > 0) {
+ val newUrl = connection.getHeaderField("Location")
+ URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1)
+ } else {
+ throw RuntimeException("Too many redirects")
+ }
+ }
+ else -> {
+ throw RuntimeException("Unhandled http code: ${connection.responseCode}")
+ }
+ }
+ }
+ else -> return connection
+ }
+ }
+
+ data class LocationInfo(
+ val externalLocationProvider: ExternalLocationProvider?,
+ val packages: Set<String>,
+ val locations: Map<String, String>
+ )
+}
+
+private val reservedFilenames = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out")
+
+internal fun identifierToFilename(name: String): String {
+ if (name.isEmpty()) return "--root--"
+ val escaped = name.replace("<|>".toRegex(), "-")
+ val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() }
+ return if (lowercase in reservedFilenames) "--$lowercase--" else lowercase
+}
+
+private val PageNode.pathName: String
+ get() = if (this is PackagePageNode) name else identifierToFilename(
+ name
+ )
diff --git a/plugins/base/src/main/kotlin/resolvers/DefaultLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProviderFactory.kt
index c649e22b..57f53ba6 100644
--- a/plugins/base/src/main/kotlin/resolvers/DefaultLocationProviderFactory.kt
+++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProviderFactory.kt
@@ -1,9 +1,10 @@
-package org.jetbrains.dokka.base.resolvers
+package org.jetbrains.dokka.base.resolvers.local
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
class DefaultLocationProviderFactory(private val context: DokkaContext) : LocationProviderFactory {
- override fun getLocationProvider(pageNode: RootPageNode) = DefaultLocationProvider(pageNode, context)
+ override fun getLocationProvider(pageNode: RootPageNode) =
+ DefaultLocationProvider(pageNode, context)
} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/resolvers/LocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt
index 13f4563c..0814cb3e 100644
--- a/plugins/base/src/main/kotlin/resolvers/LocationProvider.kt
+++ b/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt
@@ -1,7 +1,6 @@
-package org.jetbrains.dokka.base.resolvers
+package org.jetbrains.dokka.base.resolvers.local
import org.jetbrains.dokka.links.DRI
-import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.PageNode
import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.dokka.pages.RootPageNode
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
index bb7dbaaf..48be8ae7 100644
--- a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
+++ b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt
@@ -12,17 +12,17 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableMerger
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal object DefaultDocumentableMerger : DocumentableMerger {
+
override fun invoke(modules: Collection<DModule>, context: DokkaContext): DModule {
- val name = modules.map { it.name }.distinct().singleOrNull() ?: run {
- context.logger.error("All module names need to be the same")
- modules.first().name
- }
+
+ val projectName =
+ modules.fold(modules.first().name) { acc, module -> acc.commonPrefixWith(module.name) }.takeIf { it.isNotEmpty() }
+ ?: "project"
return modules.reduce { left, right ->
val list = listOf(left, right)
-
DModule(
- name = name,
+ name = projectName,
packages = merge(
list.flatMap { it.packages },
DPackage::mergeWith
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt
index 150df302..9f86c82a 100644
--- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt
+++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilter.kt
@@ -15,6 +15,7 @@ internal object DocumentableVisibilityFilter : PreMergeDocumentableTransformer {
val packageOptions =
context.configuration.passesConfigurations.first { original.platformData.contains(it.platformData) }
.perPackageOptions
+
DocumentableFilter(packageOptions).processModule(original)
}
diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt
index 885fbfee..2eb63504 100644
--- a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt
+++ b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt
@@ -30,14 +30,15 @@ object DocTagToContentConverter : CommentsToContentConverter {
)
)
- fun buildList(ordered: Boolean) =
+ fun buildList(ordered: Boolean, start: Int = 1) =
listOf(
ContentList(
buildChildren(docTag),
ordered,
dci,
platforms,
- styles
+ styles,
+ ((PropertyContainer.empty<ContentNode>()) + SimpleAttr("start", start.toString()))
)
)
@@ -53,7 +54,7 @@ object DocTagToContentConverter : CommentsToContentConverter {
is H5 -> buildHeader(5)
is H6 -> buildHeader(6)
is Ul -> buildList(false)
- is Ol -> buildList(true)
+ is Ol -> buildList(true, docTag.params["start"]?.toInt() ?: 1)
is Li -> buildChildren(docTag)
is Br -> buildNewLine()
is B -> buildChildren(docTag, setOf(TextStyle.Strong))
diff --git a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt
index f77ec086..852ac735 100644
--- a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt
+++ b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt
@@ -2,9 +2,11 @@ package renderers
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.renderers.html.RootCreator
-import org.jetbrains.dokka.base.resolvers.DefaultLocationProviderFactory
-import org.jetbrains.dokka.base.resolvers.LocationProvider
-import org.jetbrains.dokka.base.resolvers.LocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.external.DokkaExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.external.JavadocExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.local.LocationProvider
+import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory
import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider
import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
@@ -26,7 +28,9 @@ abstract class RenderingOnlyTestBase {
val context = MockContext(
DokkaBase().outputWriter to { _ -> files },
DokkaBase().locationProviderFactory to ::DefaultLocationProviderFactory,
- DokkaBase().htmlPreprocessors to { _ -> RootCreator }
+ DokkaBase().htmlPreprocessors to { _ -> RootCreator },
+ DokkaBase().externalLocationProviderFactory to { _ -> ::JavadocExternalLocationProviderFactory },
+ DokkaBase().externalLocationProviderFactory to { _ -> ::DokkaExternalLocationProviderFactory }
)
protected val renderedContent: Element by lazy {