aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/Kotlin
diff options
context:
space:
mode:
authorSimon Ogorodnik <Simon.Ogorodnik@jetbrains.com>2017-05-03 13:45:30 +0300
committerSimon Ogorodnik <Simon.Ogorodnik@jetbrains.com>2017-05-11 19:52:40 +0300
commita86c859eba6154524f3b42461aad6b45f26e3650 (patch)
tree6772882331daf29c8d19e4a3ed77ef938d45b1ac /core/src/main/kotlin/Kotlin
parent022a6a6bc9a1d61f190715dec56c3bef31887388 (diff)
downloaddokka-a86c859eba6154524f3b42461aad6b45f26e3650.tar.gz
dokka-a86c859eba6154524f3b42461aad6b45f26e3650.tar.bz2
dokka-a86c859eba6154524f3b42461aad6b45f26e3650.zip
Support linking of external documentation
Introduce PackageListService #KT-16309 fixed
Diffstat (limited to 'core/src/main/kotlin/Kotlin')
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt32
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt21
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt129
3 files changed, 145 insertions, 37 deletions
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
index 71b636bf..2ff69b4c 100644
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -14,7 +14,9 @@ class DeclarationLinkResolver
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val refGraph: NodeReferenceGraph,
val logger: DokkaLogger,
- val options: DocumentationOptions) {
+ val options: DocumentationOptions,
+ val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver) {
+
fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock {
val symbol = try {
val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext,
@@ -27,9 +29,9 @@ class DeclarationLinkResolver
// don't include unresolved links in generated doc
// assume that if an href doesn't contain '/', it's not an attempt to reference an external file
if (symbol != null) {
- val jdkHref = buildJdkLink(symbol)
- if (jdkHref != null) {
- return ContentExternalLink(jdkHref)
+ val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol)
+ if (externalHref != null) {
+ return ContentExternalLink(externalHref)
}
return ContentNodeLazyLink(href, { -> refGraph.lookupOrWarn(symbol.signature(), logger) })
}
@@ -51,26 +53,4 @@ class DeclarationLinkResolver
return symbol
}
- fun buildJdkLink(symbol: DeclarationDescriptor): String? {
- if (symbol is JavaClassDescriptor) {
- val fqName = DescriptorUtils.getFqName(symbol)
- if (fqName.startsWith(Name.identifier("java")) || fqName.startsWith(Name.identifier("javax"))) {
- return javadocRoot + fqName.asString().replace(".", "/") + ".html"
- }
- }
- else if (symbol is JavaMethodDescriptor) {
- val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
- val containingClassLink = buildJdkLink(containingClass)
- if (containingClassLink != null) {
- val psi = symbol.sourcePsi() as? PsiMethod
- if (psi != null) {
- val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
- return containingClassLink + "#" + symbol.name + "(" + params + ")"
- }
- }
- }
- return null
- }
-
- private val javadocRoot = "http://docs.oracle.com/javase/${options.jdkVersion}/docs/api/"
}
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index 4dae3a54..6a18bf26 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -3,8 +3,7 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiJavaFile
-import org.jetbrains.dokka.DokkaConfiguration.PackageOptions
-import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
+import org.jetbrains.dokka.DokkaConfiguration.*
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
@@ -39,13 +38,13 @@ class DocumentationOptions(val outputDir: String,
reportUndocumented: Boolean = true,
val skipEmptyPackages: Boolean = true,
skipDeprecated: Boolean = false,
- val jdkVersion: Int = 6,
+ jdkVersion: Int = 6,
val generateIndexPages: Boolean = true,
val sourceLinks: List<SourceLinkDefinition> = emptyList(),
val impliedPlatforms: List<String> = emptyList(),
// Sorted by pattern length
- perPackageOptions: List<PackageOptions> = emptyList()) {
-
+ perPackageOptions: List<PackageOptions> = emptyList(),
+ externalDocumentationLinks: List<ExternalDocumentationLink> = emptyList()) {
init {
if (perPackageOptions.any { it.prefix == "" })
throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
@@ -56,6 +55,8 @@ class DocumentationOptions(val outputDir: String,
fun effectivePackageOptions(pack: String): PackageOptions = perPackageOptions.firstOrNull { pack.startsWith(it.prefix + ".") } ?: rootPackageOptions
fun effectivePackageOptions(pack: FqName): PackageOptions = effectivePackageOptions(pack.asString())
+
+ val externalDocumentationLinks = listOf(ExternalDocumentationLinkImpl("http://docs.oracle.com/javase/$jdkVersion/docs/api/")) + externalDocumentationLinks
}
private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor,
@@ -114,6 +115,7 @@ class DocumentationBuilder
fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
+ node.appendSignature(descriptor)
callback(node)
return node
}
@@ -207,9 +209,9 @@ class DocumentationBuilder
node.appendTextNode("?", NodeKind.NullabilityModifier)
}
if (classifierDescriptor != null) {
- val jdkLink = linkResolver.buildJdkLink(classifierDescriptor)
- if (jdkLink != null) {
- node.append(DocumentationNode(jdkLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+ if (externalLink != null) {
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
} else {
link(node, classifierDescriptor,
if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
@@ -598,7 +600,6 @@ class DocumentationBuilder
node.appendAnnotations(this)
node.appendModifiers(this)
node.appendSourceLink(source)
- node.appendSignature(this)
node.appendDefaultPlatforms(this)
overriddenDescriptors.forEach {
@@ -628,7 +629,6 @@ class DocumentationBuilder
node.appendAnnotations(this)
node.appendModifiers(this)
node.appendSourceLink(source)
- node.appendSignature(this)
if (isVar) {
node.appendTextNode("var", NodeKind.Modifier)
}
@@ -680,7 +680,6 @@ class DocumentationBuilder
}
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSignature(this)
if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) {
node.appendTextNode("vararg", NodeKind.Modifier)
}
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
new file mode 100644
index 00000000..8113f95d
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -0,0 +1,129 @@
+package org.jetbrains.dokka
+
+import com.google.inject.Inject
+import com.intellij.psi.PsiMethod
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
+import org.jetbrains.kotlin.resolve.descriptorUtil.parents
+import java.net.URL
+
+
+class ExternalDocumentationLinkResolver @Inject constructor(
+ val options: DocumentationOptions
+) {
+
+ val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>()
+ val formats = mutableMapOf<String, InboundExternalLinkResolutionService>()
+
+ class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>)
+
+ fun loadPackageLists() {
+ options.externalDocumentationLinks.forEach { link ->
+ val (params, packages) =
+ link.packageListUrl
+ .openStream()
+ .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 resolver = if (format == "javadoc") {
+ InboundExternalLinkResolutionService.Javadoc()
+ } else {
+ val linkExtension = paramsMap["linkExtension"]?.singleOrNull() ?:
+ throw RuntimeException("Failed to parse package list from ${link.packageListUrl}")
+ InboundExternalLinkResolutionService.Dokka(linkExtension)
+ }
+
+ val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations)
+
+ packages.map { FqName(it) }.forEach { packageFqNameToLocation[it] = rootInfo }
+ }
+ }
+
+ init {
+ loadPackageLists()
+ }
+
+ fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
+ val packageFqName: FqName =
+ when (symbol) {
+ is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
+ is PackageFragmentDescriptor -> symbol.fqName
+ else -> return null
+ }
+
+ val externalLocation = packageFqNameToLocation[packageFqName] ?: return null
+
+ val path = externalLocation.locations[symbol.signature()] ?:
+ externalLocation.resolver.getPath(symbol) ?: return null
+
+ return URL(externalLocation.rootUrl, path).toExternalForm()
+ }
+
+ companion object {
+ const val DOKKA_PARAM_PREFIX = "\$dokka."
+ }
+}
+
+
+interface InboundExternalLinkResolutionService {
+ fun getPath(symbol: DeclarationDescriptor): String?
+
+ class Javadoc : InboundExternalLinkResolutionService {
+ override fun getPath(symbol: DeclarationDescriptor): String? {
+ if (symbol is JavaClassDescriptor) {
+ return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html"
+ } else if (symbol is JavaMethodDescriptor) {
+ val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
+ val containingClassLink = getPath(containingClass)
+ if (containingClassLink != null) {
+ val psi = symbol.sourcePsi() as? PsiMethod
+ if (psi != null) {
+ val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
+ return containingClassLink + "#" + symbol.name + "(" + params + ")"
+ }
+ }
+ }
+ // TODO Kotlin javadoc
+ return null
+ }
+ }
+
+ class Dokka(val extension: String) : InboundExternalLinkResolutionService {
+ override fun getPath(symbol: DeclarationDescriptor): String? {
+ val leafElement = when (symbol) {
+ is CallableDescriptor, is TypeAliasDescriptor -> true
+ else -> false
+ }
+ val path = getPathWithoutExtension(symbol)
+ if (leafElement) return "$path.$extension"
+ else return "$path/index.$extension"
+ }
+
+ fun getPathWithoutExtension(symbol: DeclarationDescriptor): String {
+ if (symbol.containingDeclaration == null)
+ return identifierToFilename(symbol.name.asString())
+ else if (symbol is PackageFragmentDescriptor) {
+ return symbol.fqName.asString()
+ } else {
+ return getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString())
+ }
+ }
+
+ }
+}
+