aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/Kotlin
diff options
context:
space:
mode:
authorEugene Petrenko <eugene.petrenko@gmail.com>2018-09-11 14:03:29 +0300
committerEugene Petrenko <eugene.petrenko@gmail.com>2018-09-11 14:03:29 +0300
commita54070f5db04d05b48c369b211efe044fbc02a8d (patch)
treeaac5889906f49f68c39e1416ea39d4d19bad008e /core/src/main/kotlin/Kotlin
parente29f1c966056b0f7ee31dd9bd7d55f9e526fa7fe (diff)
parent0df19264ccae3d294946caf634ee15eea0c4fe4a (diff)
downloaddokka-a54070f5db04d05b48c369b211efe044fbc02a8d.tar.gz
dokka-a54070f5db04d05b48c369b211efe044fbc02a8d.tar.bz2
dokka-a54070f5db04d05b48c369b211efe044fbc02a8d.zip
Merge remote-tracking branch 'zub/dev-multiplatf' into kotlin-website-jonnyzzz
Diffstat (limited to 'core/src/main/kotlin/Kotlin')
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt13
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt34
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt486
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt171
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt22
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt2
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt25
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt9
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt34
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt201
10 files changed, 616 insertions, 381 deletions
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
index ffef399d..c3a84e57 100644
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
@@ -1,21 +1,18 @@
package org.jetbrains.dokka
import com.google.inject.Inject
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
-import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi
-import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi
class DeclarationLinkResolver
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val refGraph: NodeReferenceGraph,
val logger: DokkaLogger,
- val options: DocumentationOptions,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver,
- val descriptorSignatureProvider: DescriptorSignatureProvider) {
+ val elementSignatureProvider: ElementSignatureProvider) {
fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? {
@@ -34,14 +31,14 @@ class DeclarationLinkResolver
if (externalHref != null) {
return ContentExternalLink(externalHref)
}
- val signature = descriptorSignatureProvider.signature(symbol)
+ val signature = elementSignatureProvider.signature(symbol)
val referencedAt = fromDescriptor.signatureWithSourceLocation()
return ContentNodeLazyLink(href, { ->
val target = refGraph.lookup(signature)
if (target == null) {
- logger.warn("Can't find node by signature $signature, referenced at $referencedAt")
+ logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt")
}
target
})
@@ -66,7 +63,7 @@ class DeclarationLinkResolver
if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
return symbol.overriddenDescriptors.firstOrNull()
}
- if (symbol is TypeAliasDescriptor && !symbol.isDocumented(options)) {
+ if (symbol is TypeAliasDescriptor && !symbol.isDocumented(passConfiguration)) {
return symbol.classDescriptor
}
return symbol
diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
index eb8c12d0..d0650d45 100644
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
@@ -8,14 +8,17 @@ import org.intellij.markdown.parser.LinkMap
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Samples.SampleProcessingService
import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -25,12 +28,15 @@ import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
class DescriptorDocumentationParser
- @Inject constructor(val options: DocumentationOptions,
+ @Inject constructor(val options: DokkaConfiguration.PassConfiguration,
val logger: DokkaLogger,
val linkResolver: DeclarationLinkResolver,
val resolutionFacade: DokkaResolutionFacade,
val refGraph: NodeReferenceGraph,
- val sampleService: SampleProcessingService)
+ val sampleService: SampleProcessingService,
+ val signatureProvider: KotlinElementSignatureProvider,
+ val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
+)
{
fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false): Content =
parseDocumentationAndDetails(descriptor, inline).first
@@ -49,6 +55,13 @@ class DescriptorDocumentationParser
}
return Content.Empty to { node -> }
}
+
+ val contextDescriptor =
+ (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration)
+ ?.takeIf { it != descriptor.original.sourcePsi() }
+ ?.resolveToDescriptorIfAny()
+ ?: descriptor
+
var kdocText = kdoc.getContent()
// workaround for code fence parsing problem in IJ markdown parser
if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
@@ -56,20 +69,20 @@ class DescriptorDocumentationParser
}
val tree = parseMarkdown(kdocText)
val linkMap = LinkMap.buildLinkMap(tree.node, kdocText)
- val content = buildContent(tree, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(descriptor, href) }), inline)
+ val content = buildContent(tree, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }), inline)
if (kdoc is KDocSection) {
val tags = kdoc.getTags()
tags.forEach {
when (it.knownTag) {
KDocKnownTag.SAMPLE ->
- content.append(sampleService.resolveSample(descriptor, it.getSubjectName(), it))
+ content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it))
KDocKnownTag.SEE ->
- content.addTagToSeeAlso(descriptor, it)
+ content.addTagToSeeAlso(contextDescriptor, it)
else -> {
val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName())
val sectionContent = it.getContent()
val markdownNode = parseMarkdown(sectionContent)
- buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(descriptor, href) }))
+ buildInlineContentTo(markdownNode, section, LinkResolver(linkMap, { href -> linkResolver.resolveContentLink(contextDescriptor, href) }))
}
}
}
@@ -81,7 +94,7 @@ class DescriptorDocumentationParser
val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
return if (suppressAnnotation != null) {
@Suppress("UNCHECKED_CAST")
- (suppressAnnotation.argumentValue("names") as List<StringValue>).any { it.value == "NOT_DOCUMENTED" }
+ (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" }
} else containingDeclaration?.isSuppressWarning() ?: false
}
@@ -119,7 +132,12 @@ class DescriptorDocumentationParser
fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> {
val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
if (psi is PsiDocCommentOwner) {
- val parseResult = JavadocParser(refGraph, logger).parseDocumentation(psi as PsiNamedElement)
+ val parseResult = JavadocParser(
+ refGraph,
+ logger,
+ signatureProvider,
+ externalDocumentationLinkResolver
+ ).parseDocumentation(psi as PsiNamedElement)
return parseResult.content to { node ->
parseResult.deprecatedContent?.let {
val deprecationNode = DocumentationNode("", it, NodeKind.Modifier)
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index 61bf50d6..e5bc32ab 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -2,8 +2,9 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiField
import com.intellij.psi.PsiJavaFile
-import org.jetbrains.dokka.DokkaConfiguration.*
+import org.jetbrains.dokka.DokkaConfiguration.PassConfiguration
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
@@ -11,74 +12,31 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
+import org.jetbrains.kotlin.idea.util.makeNotNullable
+import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
+import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtVariableDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
-import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.*
-import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.supertypes
-import java.io.File
-import java.nio.file.Path
-import java.nio.file.Paths
+import org.jetbrains.kotlin.util.supertypesWithAny
import com.google.inject.name.Named as GuiceNamed
-class DocumentationOptions(val outputDir: String,
- val outputFormat: String,
- includeNonPublic: Boolean = false,
- val includeRootPackage: Boolean = false,
- reportUndocumented: Boolean = true,
- val skipEmptyPackages: Boolean = true,
- skipDeprecated: Boolean = false,
- 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(),
- externalDocumentationLinks: List<ExternalDocumentationLink> = emptyList(),
- noStdlibLink: Boolean,
- val languageVersion: String?,
- val apiVersion: String?,
- cacheRoot: String? = null,
- val suppressedFiles: List<File> = emptyList()) {
- init {
- if (perPackageOptions.any { it.prefix == "" })
- throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
- }
-
- val perPackageOptions = perPackageOptions.sortedByDescending { it.prefix.length }
- val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated)
-
- fun effectivePackageOptions(pack: String): PackageOptions = perPackageOptions.firstOrNull { pack == it.prefix || pack.startsWith(it.prefix + ".") } ?: rootPackageOptions
- fun effectivePackageOptions(pack: FqName): PackageOptions = effectivePackageOptions(pack.asString())
-
- val defaultLinks = run {
- val links = mutableListOf(ExternalDocumentationLink.Builder("http://docs.oracle.com/javase/$jdkVersion/docs/api/").build())
- if (!noStdlibLink)
- links += ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build()
- links
- }
-
- val externalDocumentationLinks = defaultLinks + externalDocumentationLinks
-
- val cacheRoot: Path? = when {
- cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka")
- cacheRoot != null -> Paths.get(cacheRoot)
- else -> null
- }
-}
-
private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor,
extensionReceiverDescriptor: DeclarationDescriptor,
allFqNames: Collection<FqName>): Boolean {
@@ -101,10 +59,14 @@ interface DefaultPlatformsProvider {
fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String>
}
+val ignoredSupertypes = setOf(
+ "kotlin.Annotation", "kotlin.Enum", "kotlin.Any"
+)
+
class DocumentationBuilder
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val descriptorDocumentationParser: DescriptorDocumentationParser,
- val options: DocumentationOptions,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val refGraph: NodeReferenceGraph,
val platformNodeRegistry: PlatformNodeRegistry,
val logger: DokkaLogger,
@@ -132,8 +94,20 @@ class DocumentationBuilder
refGraph.register(descriptor.signature(), node)
}
- fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
- val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
+ fun <T> nodeForDescriptor(
+ descriptor: T,
+ kind: NodeKind,
+ external: Boolean = false
+ ): DocumentationNode where T : DeclarationDescriptor, T : Named {
+ val (doc, callback) =
+ if (external) {
+ Content.Empty to { node -> }
+ } else {
+ descriptorDocumentationParser.parseDocumentationAndDetails(
+ descriptor,
+ kind == NodeKind.Parameter
+ )
+ }
val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
node.appendSignature(descriptor)
callback(node)
@@ -167,27 +141,20 @@ class DocumentationBuilder
appendTextNode(modifier, NodeKind.Modifier)
}
- fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType) {
+ fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) {
val unwrappedType = superType.unwrap()
if (unwrappedType is AbbreviatedType) {
- appendSupertype(descriptor, unwrappedType.abbreviation)
- } else if (!ignoreSupertype(unwrappedType)) {
+ appendSupertype(descriptor, unwrappedType.abbreviation, backref)
+ } else {
appendType(unwrappedType, NodeKind.Supertype)
val superclass = unwrappedType.constructor.declarationDescriptor
- link(superclass, descriptor, RefKind.Inheritor)
+ if (backref) {
+ link(superclass, descriptor, RefKind.Inheritor)
+ }
link(descriptor, superclass, RefKind.Superclass)
}
}
- private fun ignoreSupertype(superType: KotlinType): Boolean {
- val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor
- if (superClass != null) {
- val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
- return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
- }
- return false
- }
-
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
if (projection.isStarProjection) {
appendTextNode("*", NodeKind.Type)
@@ -225,19 +192,39 @@ class DocumentationBuilder
if (prefix != "") {
node.appendTextNode(prefix, NodeKind.Modifier)
}
- if (kotlinType.isMarkedNullable) {
+ if (kotlinType.isNullabilityFlexible()) {
+ node.appendTextNode("!", NodeKind.NullabilityModifier)
+ } else if (kotlinType.isMarkedNullable) {
node.appendTextNode("?", NodeKind.NullabilityModifier)
}
if (classifierDescriptor != null) {
- val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+ val externalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
if (externalLink != null) {
- node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ val targetNode =
+ refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
+ node.append(targetNode, RefKind.ExternalType)
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
} else {
- link(node, classifierDescriptor,
- if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
+ link(
+ node, classifierDescriptor,
+ if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link
+ )
+ }
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ node.append(
+ DocumentationNode(
+ classifierDescriptor.fqNameUnsafe.asString(),
+ Content.Empty,
+ NodeKind.QualifiedName
+ ), RefKind.Detail
+ )
}
}
+
append(node, RefKind.Detail)
node.appendAnnotations(kotlinType)
for (typeArgument in kotlinType.arguments) {
@@ -271,6 +258,17 @@ class DocumentationBuilder
}
}
+ fun DocumentationNode.appendExternalLink(externalLink: String) {
+ append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+
+ fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) {
+ val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor)
+ if (target != null) {
+ appendExternalLink(target)
+ }
+ }
+
fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
val kotlinVersion = annotation
.detail(NodeKind.Parameter)
@@ -300,7 +298,7 @@ class DocumentationBuilder
fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation
fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
- appendSourceLink(sourceElement.getPsi(), options.sourceLinks)
+ appendSourceLink(sourceElement.getPsi(), passConfiguration.sourceLinks)
}
fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) {
@@ -308,7 +306,7 @@ class DocumentationBuilder
}
fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? {
- if (!descriptor.isGenerated() && descriptor.isDocumented(options)) {
+ if (!descriptor.isGenerated() && descriptor.isDocumented(passConfiguration)) {
val node = descriptor.build()
append(node, kind)
return node
@@ -335,7 +333,7 @@ class DocumentationBuilder
fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) {
- if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return
+ if (descriptor.isGenerated() || !descriptor.isDocumented(passConfiguration)) return
val existingNode = refGraph.lookup(descriptor.signature())
if (existingNode != null) {
@@ -407,25 +405,54 @@ class DocumentationBuilder
val allFqNames = fragments.map { it.fqName }.distinct()
for (packageName in allFqNames) {
- if (packageName.isRoot && !options.includeRootPackage) continue
+ if (packageName.isRoot && !passConfiguration.includeRootPackage) continue
val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() }
- if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue
+ if (passConfiguration.skipEmptyPackages && declarations.none { it.isDocumented(passConfiguration) }) continue
logger.info(" package $packageName: ${declarations.count()} declarations")
- val packageNode = findOrCreatePackageNode(packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
+ val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode,
declarations, allFqNames)
}
- propagateExtensionFunctionsToSubclasses(fragments)
}
- private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
- val allDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
- val allClasses = allDescriptors.filterIsInstance<ClassDescriptor>()
- val classHierarchy = buildClassHierarchy(allClasses)
+ fun propagateExtensionFunctionsToSubclasses(
+ fragments: Collection<PackageFragmentDescriptor>,
+ resolutionFacade: DokkaResolutionFacade
+ ) {
+
+ val moduleDescriptor = resolutionFacade.moduleDescriptor
+
+ // Wide-collect all view descriptors
+ val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages ->
+ packages
+ .flatMap { pkg ->
+ moduleDescriptor.getSubPackagesOf(pkg.fqName) { true }
+ }.map { fqName ->
+ moduleDescriptor.getPackage(fqName)
+ }.takeUnless { it.isEmpty() }
+ }.flatten()
+
+ val allDescriptors =
+ if (passConfiguration.collectInheritedExtensionsFromLibraries) {
+ allPackageViewDescriptors.map { it.memberScope }
+ } else {
+ fragments.asSequence().map { it.getMemberScope() }
+ }.flatMap {
+ it.getDescriptorsFiltered(
+ DescriptorKindFilter.CALLABLES
+ ).asSequence()
+ }
+
+
+ val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
+ val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>()
- val allExtensionFunctions = allDescriptors
+ val classHierarchy = buildClassHierarchy(documentingClasses)
+
+ val allExtensionFunctions =
+ allDescriptors
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.extensionReceiverParameter != null }
val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
@@ -433,15 +460,31 @@ class DocumentationBuilder
for (extensionFunction in allExtensionFunctions) {
if (extensionFunction.dispatchReceiverParameter != null) continue
val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
- ?.filter { fn -> fn.canShadow(extensionFunction) }
+ ?.filter { fn -> fn.canShadow(extensionFunction) }
?: emptyList()
if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
- val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
- val subclasses = classHierarchy[classDescriptor] ?: continue
- subclasses.forEach { subclass ->
+ val subclasses =
+ classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) }
+ if (subclasses.isEmpty()) continue
+ subclasses.values.flatten().forEach { subclass ->
if (subclass.isExtensionApplicable(extensionFunction) &&
- possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+ possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+
+ val hasExternalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(
+ extensionFunction
+ ) != null
+ if (hasExternalLink) {
+ val containerDesc =
+ extensionFunction.containingDeclaration as? PackageFragmentDescriptor
+ if (containerDesc != null) {
+ val container = refGraph.lookup(containerDesc.signature())
+ ?: containerDesc.buildExternal()
+ container.append(extensionFunction.buildExternal(), RefKind.Member)
+ }
+ }
+
refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension)
}
}
@@ -449,12 +492,9 @@ class DocumentationBuilder
}
private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
- val receiverType = extensionFunction.extensionReceiverParameter!!.type
- if (receiverType.arguments.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) {
- val receiverClass = receiverType.constructor.declarationDescriptor
- return receiverClass is ClassDescriptor && DescriptorUtils.isSubclass(this, receiverClass)
- }
- return defaultType.isSubtypeOf(receiverType)
+ val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable()
+ val classType = defaultType.toFuzzyType(declaredTypeParameters)
+ return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null
}
private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> {
@@ -493,34 +533,60 @@ class DocumentationBuilder
}
fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
- is ClassDescriptor -> build()
+ is ClassifierDescriptor -> build()
is ConstructorDescriptor -> build()
is PropertyDescriptor -> build()
is FunctionDescriptor -> build()
- is TypeParameterDescriptor -> build()
is ValueParameterDescriptor -> build()
is ReceiverParameterDescriptor -> build()
- is TypeAliasDescriptor -> build()
else -> throw IllegalStateException("Descriptor $this is not known")
}
- fun TypeAliasDescriptor.build(): DocumentationNode {
+ fun PackageFragmentDescriptor.buildExternal(): DocumentationNode {
+ val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package)
+
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this)
+ if (externalLink != null) {
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) {
+ is FunctionDescriptor -> build(true)
+ is PropertyDescriptor -> build(true)
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+
+ fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) {
+ is ClassDescriptor -> build(external)
+ is TypeAliasDescriptor -> build(external)
+ is TypeParameterDescriptor -> build()
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+ fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode {
val node = nodeForDescriptor(this, NodeKind.TypeAlias)
- node.appendAnnotations(this)
+ if (!external) {
+ node.appendAnnotations(this)
+ }
node.appendModifiers(this)
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
-
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
register(this, node)
return node
}
- fun ClassDescriptor.build(): DocumentationNode {
+ fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
val kind = when {
kind == ClassKind.OBJECT -> NodeKind.Object
kind == ClassKind.INTERFACE -> NodeKind.Interface
@@ -530,21 +596,25 @@ class DocumentationBuilder
isSubclassOfThrowable() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForDescriptor(this, kind)
- typeConstructor.supertypes.forEach {
- node.appendSupertype(this, it)
+ val node = nodeForDescriptor(this, kind, external)
+ register(this, node)
+ supertypesWithAnyPrecise().forEach {
+ node.appendSupertype(this, it, !external)
}
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
}
- for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
- node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ if (!external) {
+ for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+ node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ }
+ node.appendAnnotations(this)
}
- node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- register(this, node)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
return node
}
@@ -570,7 +640,7 @@ class DocumentationBuilder
.mapTo(result) { ClassMember(it, extraModifier = "static") }
val companionObjectDescriptor = companionObjectDescriptor
- if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(options)) {
+ if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(passConfiguration)) {
val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors()
val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() }
descriptorsToDocument.mapTo(result) {
@@ -611,12 +681,12 @@ class DocumentationBuilder
return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
}
- fun FunctionDescriptor.build(): DocumentationNode {
+ fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
if (ErrorUtils.containsErrorType(this)) {
logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
}
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function)
+ val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
@@ -624,8 +694,12 @@ class DocumentationBuilder
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ } else {
+ node.appendExternalLink(this)
+ }
overriddenDescriptors.forEach {
addOverrideLink(it, this)
@@ -646,32 +720,53 @@ class DocumentationBuilder
}
}
- fun PropertyDescriptor.build(): DocumentationNode {
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property)
+ fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+ val node = nodeForDescriptor(
+ this,
+ if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+ external
+ )
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- if (isVar) {
- node.appendTextNode("var", NodeKind.Modifier)
- }
- getter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ if (!external) {
+ node.appendSourceLink(source)
+ if (isVar) {
+ node.appendTextNode("var", NodeKind.Modifier)
}
- }
- setter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+
+ if (isConst) {
+ val psi = sourcePsi()
+ val valueText = when (psi) {
+ is KtVariableDeclaration -> psi.initializer?.text
+ is PsiField -> psi.initializer?.text
+ else -> null
+ }
+ valueText?.let { node.appendTextNode(it, NodeKind.Value) }
+ }
+
+
+ getter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ }
+ }
+ setter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+ }
}
+ node.appendDefaultPlatforms(this)
+ }
+ if (external) {
+ node.appendExternalLink(this)
}
overriddenDescriptors.forEach {
addOverrideLink(it, this)
}
- node.appendDefaultPlatforms(this)
register(this, node)
return node
@@ -779,21 +874,55 @@ class DocumentationBuilder
"\"" + StringUtil.escapeStringCharacters(value) + "\""
is EnumEntrySyntheticClassDescriptor ->
value.containingDeclaration.name.asString() + "." + value.name.asString()
+ is Pair<*, *> -> {
+ val (classId, name) = value
+ if (classId is ClassId && name is Name) {
+ classId.shortClassName.asString() + "." + name.asString()
+ } else {
+ value.toString()
+ }
+ }
else -> value.toString()
}.let { valueString ->
DocumentationNode(valueString, Content.Empty, NodeKind.Value)
}
}
-}
-val visibleToDocumentation = setOf(Visibilities.PROTECTED, Visibilities.PUBLIC)
-fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean {
- return (options.effectivePackageOptions(fqNameSafe).includeNonPublic
+ fun DocumentationNode.getParentForPackageMember(
+ descriptor: DeclarationDescriptor,
+ externalClassNodes: MutableMap<FqName, DocumentationNode>,
+ allFqNames: Collection<FqName>,
+ packageName: FqName
+ ): DocumentationNode {
+ if (descriptor is CallableMemberDescriptor) {
+ val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
+ if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) &&
+ !ErrorUtils.isError(extensionClassDescriptor)) {
+ val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor)
+ return externalClassNodes.getOrPut(fqName) {
+ val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass)
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(extensionClassDescriptor)
+ if (externalLink != null) {
+ newNode.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ append(newNode, RefKind.Member)
+ refGraph.register("${packageName.asString()}:${extensionClassDescriptor.signature()}", newNode)
+ newNode
+ }
+ }
+ }
+ return this
+ }
+
+}
+
+fun DeclarationDescriptor.isDocumented(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean {
+ return (passConfiguration.effectivePackageOptions(fqNameSafe).includeNonPublic
|| this !is MemberDescriptor
- || this.visibility in visibleToDocumentation)
- && !isDocumentationSuppressed(options)
- && (!options.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
+ || this.visibility.isPublicAPI)
+ && !isDocumentationSuppressed(passConfiguration)
+ && (!passConfiguration.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
}
private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION
@@ -807,8 +936,13 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
declarations.forEach { descriptor ->
with(documentationBuilder) {
- if (descriptor.isDocumented(options)) {
- val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames)
+ if (descriptor.isDocumented(passConfiguration)) {
+ val parent = packageNode.getParentForPackageMember(
+ descriptor,
+ externalClassNodes,
+ allFqNames,
+ packageName
+ )
parent.appendOrUpdateMember(descriptor)
}
}
@@ -819,20 +953,15 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
class KotlinJavaDocumentationBuilder
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val documentationBuilder: DocumentationBuilder,
- val options: DocumentationOptions,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
val logger: DokkaLogger) : JavaDocumentationBuilder {
override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) {
val classDescriptors = file.classes.map {
- val javaDescriptorResolver = resolutionFacade.getFrontendService(JavaDescriptorResolver::class.java)
-
- javaDescriptorResolver.resolveClass(JavaClassImpl(it)) ?: run {
- logger.warn("Cannot find descriptor for Java class ${it.qualifiedName}")
- null
- }
+ it.getJavaClassDescriptor(resolutionFacade)
}
- if (classDescriptors.any { it != null && it.isDocumented(options) }) {
- val packageNode = module.findOrCreatePackageNode(file.packageName, packageContent, documentationBuilder.refGraph)
+ if (classDescriptors.any { it != null && it.isDocumented(passConfiguration) }) {
+ val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph)
for (descriptor in classDescriptors.filterNotNull()) {
with(documentationBuilder) {
@@ -861,13 +990,13 @@ fun AnnotationDescriptor.mustBeDocumented(): Boolean {
return annotationClass.isDocumentedAnnotation()
}
-fun DeclarationDescriptor.isDocumentationSuppressed(options: DocumentationOptions): Boolean {
+fun DeclarationDescriptor.isDocumentationSuppressed(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean {
- if (options.effectivePackageOptions(fqNameSafe).suppress) return true
+ if (passConfiguration.effectivePackageOptions(fqNameSafe).suppress) return true
val path = this.findPsi()?.containingFile?.virtualFile?.path
if (path != null) {
- if (File(path).absoluteFile in options.suppressedFiles) return true
+ if (path in passConfiguration.suppressedFiles) return true
}
val doc = findKDoc()
@@ -877,30 +1006,12 @@ fun DeclarationDescriptor.isDocumentationSuppressed(options: DocumentationOption
}
fun DeclarationDescriptor.sourcePsi() =
- ((original as DeclarationDescriptorWithSource).source as? PsiSourceElement)?.psi
+ ((original as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any {
DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated"
} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated())
-fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
- externalClassNodes: MutableMap<FqName, DocumentationNode>,
- allFqNames: Collection<FqName>): DocumentationNode {
- if (descriptor is CallableMemberDescriptor) {
- val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
- if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) &&
- !ErrorUtils.isError(extensionClassDescriptor)) {
- val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor)
- return externalClassNodes.getOrPut(fqName, {
- val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass)
- append(newNode, RefKind.Member)
- newNode
- })
- }
- }
- return this
-}
-
fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
val extensionReceiver = extensionReceiverParameter
if (extensionReceiver != null) {
@@ -923,7 +1034,7 @@ fun DeclarationDescriptor.signature(): String {
is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString()
is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature()
- is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature()
+ is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature() + ":" + returnType?.signature()
is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name
is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name
is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name
@@ -984,8 +1095,8 @@ fun DeclarationDescriptor.sourceLocation(): String? {
return null
}
-fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
- if (options.generateIndexPages) {
+fun DocumentationModule.prepareForGeneration(configuration: DokkaConfiguration) {
+ if (configuration.generateIndexPages) {
generateAllTypesNode()
}
nodeRefGraph.resolveReferences()
@@ -993,8 +1104,10 @@ fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) {
fun DocumentationNode.generateAllTypesNode() {
val allTypes = members(NodeKind.Package)
- .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } }
- .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name }
+ .flatMap { it.members.filter {
+ it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass
+ || (it.kind == NodeKind.GroupNode && it.origins.all { it.kind in NodeKind.classLike }) } }
+ .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() }
val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes)
for (typeNode in allTypes) {
@@ -1003,3 +1116,18 @@ fun DocumentationNode.generateAllTypesNode() {
append(allTypesNode, RefKind.Member)
}
+
+fun ClassDescriptor.supertypesWithAnyPrecise(): Collection<KotlinType> {
+ if (KotlinBuiltIns.isAny(this)) {
+ return emptyList()
+ }
+ return typeConstructor.supertypesWithAny()
+}
+
+fun PassConfiguration.effectivePackageOptions(pack: String): DokkaConfiguration.PackageOptions {
+ val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated)
+ return perPackageOptions.firstOrNull { pack == it.prefix || pack.startsWith(it.prefix + ".") } ?: rootPackageOptions
+}
+
+fun PassConfiguration.effectivePackageOptions(pack: FqName): DokkaConfiguration.PackageOptions = effectivePackageOptions(pack.asString())
+
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
index 108cee78..9d986aee 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -2,13 +2,16 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.Singleton
+import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.util.io.*
+import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor
+import org.jetbrains.dokka.Formats.FormatDescriptor
+import org.jetbrains.dokka.Utilities.ServiceLocator
+import org.jetbrains.dokka.Utilities.lookup
import org.jetbrains.kotlin.descriptors.*
-import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
-import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
+import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
+import org.jetbrains.kotlin.load.java.descriptors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -19,26 +22,48 @@ import java.net.HttpURLConnection
import java.net.URL
import java.net.URLConnection
import java.nio.file.Path
+import java.nio.file.Paths
import java.security.MessageDigest
+import javax.inject.Named
+import kotlin.reflect.full.findAnnotation
fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) }
+typealias PackageFqNameToLocation = MutableMap<FqName, PackageListProvider.ExternalDocumentationRoot>
+
@Singleton
-class ExternalDocumentationLinkResolver @Inject constructor(
- val options: DocumentationOptions,
- val logger: DokkaLogger
+class PackageListProvider @Inject constructor(
+ val configuration: DokkaConfiguration,
+ val logger: DokkaLogger
) {
+ val storage = mutableMapOf<DokkaConfiguration.ExternalDocumentationLink, PackageFqNameToLocation>()
- val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>()
- val formats = mutableMapOf<String, InboundExternalLinkResolutionService>()
+ val cacheDir: Path? = when {
+ configuration.cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka")
+ configuration.cacheRoot != null -> Paths.get(configuration.cacheRoot)
+ else -> null
+ }?.resolve("packageListCache")?.apply { createDirectories() }
- class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) {
- override fun toString(): String = rootUrl.toString()
+ val cachedProtocols = setOf("http", "https", "ftp")
+
+ init {
+ for (conf in configuration.passesConfigurations) {
+ for (link in conf.externalDocumentationLinks) {
+ if (link in storage) {
+ continue
+ }
+
+ try {
+ loadPackageList(link)
+ } catch (e: Exception) {
+ throw RuntimeException("Exception while loading package-list from $link", e)
+ }
+ }
+
+ }
}
- val cacheDir: Path? = options.cacheRoot?.resolve("packageListCache")?.apply { createDirectories() }
- val cachedProtocols = setOf("http", "https", "ftp")
fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
val connection = this.openConnection()
@@ -114,48 +139,99 @@ class ExternalDocumentationLinkResolver @Inject constructor(
val (params, packages) =
packageListStream
- .bufferedReader()
- .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } }
+ .bufferedReader()
+ .useLines { lines -> lines.partition { it.startsWith(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX) } }
val paramsMap = params.asSequence()
- .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) }
- .groupBy({ (key, _) -> key }, { (_, value) -> value })
+ .map { it.removePrefix(ExternalDocumentationLinkResolver.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()
+ .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 $packageListUrl")
- InboundExternalLinkResolutionService.Dokka(linkExtension)
- }
+
+ val defaultResolverDesc = ExternalDocumentationLinkResolver.services["dokka-default"]!!
+ val resolverDesc = ExternalDocumentationLinkResolver.services[format]
+ ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver }
+ ?: defaultResolverDesc.also {
+ logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default")
+ }
+
+
+ val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin
+
+ val constructors = resolverClass.constructors
+
+ val constructor = constructors.singleOrNull()
+ ?: constructors.first { it.findAnnotation<Inject>() != null }
+ val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService
val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations)
- packages.map { FqName(it) }.forEach { packageFqNameToLocation[it] = rootInfo }
+ val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>()
+ storage[link] = packageFqNameToLocation
+
+ val fqNames = packages.map { FqName(it) }
+ for(name in fqNames) {
+ packageFqNameToLocation[name] = rootInfo
+ }
}
+
+
+ class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) {
+ override fun toString(): String = rootUrl.toString()
+ }
+
+ companion object {
+ private val formatsWithDefaultResolver =
+ ServiceLocator
+ .allServices("format")
+ .filter {
+ val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor
+ desc?.generatorServiceClass == FileGenerator::class
+ }.map { it.name }
+ .toSet()
+
+ }
+
+}
+
+class ExternalDocumentationLinkResolver @Inject constructor(
+ val configuration: DokkaConfiguration,
+ val passConfiguration: DokkaConfiguration.PassConfiguration,
+ @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade,
+ val logger: DokkaLogger,
+ val packageListProvider: PackageListProvider
+) {
+
+ val formats = mutableMapOf<String, InboundExternalLinkResolutionService>()
+ val packageFqNameToLocation = mutableMapOf<FqName, PackageListProvider.ExternalDocumentationRoot>()
+
init {
- options.externalDocumentationLinks.forEach {
- try {
- loadPackageList(it)
- } catch (e: Exception) {
- throw RuntimeException("Exception while loading package-list from $it", e)
- }
+ val fqNameToLocationMaps = passConfiguration.externalDocumentationLinks
+ .mapNotNull { packageListProvider.storage[it] }
+
+ for(map in fqNameToLocationMaps) {
+ packageFqNameToLocation.putAll(map)
+ }
+ }
+
+ fun buildExternalDocumentationLink(element: PsiElement): String? {
+ return element.extractDescriptor(libraryResolutionFacade)?.let {
+ buildExternalDocumentationLink(it)
}
}
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
+ is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
else -> return null
}
@@ -169,6 +245,7 @@ class ExternalDocumentationLinkResolver @Inject constructor(
companion object {
const val DOKKA_PARAM_PREFIX = "\$dokka."
+ val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name }
}
}
@@ -176,15 +253,17 @@ class ExternalDocumentationLinkResolver @Inject constructor(
interface InboundExternalLinkResolutionService {
fun getPath(symbol: DeclarationDescriptor): String?
- class Javadoc : InboundExternalLinkResolutionService {
+ class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
override fun getPath(symbol: DeclarationDescriptor): String? {
- if (symbol is JavaClassDescriptor) {
+ if (symbol is EnumEntrySyntheticClassDescriptor) {
+ return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() }
+ } else if (symbol is JavaClassDescriptor) {
return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html"
} else if (symbol is JavaCallableMemberDescriptor) {
val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null
val containingClassLink = getPath(containingClass)
if (containingClassLink != null) {
- if (symbol is JavaMethodDescriptor) {
+ if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) {
val psi = symbol.sourcePsi() as? PsiMethod
if (psi != null) {
val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
@@ -200,7 +279,9 @@ interface InboundExternalLinkResolutionService {
}
}
- class Dokka(val extension: String) : InboundExternalLinkResolutionService {
+ class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
+ val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver")
+
override fun getPath(symbol: DeclarationDescriptor): String? {
val leafElement = when (symbol) {
is CallableDescriptor, is TypeAliasDescriptor -> true
@@ -211,13 +292,11 @@ interface InboundExternalLinkResolutionService {
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())
+ private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String {
+ return when {
+ symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString())
+ symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString())
+ else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString())
}
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt
deleted file mode 100644
index a3be658e..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.getSignature
-import org.jetbrains.dokka.sourcePsi
-import org.jetbrains.kotlin.asJava.toLightElements
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.psi.KtElement
-
-class KotlinAsJavaDescriptorSignatureProvider : DescriptorSignatureProvider {
- override fun signature(forDesc: DeclarationDescriptor): String {
- val sourcePsi = forDesc.sourcePsi()
- val javaLikePsi = if (sourcePsi is KtElement) {
- sourcePsi.toLightElements().firstOrNull()
- } else {
- sourcePsi
- }
-
- return getSignature(javaLikePsi) ?:
- "not implemented for $forDesc with psi: $sourcePsi"
- }
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
index c7ed8292..be6dd2e1 100644
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
@@ -28,7 +28,7 @@ class KotlinAsJavaDocumentationBuilder
return
}
- val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.options,
+ val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.passConfiguration,
documentationBuilder.refGraph,
kotlinAsJavaDocumentationParser)
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
new file mode 100644
index 00000000..20ea179e
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
@@ -0,0 +1,25 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.asJava.toLightElements
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.psi.KtElement
+
+class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider {
+
+ private fun PsiElement.javaLikePsi() = when {
+ this is KtElement -> toLightElements().firstOrNull()
+ else -> this
+ }
+
+ override fun signature(forPsi: PsiElement): String {
+ return getSignature(forPsi.javaLikePsi()) ?:
+ "not implemented for $forPsi"
+ }
+
+ override fun signature(forDesc: DeclarationDescriptor): String {
+ val sourcePsi = forDesc.sourcePsi()
+ return getSignature(sourcePsi?.javaLikePsi()) ?:
+ "not implemented for $forDesc with psi: $sourcePsi"
+ }
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt
deleted file mode 100644
index 7ecd0389..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinDescriptorSignatureProvider.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import org.jetbrains.dokka.Model.DescriptorSignatureProvider
-import org.jetbrains.dokka.signature
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-
-class KotlinDescriptorSignatureProvider : DescriptorSignatureProvider {
- override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature()
-} \ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
new file mode 100644
index 00000000..bcac0182
--- /dev/null
+++ b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
@@ -0,0 +1,34 @@
+package org.jetbrains.dokka
+
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMember
+import com.intellij.psi.PsiPackage
+import org.jetbrains.kotlin.asJava.elements.KtLightElement
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.resolve.BindingContext
+import javax.inject.Inject
+
+class KotlinElementSignatureProvider @Inject constructor(
+ val resolutionFacade: DokkaResolutionFacade
+) : ElementSignatureProvider {
+ override fun signature(forPsi: PsiElement): String {
+ return forPsi.extractDescriptor(resolutionFacade)
+ ?.let { signature(it) }
+ ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" }
+ }
+
+ override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature()
+}
+
+
+fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? {
+ val forPsi = this
+
+ return when (forPsi) {
+ is KtLightElement<*, *> -> return (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade)
+ is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName))
+ is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade)
+ else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi]
+ }
+}
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index f33c8c96..5b565464 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -5,8 +5,13 @@ import org.jetbrains.dokka.LanguageService.RenderMode
/**
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
-class KotlinLanguageService : LanguageService {
- private val fullOnlyModifiers = setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
+class KotlinLanguageService : CommonLanguageService() {
+ override fun showModifierInSummary(node: DocumentationNode): Boolean {
+ return node.name !in fullOnlyModifiers
+ }
+
+ private val fullOnlyModifiers =
+ setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
return content {
@@ -17,11 +22,12 @@ class KotlinLanguageService : LanguageService {
NodeKind.EnumItem,
NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name)
+ NodeKind.Parameter -> renderParameter(node, renderMode)
NodeKind.TypeParameter -> renderTypeParameter(node, renderMode)
NodeKind.Type,
NodeKind.UpperBound -> renderType(node, renderMode)
- NodeKind.Modifier -> renderModifier(node)
+ NodeKind.Modifier -> renderModifier(this, node, renderMode)
NodeKind.Constructor,
NodeKind.Function,
NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode)
@@ -32,12 +38,6 @@ class KotlinLanguageService : LanguageService {
}
}
- override fun renderName(node: DocumentationNode): String {
- return when (node.kind) {
- NodeKind.Constructor -> node.owner!!.name
- else -> node.name
- }
- }
override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
if (nodes.size < 2) return null
@@ -46,10 +46,17 @@ class KotlinLanguageService : LanguageService {
return content {
val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first()
if (functionWithTypeParameter.kind == NodeKind.Function) {
- renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
- }
- else {
- renderProperty(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ renderFunction(
+ functionWithTypeParameter,
+ RenderMode.SUMMARY,
+ SummarizingMapper(receiverKind, typeParameter.name)
+ )
+ } else {
+ renderProperty(
+ functionWithTypeParameter,
+ RenderMode.SUMMARY,
+ SummarizingMapper(receiverKind, typeParameter.name)
+ )
}
}
}
@@ -70,26 +77,27 @@ class KotlinLanguageService : LanguageService {
companion object {
private val arrayClasses = setOf(
- "kotlin.Array",
- "kotlin.BooleanArray",
- "kotlin.ByteArray",
- "kotlin.CharArray",
- "kotlin.ShortArray",
- "kotlin.IntArray",
- "kotlin.LongArray",
- "kotlin.FloatArray",
- "kotlin.DoubleArray"
+ "kotlin.Array",
+ "kotlin.BooleanArray",
+ "kotlin.ByteArray",
+ "kotlin.CharArray",
+ "kotlin.ShortArray",
+ "kotlin.IntArray",
+ "kotlin.LongArray",
+ "kotlin.FloatArray",
+ "kotlin.DoubleArray"
)
private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses
private val iterableClasses = setOf(
- "kotlin.Collection",
- "kotlin.Sequence",
- "kotlin.Iterable",
- "kotlin.Map",
- "kotlin.String",
- "kotlin.CharSequence") + arrayOrListClasses
+ "kotlin.Collection",
+ "kotlin.Sequence",
+ "kotlin.Iterable",
+ "kotlin.Map",
+ "kotlin.String",
+ "kotlin.CharSequence"
+ ) + arrayOrListClasses
}
private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
@@ -102,53 +110,21 @@ class KotlinLanguageService : LanguageService {
fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
}
- private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String): SignatureMapper {
+ private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper {
override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
to.text("<$typeParameterName>")
}
}
- private fun ContentBlock.renderPackage(node: DocumentationNode) {
- keyword("package")
- text(" ")
- identifier(node.name)
- }
-
- private fun <T> ContentBlock.renderList(nodes: List<T>, separator: String = ", ",
- noWrap: Boolean = false, renderItem: (T) -> Unit) {
- if (nodes.none())
- return
- renderItem(nodes.first())
- nodes.drop(1).forEach {
- if (noWrap) {
- symbol(separator.removeSuffix(" "))
- nbsp()
- } else {
- symbol(separator)
- }
- renderItem(it)
- }
- }
-
- private fun ContentBlock.renderLinked(node: DocumentationNode, body: ContentBlock.(DocumentationNode)->Unit) {
- val to = node.links.firstOrNull()
- if (to == null)
- body(node)
- else
- link(to) {
- body(node)
- }
- }
-
private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) {
node.references(RefKind.HiddenAnnotation).map { it.to }
- .find { it.name == "ParameterName" }?.let {
- val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
- identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
- symbol(":")
- nbsp()
- }
+ .find { it.name == "ParameterName" }?.let {
+ val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
+ identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
+ symbol(":")
+ nbsp()
+ }
}
private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
@@ -198,7 +174,9 @@ class KotlinLanguageService : LanguageService {
renderAnnotationsForNode(node)
}
renderModifiersForNode(node, renderMode, true)
- renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) }
+ renderLinked(this, node) {
+ identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName)
+ }
val typeArguments = node.details(NodeKind.Type)
if (typeArguments.isNotEmpty()) {
symbol("<")
@@ -213,16 +191,18 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) {
+ override fun renderModifier(
+ block: ContentBlock,
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ nowrap: Boolean
+ ) {
when (node.name) {
- "final", "public", "var" -> {}
+ "final", "public", "var", "expect", "actual" -> {
+ }
else -> {
- keyword(node.name)
- if (nowrap) {
- nbsp()
- }
- else {
- text(" ")
+ if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) {
+ super.renderModifier(block, node, renderMode, nowrap)
}
}
}
@@ -238,11 +218,12 @@ class KotlinLanguageService : LanguageService {
nbsp()
symbol(":")
nbsp()
- renderList(constraints, noWrap=true) {
+ renderList(constraints, noWrap = true) {
renderType(it, renderMode)
}
}
}
+
private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
@@ -274,9 +255,12 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) {
- val parametersWithMultipleConstraints = node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
+ val parametersWithMultipleConstraints =
+ node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
val parametersWithConstraints = parametersWithMultipleConstraints
- .flatMap { parameter -> parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint } }
+ .flatMap { parameter ->
+ parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint }
+ }
if (parametersWithMultipleConstraints.isNotEmpty()) {
keyword(" where ")
renderList(parametersWithConstraints) {
@@ -290,7 +274,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
- val supertypes = node.details(NodeKind.Supertype)
+ val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
if (supertypes.any()) {
nbsp()
symbol(":")
@@ -302,20 +286,6 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifiersForNode(node: DocumentationNode,
- renderMode: RenderMode,
- nowrap: Boolean = false) {
- val modifiers = node.details(NodeKind.Modifier)
- for (it in modifiers) {
- if (node.kind == org.jetbrains.dokka.NodeKind.Interface && it.name == "abstract")
- continue
- if (renderMode == RenderMode.SUMMARY && it.name in fullOnlyModifiers) {
- continue
- }
- renderModifier(it, nowrap)
- }
- }
-
private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) {
node.annotations.forEach {
renderAnnotation(it)
@@ -365,9 +335,11 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderFunction(node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null) {
+ private fun ContentBlock.renderFunction(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -385,7 +357,7 @@ class KotlinLanguageService : LanguageService {
renderReceiver(node, renderMode, signatureMapper)
- if (node.kind != org.jetbrains.dokka.NodeKind.Constructor)
+ if (node.kind != NodeKind.Constructor)
identifierOrDeprecated(node)
symbol("(")
@@ -401,14 +373,17 @@ class KotlinLanguageService : LanguageService {
symbol(")")
symbol(": ")
renderType(node.detail(NodeKind.Type), renderMode)
- }
- else {
+ } else {
symbol(")")
}
renderExtraTypeParameterConstraints(node, renderMode)
}
- private fun ContentBlock.renderReceiver(node: DocumentationNode, renderMode: RenderMode, signatureMapper: SignatureMapper?) {
+ private fun ContentBlock.renderReceiver(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper?
+ ) {
val receiver = node.details(NodeKind.Receiver).singleOrNull()
if (receiver != null) {
if (signatureMapper != null) {
@@ -428,17 +403,19 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun needReturnType(node: DocumentationNode) = when(node.kind) {
+ private fun needReturnType(node: DocumentationNode) = when (node.kind) {
NodeKind.Constructor -> false
else -> !node.isUnitReturnType()
}
fun DocumentationNode.isUnitReturnType(): Boolean =
- detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
+ detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
- private fun ContentBlock.renderProperty(node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null) {
+ private fun ContentBlock.renderProperty(
+ node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null
+ ) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -462,7 +439,7 @@ class KotlinLanguageService : LanguageService {
}
fun DocumentationNode.getPropertyKeyword() =
- if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
+ if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) {
if (node.deprecation != null) {
@@ -475,4 +452,12 @@ class KotlinLanguageService : LanguageService {
}
}
-fun DocumentationNode.qualifiedNameFromType() = (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() ?: name
+fun DocumentationNode.qualifiedNameFromType(): String {
+ return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name
+ ?: (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName()
+ ?: name
+}
+
+
+val DocumentationNode.typeDeclarationClass
+ get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType)