aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/Kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/kotlin/Kotlin')
-rw-r--r--core/src/main/kotlin/Kotlin/ContentBuilder.kt188
-rw-r--r--core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt73
-rw-r--r--core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt177
-rw-r--r--core/src/main/kotlin/Kotlin/DocumentationBuilder.kt1166
-rw-r--r--core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt305
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt68
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt25
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt33
-rw-r--r--core/src/main/kotlin/Kotlin/KotlinLanguageService.kt473
9 files changed, 0 insertions, 2508 deletions
diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt
deleted file mode 100644
index 573b41b6..00000000
--- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-package org.jetbrains.dokka
-
-import org.intellij.markdown.MarkdownElementTypes
-import org.intellij.markdown.MarkdownTokenTypes
-import org.intellij.markdown.html.entities.EntityConverter
-import org.intellij.markdown.parser.LinkMap
-import java.util.*
-
-class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) {
- fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel)
- fun resolve(href: String): ContentBlock = contentFactory(href)
-}
-
-fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent {
- val result = MutableContent()
- if (inline) {
- buildInlineContentTo(tree, result, linkResolver)
- } else {
- buildContentTo(tree, result, linkResolver)
- }
- return result
-}
-
-fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
-// println(tree.toTestString())
- val nodeStack = ArrayDeque<ContentBlock>()
- nodeStack.push(target)
-
- tree.visit { node, processChildren ->
- val parent = nodeStack.peek()
-
- fun appendNodeWithChildren(content: ContentBlock) {
- nodeStack.push(content)
- processChildren()
- parent.append(nodeStack.pop())
- }
-
- when (node.type) {
- MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1))
- MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2))
- MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3))
- MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4))
- MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5))
- MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6))
- MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList())
- MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList())
- MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem())
- MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis())
- MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong())
- MarkdownElementTypes.CODE_SPAN -> {
- val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text
- if (startDelimiter != null) {
- val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter)
- val codeSpan = ContentCode().apply { append(ContentText(text)) }
- parent.append(codeSpan)
- }
- }
- MarkdownElementTypes.CODE_BLOCK,
- MarkdownElementTypes.CODE_FENCE -> {
- val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: ""
- appendNodeWithChildren(ContentBlockCode(language))
- }
- MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph())
-
- MarkdownElementTypes.INLINE_LINK -> {
- val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT)
- val destination = node.child(MarkdownElementTypes.LINK_DESTINATION)
- if (linkTextNode != null) {
- if (destination != null) {
- val link = ContentExternalLink(destination.text)
- renderLinkTextTo(linkTextNode, link, linkResolver)
- parent.append(link)
- } else {
- val link = ContentExternalLink(linkTextNode.getLabelText())
- renderLinkTextTo(linkTextNode, link, linkResolver)
- parent.append(link)
- }
- }
- }
- MarkdownElementTypes.SHORT_REFERENCE_LINK,
- MarkdownElementTypes.FULL_REFERENCE_LINK -> {
- val labelElement = node.child(MarkdownElementTypes.LINK_LABEL)
- if (labelElement != null) {
- val linkInfo = linkResolver.getLinkInfo(labelElement.text)
- val labelText = labelElement.getLabelText()
- val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText)
- val linkText = node.child(MarkdownElementTypes.LINK_TEXT)
- if (linkText != null) {
- renderLinkTextTo(linkText, link, linkResolver)
- } else {
- link.append(ContentText(labelText))
- }
- parent.append(link)
- }
- }
- MarkdownTokenTypes.WHITE_SPACE -> {
- // Don't append first space if start of header (it is added during formatting later)
- // v
- // #### Some Heading
- if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) {
- parent.append(ContentText(node.text))
- }
- }
- MarkdownTokenTypes.EOL -> {
- if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) ||
- // Keep extra blank lines when processing lists (affects Markdown formatting)
- (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) {
- parent.append(ContentText(node.text))
- }
- }
-
- MarkdownTokenTypes.CODE_LINE -> {
- val content = ContentText(node.text)
- if (parent is ContentBlockCode) {
- parent.append(content)
- } else {
- parent.append(ContentBlockCode().apply { append(content) })
- }
- }
-
- MarkdownTokenTypes.TEXT -> {
- fun createEntityOrText(text: String): ContentNode {
- if (text == "&amp;" || text == "&quot;" || text == "&lt;" || text == "&gt;") {
- return ContentEntity(text)
- }
- if (text == "&") {
- return ContentEntity("&amp;")
- }
- val decodedText = EntityConverter.replaceEntities(text, true, true)
- if (decodedText != text) {
- return ContentEntity(text)
- }
- return ContentText(text)
- }
-
- parent.append(createEntityOrText(node.text))
- }
-
- MarkdownTokenTypes.EMPH -> {
- val parentNodeType = node.parent?.type
- if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) {
- parent.append(ContentText(node.text))
- }
- }
-
- MarkdownTokenTypes.COLON,
- MarkdownTokenTypes.SINGLE_QUOTE,
- MarkdownTokenTypes.DOUBLE_QUOTE,
- MarkdownTokenTypes.LT,
- MarkdownTokenTypes.GT,
- MarkdownTokenTypes.LPAREN,
- MarkdownTokenTypes.RPAREN,
- MarkdownTokenTypes.LBRACKET,
- MarkdownTokenTypes.RBRACKET,
- MarkdownTokenTypes.EXCLAMATION_MARK,
- MarkdownTokenTypes.BACKTICK,
- MarkdownTokenTypes.CODE_FENCE_CONTENT -> {
- parent.append(ContentText(node.text))
- }
-
- MarkdownElementTypes.LINK_DEFINITION -> {
- }
-
- else -> {
- processChildren()
- }
- }
- }
-}
-
-private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH || it.type == MarkdownTokenTypes.COLON }.joinToString("") { it.text }
-
-private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode
-private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList
-
-fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
- val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree)
- inlineContent.forEach {
- buildContentTo(it, target, linkResolver)
- }
-}
-
-fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) {
- val linkTextNodes = tree.children.drop(1).dropLast(1)
- linkTextNodes.forEach {
- buildContentTo(it, target, linkResolver)
- }
-}
diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
deleted file mode 100644
index 88494581..00000000
--- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.jetbrains.dokka
-
-import com.google.inject.Inject
-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
-
-class DeclarationLinkResolver
- @Inject constructor(val resolutionFacade: DokkaResolutionFacade,
- val refGraph: NodeReferenceGraph,
- val logger: DokkaLogger,
- val passConfiguration: DokkaConfiguration.PassConfiguration,
- val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver,
- val elementSignatureProvider: ElementSignatureProvider) {
-
-
- fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? {
- val symbol = try {
- val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext,
- resolutionFacade, fromDescriptor, null, href.split('.').toList())
- findTargetSymbol(symbols)
- } catch(e: Exception) {
- null
- }
-
- // 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 externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol)
- if (externalHref != null) {
- return ContentExternalLink(externalHref)
- }
- 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. " +
- "This is probably caused by invalid configuration of cross-module dependencies")
- }
- target
- }
- }
- if ("/" in href) {
- return ContentExternalLink(href)
- }
- return null
- }
-
- fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) =
- tryResolveContentLink(fromDescriptor, href) ?: run {
- logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}")
- ContentExternalLink("#")
- }
-
- fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? {
- if (symbols.isEmpty()) {
- return null
- }
- val symbol = symbols.first()
- if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
- return symbol.overriddenDescriptors.firstOrNull()
- }
- 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
deleted file mode 100644
index ce20aeec..00000000
--- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-package org.jetbrains.dokka.Kotlin
-
-import com.google.inject.Inject
-import com.intellij.psi.PsiDocCommentOwner
-import com.intellij.psi.PsiNamedElement
-import com.intellij.psi.util.PsiTreeUtil
-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.psi.KtElement
-import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
-import org.jetbrains.kotlin.resolve.DescriptorUtils
-import org.jetbrains.kotlin.resolve.annotations.argumentValue
-import org.jetbrains.kotlin.resolve.constants.StringValue
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
-import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
-import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
-import org.jetbrains.kotlin.resolve.source.PsiSourceElement
-
-class DescriptorDocumentationParser
- @Inject constructor(val options: DokkaConfiguration.PassConfiguration,
- val logger: DokkaLogger,
- val linkResolver: DeclarationLinkResolver,
- val resolutionFacade: DokkaResolutionFacade,
- val refGraph: NodeReferenceGraph,
- val sampleService: SampleProcessingService,
- val signatureProvider: KotlinElementSignatureProvider,
- val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver
-)
-{
- fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Content =
- parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first
-
- fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> {
- if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) {
- return parseJavadoc(descriptor)
- }
-
- val kdoc = descriptor.findKDoc() ?: findStdlibKDoc(descriptor)
- if (kdoc == null) {
- if (options.effectivePackageOptions(descriptor.fqNameSafe).reportUndocumented && !descriptor.isDeprecated() &&
- descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor &&
- descriptor !is PropertyAccessorDescriptor && !descriptor.isSuppressWarning()) {
- logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}")
- }
- 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 = if (isDefaultNoArgConstructor) {
- getConstructorTagContent(descriptor) ?: kdoc.getContent()
- } else kdoc.getContent()
-
- // workaround for code fence parsing problem in IJ markdown parser
- if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) {
- kdocText += "\n"
- }
- val tree = parseMarkdown(kdocText)
- val linkMap = LinkMap.buildLinkMap(tree.node, kdocText)
- 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(contextDescriptor, it.getSubjectName(), it))
- KDocKnownTag.SEE ->
- 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(contextDescriptor, href) })
- }
- }
- }
- }
- return content to { node -> }
- }
-
- private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? {
- return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag(
- KDocKnownTag.CONSTRUCTOR
- )?.getContent()
- }
-
-
- private fun DeclarationDescriptor.isSuppressWarning() : Boolean {
- val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!))
- return if (suppressAnnotation != null) {
- @Suppress("UNCHECKED_CAST")
- (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" }
- } else containingDeclaration?.isSuppressWarning() ?: false
- }
-
- /**
- * Special case for generating stdlib documentation (the Any class to which the override chain will resolve
- * is not the same one as the Any class included in the source scope).
- */
- fun findStdlibKDoc(descriptor: DeclarationDescriptor): KDocTag? {
- if (descriptor !is CallableMemberDescriptor) {
- return null
- }
- val name = descriptor.name.asString()
- if (name == "equals" || name == "hashCode" || name == "toString") {
- var deepestDescriptor: CallableMemberDescriptor = descriptor
- while (!deepestDescriptor.overriddenDescriptors.isEmpty()) {
- deepestDescriptor = deepestDescriptor.overriddenDescriptors.first()
- }
- if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") {
- val anyClassDescriptors = resolutionFacade.resolveSession.getTopLevelClassifierDescriptors(
- FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE)
- anyClassDescriptors.forEach {
- val anyMethod = (it as ClassDescriptor).getMemberScope(listOf())
- .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) { it == descriptor.name }
- .single()
- val kdoc = anyMethod.findKDoc()
- if (kdoc != null) {
- return kdoc
- }
- }
- }
- }
- return null
- }
-
- 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,
- signatureProvider,
- externalDocumentationLinkResolver
- ).parseDocumentation(psi as PsiNamedElement)
- return parseResult.content to { node ->
- parseResult.deprecatedContent?.let {
- val deprecationNode = DocumentationNode("", it, NodeKind.Modifier)
- node.append(deprecationNode, RefKind.Deprecation)
- }
- }
- }
- return Content.Empty to { node -> }
- }
-
- fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) ?: arrayOf()
-
- private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) {
- val subjectName = seeTag.getSubjectName()
- if (subjectName != null) {
- val seeSection = findSectionByTag("See Also") ?: addSection("See Also", null)
- val link = linkResolver.resolveContentLink(descriptor, subjectName)
- link.append(ContentText(subjectName))
- val para = ContentParagraph()
- para.append(link)
- seeSection.append(para)
- }
- }
-
-}
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
deleted file mode 100644
index 13bbbb11..00000000
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ /dev/null
@@ -1,1166 +0,0 @@
-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.PassConfiguration
-import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
-import org.jetbrains.kotlin.builtins.KotlinBuiltIns
-import org.jetbrains.kotlin.coroutines.hasFunctionOrSuspendFunctionType
-import org.jetbrains.kotlin.descriptors.*
-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.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.addRemoveModifier.MODIFIERS_ORDER
-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.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.immediateSupertypes
-import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
-import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
-import org.jetbrains.kotlin.types.typeUtil.supertypes
-import org.jetbrains.kotlin.util.supertypesWithAny
-import com.google.inject.name.Named as GuiceNamed
-
-private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor,
- extensionReceiverDescriptor: DeclarationDescriptor,
- allFqNames: Collection<FqName>): Boolean {
- val extensionFunctionPackage = DescriptorUtils.getParentOfType(extensionFunctionDescriptor, PackageFragmentDescriptor::class.java)
- val extensionReceiverPackage = DescriptorUtils.getParentOfType(extensionReceiverDescriptor, PackageFragmentDescriptor::class.java)
- return extensionFunctionPackage != null && extensionReceiverPackage != null &&
- extensionFunctionPackage.fqName != extensionReceiverPackage.fqName &&
- extensionReceiverPackage.fqName !in allFqNames
-}
-
-interface PackageDocumentationBuilder {
- fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
- packageName: FqName,
- packageNode: DocumentationNode,
- declarations: List<DeclarationDescriptor>,
- allFqNames: Collection<FqName>)
-}
-
-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 passConfiguration: DokkaConfiguration.PassConfiguration,
- val refGraph: NodeReferenceGraph,
- val platformNodeRegistry: PlatformNodeRegistry,
- val logger: DokkaLogger,
- val linkResolver: DeclarationLinkResolver,
- val defaultPlatformsProvider: DefaultPlatformsProvider) {
- val boringBuiltinClasses = setOf(
- "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean",
- "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any")
- val knownModifiers = setOf(
- KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.INTERNAL_KEYWORD, KtTokens.PRIVATE_KEYWORD,
- KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.ABSTRACT_KEYWORD, KtTokens.SEALED_KEYWORD,
- KtTokens.OVERRIDE_KEYWORD, KtTokens.INLINE_KEYWORD)
-
- fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: RefKind) {
- refGraph.link(node, descriptor.signature(), kind)
- }
-
- fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: RefKind) {
- if (fromDescriptor != null && toDescriptor != null) {
- refGraph.link(fromDescriptor.signature(), toDescriptor.signature(), kind)
- }
- }
-
- fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
- refGraph.register(descriptor.signature(), node)
- }
-
- 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)
- return node
- }
-
- private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor): DocumentationNode {
- if (descriptor is MemberDescriptor) {
- appendVisibility(descriptor)
- if (descriptor !is ConstructorDescriptor) {
- appendModality(descriptor)
- }
- }
- return this
- }
-
- fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
- var modality = descriptor.modality
- if (modality == Modality.OPEN) {
- val containingClass = descriptor.containingDeclaration as? ClassDescriptor
- if (containingClass?.modality == Modality.FINAL) {
- modality = Modality.FINAL
- }
- }
- val modifier = modality.name.toLowerCase()
- appendTextNode(modifier, NodeKind.Modifier)
- }
-
- fun DocumentationNode.appendInline(descriptor: DeclarationDescriptor, psi: KtModifierListOwner) {
- if (!psi.hasModifier(KtTokens.INLINE_KEYWORD)) return
- if (descriptor is FunctionDescriptor
- && descriptor.valueParameters.none { it.hasFunctionOrSuspendFunctionType }) return
- appendTextNode(KtTokens.INLINE_KEYWORD.value, NodeKind.Modifier)
- }
-
- fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
- val modifier = descriptor.visibility.normalize().displayName
- appendTextNode(modifier, NodeKind.Modifier)
- }
-
- fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) {
- val unwrappedType = superType.unwrap()
- if (unwrappedType is AbbreviatedType) {
- appendSupertype(descriptor, unwrappedType.abbreviation, backref)
- } else {
- appendType(unwrappedType, NodeKind.Supertype)
- val superclass = unwrappedType.constructor.declarationDescriptor
- if (backref) {
- link(superclass, descriptor, RefKind.Inheritor)
- }
- link(descriptor, superclass, RefKind.Superclass)
- }
- }
-
- fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
- if (projection.isStarProjection) {
- appendTextNode("*", NodeKind.Type)
- } else {
- appendType(projection.type, kind, projection.projectionKind.label)
- }
- }
-
- fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: NodeKind = NodeKind.Type, prefix: String = "") {
- if (kotlinType == null)
- return
- (kotlinType.unwrap() as? AbbreviatedType)?.let {
- return appendType(it.abbreviation)
- }
-
- if (kotlinType.isDynamic()) {
- append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail)
- return
- }
-
- val classifierDescriptor = kotlinType.constructor.declarationDescriptor
- val name = when (classifierDescriptor) {
- is ClassDescriptor -> {
- if (classifierDescriptor.isCompanionObject) {
- classifierDescriptor.containingDeclaration.name.asString() +
- "." + classifierDescriptor.name.asString()
- } else {
- classifierDescriptor.name.asString()
- }
- }
- is Named -> classifierDescriptor.name.asString()
- else -> "<anonymous>"
- }
- val node = DocumentationNode(name, Content.Empty, kind)
- if (prefix != "") {
- node.appendTextNode(prefix, NodeKind.Modifier)
- }
- if (kotlinType.isNullabilityFlexible()) {
- node.appendTextNode("!", NodeKind.NullabilityModifier)
- } else if (kotlinType.isMarkedNullable) {
- node.appendTextNode("?", NodeKind.NullabilityModifier)
- }
- if (classifierDescriptor != null) {
- val externalLink =
- linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
- if (externalLink != null) {
- 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
- )
- }
- 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) {
- node.appendProjection(typeArgument)
- }
- }
-
- fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean =
- DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses
-
- fun DocumentationNode.appendAnnotations(annotated: Annotated) {
- annotated.annotations.forEach {
- it.build()?.let { annotationNode ->
- if (annotationNode.isSinceKotlin()) {
- appendSinceKotlin(annotationNode)
- }
- else {
- val refKind = when {
- it.isDocumented() ->
- when {
- annotationNode.isDeprecation() -> RefKind.Deprecation
- else -> RefKind.Annotation
- }
- it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation
- else -> return@forEach
- }
- append(annotationNode, refKind)
- }
-
- }
- }
- }
-
- 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)
- .detail(NodeKind.Value)
- .name.removeSurrounding("\"")
-
- sinceKotlin = kotlinVersion
- }
-
- fun DocumentationNode.appendDefaultSinceKotlin() {
- if (sinceKotlin == null) {
- sinceKotlin = passConfiguration.sinceKotlin
- }
- }
-
- fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) {
- val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return
- KtTokens.MODIFIER_KEYWORDS_ARRAY.filter {
- it !in knownModifiers
- }.sortedBy {
- MODIFIERS_ORDER.indexOf(it)
- }.forEach {
- if (psi.hasModifier(it)) {
- appendTextNode(it.value, NodeKind.Modifier)
- }
- }
- appendInline(descriptor, psi)
- }
-
- fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) {
- for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) {
- append(platformNodeRegistry[platform], RefKind.Platform)
- }
- }
-
- fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated"
-
- fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation
-
- fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
- appendSourceLink(sourceElement.getPsi(), passConfiguration.sourceLinks)
- }
-
- fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) {
- appendTextNode(descriptor.signature(), NodeKind.Signature, RefKind.Detail)
- }
-
- fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? {
- if (!descriptor.isGenerated() && descriptor.isDocumented(passConfiguration)) {
- val node = descriptor.build()
- append(node, kind)
- return node
- }
- return null
- }
-
- fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?:
- DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply {
- appendTextNode(signature, NodeKind.Signature, RefKind.Detail)
- })
- .also { groupNode ->
- nodes.forEach { node ->
- if (node != groupNode) {
- node.owner?.let { owner ->
- node.dropReferences { it.to == owner && it.kind == RefKind.Owner }
- owner.dropReferences { it.to == node && it.kind == RefKind.Member }
- owner.append(groupNode, RefKind.Member)
- }
- groupNode.append(node, RefKind.Member)
- }
- }
- }
-
-
- fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) {
- if (descriptor.isGenerated() || !descriptor.isDocumented(passConfiguration)) return
-
- val existingNode = refGraph.lookup(descriptor.signature())
- if (existingNode != null) {
- if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor
- || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) {
- val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build()))
- register(descriptor, node)
- return
- }
-
- existingNode.updatePlatforms(descriptor)
-
- if (descriptor is ClassDescriptor) {
- val membersToDocument = descriptor.collectMembersToDocument()
- for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) {
- if (memberDescriptor is ClassDescriptor) {
- existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes
- }
- else {
- val existingMemberNode = refGraph.lookup(memberDescriptor.signature())
- if (existingMemberNode != null) {
- existingMemberNode.updatePlatforms(memberDescriptor)
- }
- else {
- existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier)
- }
- }
- }
- }
- }
- else {
- appendChild(descriptor, RefKind.Member)
- }
- }
-
- private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) {
- for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) {
- append(platformNodeRegistry[platform], RefKind.Platform)
- }
- }
-
- fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor,
- inheritedLinkKind: RefKind = RefKind.InheritedMember,
- extraModifier: String?) {
- if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
- val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull()
- if (baseDescriptor != null) {
- link(this, baseDescriptor, inheritedLinkKind)
- }
- } else {
- val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original
- val child = appendChild(descriptorToUse, RefKind.Member)
- if (extraModifier != null) {
- child?.appendTextNode("static", NodeKind.Modifier)
- }
- }
- }
-
- fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) {
- descriptors.forEach { descriptor ->
- val node = appendChild(descriptor, kind)
- node?.addReferenceTo(this, RefKind.TopLevelPage)
- }
- }
-
- fun DocumentationModule.appendFragments(fragments: Collection<PackageFragmentDescriptor>,
- packageContent: Map<String, Content>,
- packageDocumentationBuilder: PackageDocumentationBuilder) {
- val allFqNames = fragments.filter { it.isDocumented(passConfiguration) }.map { it.fqName }.distinct()
-
- for (packageName in allFqNames) {
- if (packageName.isRoot && !passConfiguration.includeRootPackage) continue
- val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() }
-
- if (passConfiguration.skipEmptyPackages && declarations.none { it.isDocumented(passConfiguration) }) continue
- logger.info(" package $packageName: ${declarations.count()} declarations")
- val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph)
- packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode,
- declarations, allFqNames)
- }
-
- }
-
- 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 classHierarchy = buildClassHierarchy(documentingClasses)
-
- val allExtensionFunctions =
- allDescriptors
- .filterIsInstance<CallableMemberDescriptor>()
- .filter { it.extensionReceiverParameter != null }
- val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
-
- fun isIgnoredReceiverType(type: KotlinType) =
- type.isDynamic() ||
- type.isAnyOrNullableAny() ||
- (type.isTypeParameter() && type.immediateSupertypes().all { it.isAnyOrNullableAny() })
-
-
- for (extensionFunction in allExtensionFunctions) {
- val extensionReceiverParameter = extensionFunction.extensionReceiverParameter!!
- if (extensionFunction.dispatchReceiverParameter != null) continue
- val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
- ?.filter { fn -> fn.canShadow(extensionFunction) }
- ?: emptyList()
-
- if (isIgnoredReceiverType(extensionReceiverParameter.type)) continue
- 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) }) {
-
- 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)
- }
- }
- }
- }
-
- private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
- 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>> {
- val result = hashMapOf<ClassDescriptor, MutableList<ClassDescriptor>>()
- classes.forEach { cls ->
- TypeUtils.getAllSupertypes(cls.defaultType).forEach { supertype ->
- val classDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor
- if (classDescriptor != null) {
- val subtypesList = result.getOrPut(classDescriptor) { arrayListOf() }
- subtypesList.add(cls)
- }
- }
- }
- return result
- }
-
- private fun CallableMemberDescriptor.canShadow(other: CallableMemberDescriptor): Boolean {
- if (this == other) return false
- if (this is PropertyDescriptor && other is PropertyDescriptor) {
- return true
- }
- if (this is FunctionDescriptor && other is FunctionDescriptor) {
- val parameters1 = valueParameters
- val parameters2 = other.valueParameters
- if (parameters1.size != parameters2.size) {
- return false
- }
- for ((p1, p2) in parameters1 zip parameters2) {
- if (p1.type != p2.type) {
- return false
- }
- }
- return true
- }
- return false
- }
-
- fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
- is ClassifierDescriptor -> build()
- is ConstructorDescriptor -> build()
- is PropertyDescriptor -> build()
- is FunctionDescriptor -> build()
- is ValueParameterDescriptor -> build()
- is ReceiverParameterDescriptor -> build()
- else -> throw IllegalStateException("Descriptor $this is not known")
- }
-
- 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)
-
- if (!external) {
- node.appendDefaultSinceKotlin()
- node.appendAnnotations(this)
- }
- node.appendModifiers(this)
- node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
-
- node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
-
- if (!external) {
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- }
- register(this, node)
- return node
- }
-
- fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
- val kind = when {
- kind == ClassKind.OBJECT -> NodeKind.Object
- kind == ClassKind.INTERFACE -> NodeKind.Interface
- kind == ClassKind.ENUM_CLASS -> NodeKind.Enum
- kind == ClassKind.ANNOTATION_CLASS -> NodeKind.AnnotationClass
- kind == ClassKind.ENUM_ENTRY -> NodeKind.EnumItem
- isSubclassOfThrowable() -> NodeKind.Exception
- else -> NodeKind.Class
- }
- 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)
- }
- if (!external) {
- for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
- node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
- }
- node.appendDefaultSinceKotlin()
- node.appendAnnotations(this)
- }
- node.appendModifiers(this)
- if (!external) {
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- }
- return node
- }
-
- data class ClassMember(val descriptor: DeclarationDescriptor,
- val inheritedLinkKind: RefKind = RefKind.InheritedMember,
- val extraModifier: String? = null)
-
- fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> {
- val result = arrayListOf<ClassMember>()
- if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
- val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS)
- constructors.filter { it.valueParameters.size > 0 }
- else
- constructors
- constructorsToDocument.mapTo(result) { ClassMember(it) }
- }
-
- defaultType.memberScope.getContributedDescriptors()
- .filter { it != companionObjectDescriptor }
- .mapTo(result) { ClassMember(it) }
-
- staticScope.getContributedDescriptors()
- .mapTo(result) { ClassMember(it, extraModifier = "static") }
-
- val companionObjectDescriptor = companionObjectDescriptor
- if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(passConfiguration)) {
- val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors()
- val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() }
- descriptorsToDocument.mapTo(result) {
- ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember)
- }
-
- if (companionObjectDescriptor.getAllSuperclassesWithoutAny().isNotEmpty()
- || companionObjectDescriptor.getSuperInterfaces().isNotEmpty()) {
- result += ClassMember(companionObjectDescriptor)
- }
- }
- return result
- }
-
- fun CallableDescriptor.isInheritedFromAny(): Boolean {
- return findTopMostOverriddenDescriptors().any {
- DescriptorUtils.getFqNameSafe(it.containingDeclaration).asString() == "kotlin.Any"
- }
- }
-
- fun ClassDescriptor.isSubclassOfThrowable(): Boolean =
- defaultType.supertypes().any { it.constructor.declarationDescriptor == builtIns.throwable }
-
- fun ConstructorDescriptor.build(): DocumentationNode {
- val node = nodeForDescriptor(this, NodeKind.Constructor)
- node.appendInPageChildren(valueParameters, RefKind.Detail)
- node.appendDefaultPlatforms(this)
- node.appendDefaultSinceKotlin()
- register(this, node)
- return node
- }
-
- private fun CallableMemberDescriptor.inCompanionObject(): Boolean {
- val containingDeclaration = containingDeclaration
- if ((containingDeclaration as? ClassDescriptor)?.isCompanionObject ?: false) {
- return true
- }
- val receiver = extensionReceiverParameter
- return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
- }
-
- fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
- if (ErrorUtils.containsErrorTypeInParameters(this) || ErrorUtils.containsErrorType(this.returnType)) {
- logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
- }
-
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
-
- node.appendInPageChildren(typeParameters, RefKind.Detail)
- extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
- node.appendInPageChildren(valueParameters, RefKind.Detail)
- node.appendType(returnType)
- if (!external) {
- node.appendDefaultSinceKotlin()
- }
- node.appendAnnotations(this)
- node.appendModifiers(this)
- if (!external) {
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- } else {
- node.appendExternalLink(this)
- }
-
- overriddenDescriptors.forEach {
- addOverrideLink(it, this)
- }
-
- register(this, node)
- return node
- }
-
- fun addOverrideLink(baseClassFunction: CallableMemberDescriptor, overridingFunction: CallableMemberDescriptor) {
- val source = baseClassFunction.original.source.getPsi()
- if (source != null) {
- link(overridingFunction, baseClassFunction, RefKind.Override)
- } else {
- baseClassFunction.overriddenDescriptors.forEach {
- addOverrideLink(it, overridingFunction)
- }
- }
- }
-
- 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)
- if (!external) {
- node.appendDefaultSinceKotlin()
- }
- node.appendAnnotations(this)
- node.appendModifiers(this)
- if (!external) {
- node.appendSourceLink(source)
- if (isVar) {
- node.appendTextNode("var", NodeKind.Modifier)
- }
-
- if (isConst) {
- this.compileTimeInitializer?.toDocumentationNode()?.let { node.append(it, RefKind.Detail) }
- }
-
-
- 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)
- }
-
- register(this, node)
- return node
- }
-
- fun DocumentationNode.addAccessorDocumentation(documentation: Content, prefix: String) {
- if (documentation == Content.Empty) return
- updateContent {
- if (!documentation.children.isEmpty()) {
- val section = addSection(prefix, null)
- documentation.children.forEach { section.append(it) }
- }
- documentation.sections.forEach {
- val section = addSection("$prefix ${it.tag}", it.subjectName)
- it.children.forEach { section.append(it) }
- }
- }
- }
-
- fun ValueParameterDescriptor.build(): DocumentationNode {
- val node = nodeForDescriptor(this, NodeKind.Parameter)
- node.appendType(varargElementType ?: type)
- if (declaresDefaultValue()) {
- val psi = source.getPsi() as? KtParameter
- if (psi != null) {
- val defaultValueText = psi.defaultValue?.text
- if (defaultValueText != null) {
- node.appendTextNode(defaultValueText, NodeKind.Value)
- }
- }
- }
- node.appendDefaultSinceKotlin()
- node.appendAnnotations(this)
- node.appendModifiers(this)
- if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) {
- node.appendTextNode("vararg", NodeKind.Modifier)
- }
- register(this, node)
- return node
- }
-
- fun TypeParameterDescriptor.build(): DocumentationNode {
- val doc = descriptorDocumentationParser.parseDocumentation(this)
- val name = name.asString()
- val prefix = variance.label
-
- val node = DocumentationNode(name, doc, NodeKind.TypeParameter)
- if (prefix != "") {
- node.appendTextNode(prefix, NodeKind.Modifier)
- }
- if (isReified) {
- node.appendTextNode("reified", NodeKind.Modifier)
- }
-
- for (constraint in upperBounds) {
- if (KotlinBuiltIns.isDefaultBound(constraint)) {
- continue
- }
- node.appendType(constraint, NodeKind.UpperBound)
- }
- register(this, node)
- return node
- }
-
- fun ReceiverParameterDescriptor.build(): DocumentationNode {
- var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!!
- if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) {
- receiverClass = receiverClass.containingDeclaration!!
- } else if (receiverClass is TypeParameterDescriptor) {
- val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor
- if (upperBoundClass != null) {
- receiverClass = upperBoundClass
- }
- }
-
- if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) {
- link(receiverClass, containingDeclaration, RefKind.Extension)
- }
-
- val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver)
- node.appendType(type)
- register(this, node)
- return node
- }
-
- fun AnnotationDescriptor.build(): DocumentationNode? {
- val annotationClass = type.constructor.declarationDescriptor
- if (annotationClass == null || ErrorUtils.isError(annotationClass)) {
- return null
- }
- val node = DocumentationNode(annotationClass.name.asString(), Content.Empty, NodeKind.Annotation)
- allValueArguments.forEach { (name, value) ->
- val valueNode = value.toDocumentationNode()
- if (valueNode != null) {
- val paramNode = DocumentationNode(name.asString(), Content.Empty, NodeKind.Parameter)
- paramNode.append(valueNode, RefKind.Detail)
- node.append(paramNode, RefKind.Detail)
- }
- }
- return node
- }
-
- fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value.let { value ->
- val text = when (value) {
- is String ->
- "\"" + 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"
- }
- DocumentationNode(text, Content.Empty, NodeKind.Value)
- }
-
-
- 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.isPublicAPI)
- && !isDocumentationSuppressed(passConfiguration)
- && (!passConfiguration.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated())
-}
-
-private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION
-
-class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder {
- override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
- packageName: FqName,
- packageNode: DocumentationNode,
- declarations: List<DeclarationDescriptor>,
- allFqNames: Collection<FqName>) {
- val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
- declarations.forEach { descriptor ->
- with(documentationBuilder) {
- if (descriptor.isDocumented(passConfiguration)) {
- val parent = packageNode.getParentForPackageMember(
- descriptor,
- externalClassNodes,
- allFqNames,
- packageName
- )
- parent.appendOrUpdateMember(descriptor)
- }
- }
- }
- }
-}
-
-class KotlinJavaDocumentationBuilder
-@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
- val documentationBuilder: DocumentationBuilder,
- val passConfiguration: DokkaConfiguration.PassConfiguration,
- val logger: DokkaLogger) : JavaDocumentationBuilder {
- override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) {
- val classDescriptors = file.classes.map {
- it.getJavaClassDescriptor(resolutionFacade)
- }
-
- if (classDescriptors.any { it != null && it.isDocumented(passConfiguration) }) {
- val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph)
-
- for (descriptor in classDescriptors.filterNotNull()) {
- with(documentationBuilder) {
- packageNode.appendChild(descriptor, RefKind.Member)
- }
- }
- }
- }
-}
-
-private val hiddenAnnotations = setOf(
- KotlinBuiltIns.FQ_NAMES.parameterName.asString()
-)
-
-private fun AnnotationDescriptor.isHiddenInDocumentation() =
- type.constructor.declarationDescriptor?.fqNameSafe?.asString() in hiddenAnnotations
-
-private fun AnnotationDescriptor.isDocumented(): Boolean {
- if (source.getPsi() != null && mustBeDocumented()) return true
- val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString()
- return annotationClassName == KotlinBuiltIns.FQ_NAMES.extensionFunctionType.asString()
-}
-
-fun AnnotationDescriptor.mustBeDocumented(): Boolean {
- val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false
- return annotationClass.isDocumentedAnnotation()
-}
-
-fun DeclarationDescriptor.isDocumentationSuppressed(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean {
-
- if (passConfiguration.effectivePackageOptions(fqNameSafe).suppress) return true
-
- val path = this.findPsi()?.containingFile?.virtualFile?.path
- if (path != null) {
- if (path in passConfiguration.suppressedFiles) return true
- }
-
- val doc = findKDoc()
- if (doc is KDocSection && doc.findTagByName("suppress") != null) return true
-
- return hasSuppressDocTag(sourcePsi())
-}
-
-fun DeclarationDescriptor.sourcePsi() =
- ((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 CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
- val extensionReceiver = extensionReceiverParameter
- if (extensionReceiver != null) {
- val type = extensionReceiver.type
- val receiverClass = type.constructor.declarationDescriptor as? ClassDescriptor
- if (receiverClass?.isCompanionObject ?: false) {
- return receiverClass?.containingDeclaration as? ClassifierDescriptor
- }
- return receiverClass
- }
- return null
-}
-
-fun DeclarationDescriptor.signature(): String {
- if (this != original) return original.signature()
- return when (this) {
- is ClassDescriptor,
- is PackageFragmentDescriptor,
- is PackageViewDescriptor,
- is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString()
-
- is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature()
- is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature()
- is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name
- is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name
- is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name
- else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this")
- }
-}
-
-fun PropertyDescriptor.receiverSignature(): String {
- val receiver = extensionReceiverParameter
- if (receiver != null) {
- return "#" + receiver.type.signature()
- }
- return ""
-}
-
-fun CallableMemberDescriptor.parameterSignature(): String {
- val params = valueParameters.map { it.type }.toMutableList()
- val extensionReceiver = extensionReceiverParameter
- if (extensionReceiver != null) {
- params.add(0, extensionReceiver.type)
- }
- return params.joinToString(prefix = "(", postfix = ")") { it.signature() }
-}
-
-fun KotlinType.signature(): String {
- val visited = hashSetOf<KotlinType>()
-
- fun KotlinType.signatureRecursive(): String {
- if (this in visited) {
- return ""
- }
- visited.add(this)
-
- val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>"
- val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString()
- if (arguments.isEmpty()) {
- return typeName
- }
- return typeName + arguments.joinToString(prefix = "((", postfix = "))") { it.type.signatureRecursive() }
- }
-
- return signatureRecursive()
-}
-
-fun DeclarationDescriptor.signatureWithSourceLocation(): String {
- val signature = signature()
- val sourceLocation = sourceLocation()
- return if (sourceLocation != null) "$signature ($sourceLocation)" else signature
-}
-
-fun DeclarationDescriptor.sourceLocation(): String? {
- val psi = sourcePsi()
- if (psi != null) {
- val fileName = psi.containingFile.name
- val lineNumber = psi.lineNumber()
- return if (lineNumber != null) "$fileName:$lineNumber" else fileName
- }
- return null
-}
-
-fun DocumentationModule.prepareForGeneration(configuration: DokkaConfiguration) {
- if (configuration.generateIndexPages) {
- generateAllTypesNode()
- }
- nodeRefGraph.resolveReferences()
-}
-
-fun DocumentationNode.generateAllTypesNode() {
- val allTypes = members(NodeKind.Package)
- .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) {
- allTypesNode.addReferenceTo(typeNode, RefKind.Member)
- }
-
- 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, false)
- return perPackageOptions.firstOrNull { pack == it.prefix }
- ?: perPackageOptions.firstOrNull { 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
deleted file mode 100644
index bd8b9cf5..00000000
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ /dev/null
@@ -1,305 +0,0 @@
-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.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
-import org.jetbrains.kotlin.resolve.descriptorUtil.parents
-import java.io.ByteArrayOutputStream
-import java.io.PrintWriter
-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 PackageListProvider @Inject constructor(
- val configuration: DokkaConfiguration,
- val logger: DokkaLogger
-) {
- val storage = mutableMapOf<DokkaConfiguration.ExternalDocumentationLink, PackageFqNameToLocation>()
-
- 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 { toFile().mkdirs() }
-
- 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)
- }
- }
-
- }
- }
-
-
-
- fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection {
- val connection = this.openConnection()
- connection.connectTimeout = timeout
- connection.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
- }
- }
-
- fun loadPackageList(link: DokkaConfiguration.ExternalDocumentationLink) {
-
- val packageListUrl = link.packageListUrl
- val needsCache = packageListUrl.protocol in cachedProtocols
-
- val packageListStream = if (cacheDir != null && needsCache) {
- val packageListLink = packageListUrl.toExternalForm()
-
- val digest = MessageDigest.getInstance("SHA-256")
- val hash = digest.digest(packageListLink.toByteArray(Charsets.UTF_8)).toHexString()
- val cacheEntry = cacheDir.resolve(hash).toFile()
-
- if (cacheEntry.exists()) {
- try {
- val connection = packageListUrl.doOpenConnectionToReadContent()
- val originModifiedDate = connection.date
- val cacheDate = cacheEntry.lastModified()
- if (originModifiedDate > cacheDate || originModifiedDate == 0L) {
- if (originModifiedDate == 0L)
- logger.warn("No date header for $packageListUrl, downloading anyway")
- else
- logger.info("Renewing package-list from $packageListUrl")
- connection.getInputStream().copyTo(cacheEntry.outputStream())
- }
- } catch (e: Exception) {
- logger.error("Failed to update package-list cache for $link")
- val baos = ByteArrayOutputStream()
- PrintWriter(baos).use {
- e.printStackTrace(it)
- }
- baos.flush()
- logger.error(baos.toString())
- }
- } else {
- logger.info("Downloading package-list from $packageListUrl")
- packageListUrl.openStream().copyTo(cacheEntry.outputStream())
- }
- cacheEntry.inputStream()
- } else {
- packageListUrl.doOpenConnectionToReadContent().getInputStream()
- }
-
- val (params, packages) =
- packageListStream
- .bufferedReader()
- .useLines { lines -> lines.partition { it.startsWith(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX) } }
-
- val paramsMap = params.asSequence()
- .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()
-
-
- val defaultResolverDesc = ExternalDocumentationLinkResolver.services.getValue("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)
-
- 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 {
- 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 PackageFragmentDescriptor -> symbol.fqName
- is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
- 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."
- val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name }
- }
-}
-
-
-interface InboundExternalLinkResolutionService {
- fun getPath(symbol: DeclarationDescriptor): String?
-
- class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService {
- override fun getPath(symbol: DeclarationDescriptor): String? {
- 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 || symbol is JavaClassConstructorDescriptor) {
- val psi = symbol.sourcePsi() as? PsiMethod
- if (psi != null) {
- val params = psi.parameterList.parameters.joinToString { it.type.canonicalText }
- return containingClassLink + "#" + symbol.name + "(" + params + ")"
- }
- } else if (symbol is JavaPropertyDescriptor) {
- return "$containingClassLink#${symbol.name}"
- }
- }
- }
- // TODO Kotlin javadoc
- return null
- }
- }
-
- 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
- else -> false
- }
- val path = getPathWithoutExtension(symbol)
- if (leafElement) return "$path.$extension"
- else return "$path/index.$extension"
- }
-
- 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/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
deleted file mode 100644
index ee9d8c51..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.jetbrains.dokka
-
-import com.google.inject.Inject
-import com.intellij.psi.JavaPsiFacade
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiNamedElement
-import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
-import org.jetbrains.kotlin.asJava.elements.KtLightElement
-import org.jetbrains.kotlin.asJava.elements.KtLightMethod
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.lexer.KtTokens
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.psi.KtClass
-import org.jetbrains.kotlin.psi.KtDeclaration
-import org.jetbrains.kotlin.psi.KtParameter
-import org.jetbrains.kotlin.psi.KtPropertyAccessor
-
-class KotlinAsJavaDocumentationBuilder
- @Inject constructor(val kotlinAsJavaDocumentationParser: KotlinAsJavaDocumentationParser) : PackageDocumentationBuilder
-{
- override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder,
- packageName: FqName,
- packageNode: DocumentationNode,
- declarations: List<DeclarationDescriptor>,
- allFqNames: Collection<FqName>) {
- val project = documentationBuilder.resolutionFacade.project
- val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString())
- if (psiPackage == null) {
- documentationBuilder.logger.error("Cannot find Java package by qualified name: ${packageName.asString()}")
- return
- }
-
- val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.passConfiguration,
- documentationBuilder.refGraph,
- kotlinAsJavaDocumentationParser)
-
- psiPackage.classes.filter { it is KtLightElement<*, *> }.filter { it.isVisibleInDocumentation() }.forEach {
- javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it))
- }
- }
-
- fun PsiClass.isVisibleInDocumentation(): Boolean {
- val origin: KtDeclaration = (this as KtLightElement<*, *>).kotlinOrigin as? KtDeclaration ?: return true
-
- return !origin.hasModifier(KtTokens.INTERNAL_KEYWORD) && !origin.hasModifier(KtTokens.PRIVATE_KEYWORD)
- }
-}
-
-class KotlinAsJavaDocumentationParser
- @Inject constructor(val resolutionFacade: DokkaResolutionFacade,
- val descriptorDocumentationParser: DescriptorDocumentationParser) : JavaDocumentationParser
-{
- override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult {
- val kotlinLightElement = element as? KtLightElement<*, *> ?: return JavadocParseResult.Empty
- val origin = kotlinLightElement.kotlinOrigin as? KtDeclaration ?: return JavadocParseResult.Empty
- if (origin is KtParameter) {
- // LazyDeclarationResolver does not support setter parameters
- val grandFather = origin.parent?.parent
- if (grandFather is KtPropertyAccessor) {
- return JavadocParseResult.Empty
- }
- }
- val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass
- val descriptor = resolutionFacade.resolveToDescriptor(origin)
- val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor)
- return JavadocParseResult(content, null)
- }
-}
diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
deleted file mode 100644
index 20ea179e..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-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/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
deleted file mode 100644
index c7187b23..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.jetbrains.dokka
-
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiMember
-import com.intellij.psi.PsiPackage
-import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
-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? =
- when (val forPsi = this) {
- is KtLightClassForFacade -> resolutionFacade.moduleDescriptor.getPackage(forPsi.fqName)
- is KtLightElement<*, *> -> (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
deleted file mode 100644
index 7310610f..00000000
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ /dev/null
@@ -1,473 +0,0 @@
-package org.jetbrains.dokka
-
-import org.jetbrains.dokka.LanguageService.RenderMode
-
-/**
- * Implements [LanguageService] and provides rendering of symbols in Kotlin language
- */
-class KotlinLanguageService : CommonLanguageService() {
- override fun showModifierInSummary(node: DocumentationNode): Boolean {
- return node.name !in fullOnlyModifiers
- }
-
- private val fullOnlyModifiers =
- setOf("public", "protected", "private", "internal", "inline", "noinline", "crossinline", "reified")
-
- override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
- return content {
- when (node.kind) {
- NodeKind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node)
- in NodeKind.classLike -> renderClass(node, renderMode)
-
- 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(this, node, renderMode)
- NodeKind.Constructor,
- NodeKind.Function,
- NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode)
- NodeKind.Property,
- NodeKind.CompanionObjectProperty -> renderProperty(node, renderMode)
- else -> identifier(node.name)
- }
- }
- }
-
-
- override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
- if (nodes.size < 2) return null
- val receiverKind = nodes.getReceiverKind() ?: return null
- val functionWithTypeParameter = nodes.firstOrNull { it.details(NodeKind.TypeParameter).any() } ?: return null
- 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)
- )
- }
- }
- }
-
- private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? {
- val qNames = mapNotNull { it.getReceiverQName() }
- if (qNames.size != size)
- return null
-
- return ReceiverKind.values().firstOrNull { kind -> qNames.all { it in kind.classes } }
- }
-
- private fun DocumentationNode.getReceiverQName(): String? {
- if (kind != NodeKind.Function && kind != NodeKind.Property) return null
- val receiver = details(NodeKind.Receiver).singleOrNull() ?: return null
- return receiver.detail(NodeKind.Type).qualifiedNameFromType()
- }
-
- companion object {
- private val arrayClasses = setOf(
- "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
- }
-
- private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
- ARRAY("any_array", arrayClasses),
- ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses),
- ITERABLE("any_iterable", iterableClasses),
- }
-
- interface SignatureMapper {
- fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
- }
-
- 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.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()
- }
- }
-
- private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
- var typeArguments = node.details(NodeKind.Type)
-
- if (node.name.startsWith("Suspend")) {
- keyword("suspend ")
- }
-
- // lambda
- val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" }
- if (isExtension) {
- renderType(typeArguments.first(), renderMode)
- symbol(".")
- typeArguments = typeArguments.drop(1)
- }
- symbol("(")
- renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
- renderFunctionalTypeParameterName(it, renderMode)
- renderType(it, renderMode)
- }
- symbol(")")
- nbsp()
- symbol("->")
- nbsp()
- renderType(typeArguments.last(), renderMode)
-
- }
-
- private fun DocumentationNode.isFunctionalType(): Boolean {
- val typeArguments = details(NodeKind.Type)
- val functionalTypeName = "Function${typeArguments.count() - 1}"
- val suspendFunctionalTypeName = "Suspend$functionalTypeName"
- return name == functionalTypeName || name == suspendFunctionalTypeName
- }
-
- private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
- if (node.name == "dynamic") {
- keyword("dynamic")
- return
- }
-
- val nullabilityModifier = node.detailOrNull(NodeKind.NullabilityModifier)
-
- if (node.isFunctionalType()) {
- if (nullabilityModifier != null) {
- symbol("(")
- renderFunctionalType(node, renderMode)
- symbol(")")
- symbol(nullabilityModifier.name)
- } else {
- renderFunctionalType(node, renderMode)
- }
- return
- }
- if (renderMode == RenderMode.FULL) {
- renderAnnotationsForNode(node)
- }
- renderModifiersForNode(node, renderMode, true)
- renderLinked(this, node) {
- identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName)
- }
- val typeArguments = node.details(NodeKind.Type)
- if (typeArguments.isNotEmpty()) {
- symbol("<")
- renderList(typeArguments, noWrap = true) {
- renderType(it, renderMode)
- }
- symbol(">")
- }
-
- nullabilityModifier ?.apply {
- symbol(nullabilityModifier.name)
- }
- }
-
- override fun renderModifier(
- block: ContentBlock,
- node: DocumentationNode,
- renderMode: RenderMode,
- nowrap: Boolean
- ) {
- when (node.name) {
- "final", "public", "var", "expect", "actual", "external" -> {
- }
- else -> {
- if (showModifierInSummary(node) || renderMode == RenderMode.FULL) {
- super.renderModifier(block, node, renderMode, nowrap)
- }
- }
- }
- }
-
- private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) {
- renderModifiersForNode(node, renderMode, true)
-
- identifier(node.name)
-
- val constraints = node.details(NodeKind.UpperBound)
- if (constraints.size == 1) {
- nbsp()
- symbol(":")
- nbsp()
- renderList(constraints, noWrap = true) {
- renderType(it, renderMode)
- }
- }
- }
-
- private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
- if (renderMode == RenderMode.FULL) {
- renderAnnotationsForNode(node)
- }
- renderModifiersForNode(node, renderMode)
- identifier(node.name, IdentifierKind.ParameterName, node.detailOrNull(NodeKind.Signature)?.name)
- symbol(":")
- nbsp()
- val parameterType = node.detail(NodeKind.Type)
- renderType(parameterType, renderMode)
- val valueNode = node.details(NodeKind.Value).firstOrNull()
- if (valueNode != null) {
- nbsp()
- symbol("=")
- nbsp()
- text(valueNode.name)
- }
- }
-
- private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) {
- val typeParameters = node.details(NodeKind.TypeParameter)
- if (typeParameters.any()) {
- symbol("<")
- renderList(typeParameters) {
- renderTypeParameter(it, renderMode)
- }
- symbol(">")
- }
- }
-
- private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) {
- 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 }
- }
- if (parametersWithMultipleConstraints.isNotEmpty()) {
- keyword(" where ")
- renderList(parametersWithConstraints) {
- identifier(it.first.name)
- nbsp()
- symbol(":")
- nbsp()
- renderType(it.second, renderMode)
- }
- }
- }
-
- private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
- val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
- if (supertypes.any()) {
- nbsp()
- symbol(":")
- nbsp()
- renderList(supertypes) {
- indentedSoftLineBreak()
- renderType(it, renderMode)
- }
- }
- }
-
- private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) {
- node.annotations.forEach {
- renderAnnotation(it)
- }
- }
-
- private fun ContentBlock.renderAnnotation(node: DocumentationNode) {
- identifier("@" + node.name, IdentifierKind.AnnotationName)
- val parameters = node.details(NodeKind.Parameter)
- if (!parameters.isEmpty()) {
- symbol("(")
- renderList(parameters) {
- text(it.detail(NodeKind.Value).name)
- }
- symbol(")")
- }
- text(" ")
- }
-
- private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
- if (renderMode == RenderMode.FULL) {
- renderAnnotationsForNode(node)
- }
- renderModifiersForNode(node, renderMode)
- when (node.kind) {
- NodeKind.Class,
- NodeKind.AnnotationClass,
- NodeKind.Exception,
- NodeKind.Enum -> keyword("class ")
- NodeKind.Interface -> keyword("interface ")
- NodeKind.EnumItem -> keyword("enum val ")
- NodeKind.Object -> keyword("object ")
- NodeKind.TypeAlias -> keyword("typealias ")
- else -> throw IllegalArgumentException("Node $node is not a class-like object")
- }
-
- identifierOrDeprecated(node)
- renderTypeParametersForNode(node, renderMode)
- renderSupertypesForNode(node, renderMode)
- renderExtraTypeParameterConstraints(node, renderMode)
-
- if (node.kind == NodeKind.TypeAlias) {
- nbsp()
- symbol("=")
- nbsp()
- renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode)
- }
- }
-
- private fun ContentBlock.renderFunction(
- node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null
- ) {
- if (renderMode == RenderMode.FULL) {
- renderAnnotationsForNode(node)
- }
- renderModifiersForNode(node, renderMode)
- when (node.kind) {
- NodeKind.Constructor -> identifier(node.owner!!.name)
- NodeKind.Function,
- NodeKind.CompanionObjectFunction -> keyword("fun ")
- else -> throw IllegalArgumentException("Node $node is not a function-like object")
- }
- renderTypeParametersForNode(node, renderMode)
- if (node.details(NodeKind.TypeParameter).any()) {
- text(" ")
- }
-
- renderReceiver(node, renderMode, signatureMapper)
-
- if (node.kind != NodeKind.Constructor)
- identifierOrDeprecated(node)
-
- symbol("(")
- val parameters = node.details(NodeKind.Parameter)
- renderList(parameters) {
- indentedSoftLineBreak()
- renderParameter(it, renderMode)
- }
- if (needReturnType(node)) {
- if (parameters.isNotEmpty()) {
- softLineBreak()
- }
- symbol(")")
- symbol(": ")
- renderType(node.detail(NodeKind.Type), renderMode)
- } else {
- symbol(")")
- }
- renderExtraTypeParameterConstraints(node, renderMode)
- }
-
- private fun ContentBlock.renderReceiver(
- node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper?
- ) {
- val receiver = node.details(NodeKind.Receiver).singleOrNull()
- if (receiver != null) {
- if (signatureMapper != null) {
- signatureMapper.renderReceiver(receiver, this)
- } else {
- val type = receiver.detail(NodeKind.Type)
-
- if (type.isFunctionalType()) {
- symbol("(")
- renderFunctionalType(type, renderMode)
- symbol(")")
- } else {
- renderType(type, renderMode)
- }
- }
- symbol(".")
- }
- }
-
- 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"
-
- private fun ContentBlock.renderProperty(
- node: DocumentationNode,
- renderMode: RenderMode,
- signatureMapper: SignatureMapper? = null
- ) {
- if (renderMode == RenderMode.FULL) {
- renderAnnotationsForNode(node)
- }
- renderModifiersForNode(node, renderMode)
- when (node.kind) {
- NodeKind.Property,
- NodeKind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ")
- else -> throw IllegalArgumentException("Node $node is not a property")
- }
- renderTypeParametersForNode(node, renderMode)
- if (node.details(NodeKind.TypeParameter).any()) {
- text(" ")
- }
-
- renderReceiver(node, renderMode, signatureMapper)
-
- identifierOrDeprecated(node)
- symbol(": ")
- renderType(node.detail(NodeKind.Type), renderMode)
- renderExtraTypeParameterConstraints(node, renderMode)
- }
-
- fun DocumentationNode.getPropertyKeyword() =
- if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
-
- fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) {
- if (node.deprecation != null) {
- val strike = ContentStrikethrough()
- strike.identifier(node.name)
- append(strike)
- } else {
- identifier(node.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)