Skip to content

Commit

Permalink
Multilanguage docs inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcinAman committed Jun 10, 2021
1 parent 5c000c8 commit 670fd25
Show file tree
Hide file tree
Showing 13 changed files with 611 additions and 71 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Expand Up @@ -4,7 +4,7 @@ dokka_publication_channels=bintray-kotlin-dev&space-dokka-dev
dokka_integration_test_parallelism=2
# Versions
kotlin_version=1.5.0
coroutines_version=1.4.3
coroutines_version=1.5.0
kotlinx_html_version=0.7.3
kotlin_plugin_version=202-1.5.0-release-755-release-IJ8194.7
idea_version=202.7660.26
Expand Down
7 changes: 4 additions & 3 deletions plugins/base/api/base.api
Expand Up @@ -115,7 +115,8 @@ public class org/jetbrains/dokka/base/parsers/MarkdownParser : org/jetbrains/dok

public final class org/jetbrains/dokka/base/parsers/MarkdownParser$Companion {
public final fun fqName (Lorg/jetbrains/dokka/links/DRI;)Ljava/lang/String;
public final fun parseFromKDocTag (Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag;Lkotlin/jvm/functions/Function1;Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocumentationNode;
public final fun parseFromKDocTag (Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Z)Lorg/jetbrains/dokka/model/doc/DocumentationNode;
public static synthetic fun parseFromKDocTag$default (Lorg/jetbrains/dokka/base/parsers/MarkdownParser$Companion;Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZILjava/lang/Object;)Lorg/jetbrains/dokka/model/doc/DocumentationNode;
}

public abstract class org/jetbrains/dokka/base/parsers/Parser {
Expand Down Expand Up @@ -1349,7 +1350,7 @@ public final class org/jetbrains/dokka/base/translators/psi/DefaultPsiToDocument
}

public final class org/jetbrains/dokka/base/translators/psi/DefaultPsiToDocumentableTranslator$DokkaPsiParser {
public fun <init> (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/utilities/DokkaLogger;)V
public fun <init> (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/analysis/DokkaResolutionFacade;Lorg/jetbrains/dokka/utilities/DokkaLogger;)V
public final fun parsePackage (Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

Expand All @@ -1359,7 +1360,7 @@ public abstract interface class org/jetbrains/dokka/base/translators/psi/parsers

public final class org/jetbrains/dokka/base/translators/psi/parsers/JavadocParser : org/jetbrains/dokka/base/translators/psi/parsers/JavaDocumentationParser {
public static final field Companion Lorg/jetbrains/dokka/base/translators/psi/parsers/JavadocParser$Companion;
public fun <init> (Lorg/jetbrains/dokka/utilities/DokkaLogger;)V
public fun <init> (Lorg/jetbrains/dokka/utilities/DokkaLogger;Lorg/jetbrains/dokka/analysis/DokkaResolutionFacade;)V
public fun parseDocumentation (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/model/doc/DocumentationNode;
}

Expand Down
3 changes: 0 additions & 3 deletions plugins/base/build.gradle.kts
@@ -1,8 +1,5 @@
import org.jetbrains.registerDokkaArtifactPublication

plugins {
id("com.jfrog.bintray")
}

dependencies {
val coroutines_version: String by project
Expand Down
3 changes: 2 additions & 1 deletion plugins/base/src/main/kotlin/parsers/MarkdownParser.kt
Expand Up @@ -504,6 +504,7 @@ open class MarkdownParser(
kDocTag: KDocTag?,
externalDri: (String) -> DRI?,
kdocLocation: String?,
parseWithChildren: Boolean = true
): DocumentationNode {
return if (kDocTag == null) {
DocumentationNode(emptyList())
Expand All @@ -517,7 +518,7 @@ open class MarkdownParser(
}

val allTags =
listOf(kDocTag) + if (kDocTag.canHaveParent()) getAllKDocTags(findParent(kDocTag)) else emptyList()
listOf(kDocTag) + if (kDocTag.canHaveParent() && parseWithChildren) getAllKDocTags(findParent(kDocTag)) else emptyList()
DocumentationNode(
allTags.map {
when (it.knownTag) {
Expand Down
Expand Up @@ -123,7 +123,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
text(
if (c.modifier[sourceSet] !in ignoredModifiers)
when {
c.extra[AdditionalModifiers]?.content?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> ""
c.extra[AdditionalModifiers]?.content?.get(sourceSet)?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> ""
c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} "
else -> c.modifier[sourceSet]?.name?.let { "$it " } ?: ""
}
Expand Down
Expand Up @@ -159,6 +159,6 @@ internal class ReportUndocumentedTransformer : DocumentableTransformer {
val packageName = documentable.dri.packageName ?: return null
return dokkaSourceSet.perPackageOptions
.filter { packageOptions -> Regex(packageOptions.matchingRegex).matches(packageName) }
.maxBy { packageOptions -> packageOptions.matchingRegex.length }
.maxByOrNull { packageOptions -> packageOptions.matchingRegex.length }
}
}
Expand Up @@ -13,7 +13,7 @@ class DefaultSamplesTransformer(context: DokkaContext) : SamplesTransformer(cont
override fun processBody(psiElement: PsiElement): String {
val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd()
val lines = text.split("\n")
val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0
val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.minOrNull() ?: 0
return lines.joinToString("\n") { it.drop(indent) }
}

Expand Down
@@ -1,7 +1,9 @@
package org.jetbrains.dokka.base.translators.descriptors

import com.intellij.psi.PsiNamedElement
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
import org.jetbrains.dokka.analysis.DescriptorDocumentableSource
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
Expand All @@ -10,6 +12,7 @@ import org.jetbrains.dokka.analysis.from
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.parsers.MarkdownParser
import org.jetbrains.dokka.base.translators.isDirectlyAnException
import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser
import org.jetbrains.dokka.base.translators.unquotedValue
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.links.Callable
Expand Down Expand Up @@ -38,6 +41,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations
import org.jetbrains.kotlin.idea.kdoc.findKDoc
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.load.kotlin.toSourceElement
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
Expand All @@ -58,6 +62,7 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.nio.file.Paths
import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue
Expand Down Expand Up @@ -110,6 +115,8 @@ private class DokkaDescriptorVisitor(
private val resolutionFacade: DokkaResolutionFacade,
private val logger: DokkaLogger
) {
private val javadocParser = JavadocParser(logger, resolutionFacade)

private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter {
it.toSourceElement.containingFile.toString().let { path ->
path.isNotBlank() && sourceSet.sourceRoots.any { root ->
Expand Down Expand Up @@ -848,7 +855,7 @@ private class DokkaDescriptorVisitor(
org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Covariance(this)
}

private fun DeclarationDescriptor.getDocumentation() = findKDoc().let {
private fun DeclarationDescriptor.getDocumentation() = (findKDoc()?.let {
MarkdownParser.parseFromKDocTag(
kDocTag = it,
externalDri = { link: String ->
Expand All @@ -871,7 +878,12 @@ private class DokkaDescriptorVisitor(
else it
}
)
}.takeIf { it.children.isNotEmpty() }
} ?: getJavaDocs())?.takeIf { it.children.isNotEmpty() }

private fun DeclarationDescriptor.getJavaDocs() = (this as? CallableDescriptor)
?.overriddenDescriptors
?.mapNotNull { it.findPsi() as? PsiNamedElement }
?.firstNotNullResult { javadocParser.parseDocumentation(it) }

private suspend fun ClassDescriptor.companion(dri: DRIWithPlatformInfo): DObject? = companionObjectDescriptor?.let {
objectDescriptor(it, dri)
Expand Down
Expand Up @@ -12,6 +12,7 @@ import com.intellij.psi.impl.source.PsiImmediateClassType
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
import org.jetbrains.dokka.analysis.KotlinAnalysis
import org.jetbrains.dokka.analysis.PsiDocumentableSource
import org.jetbrains.dokka.analysis.from
Expand Down Expand Up @@ -46,6 +47,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName
Expand All @@ -69,7 +71,7 @@ class DefaultPsiToDocumentableTranslator(
sourceSet.sourceRoots.any { root -> file.startsWith(root) }


val (environment, _) = kotlinAnalysis[sourceSet]
val (environment, facade) = kotlinAnalysis[sourceSet]

val sourceRoots = environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS)
?.filterIsInstance<JavaSourceRoot>()
Expand All @@ -88,6 +90,7 @@ class DefaultPsiToDocumentableTranslator(
val docParser =
DokkaPsiParser(
sourceSet,
facade,
context.logger
)

Expand All @@ -106,10 +109,11 @@ class DefaultPsiToDocumentableTranslator(

class DokkaPsiParser(
private val sourceSetData: DokkaSourceSet,
facade: DokkaResolutionFacade,
private val logger: DokkaLogger
) {

private val javadocParser: JavaDocumentationParser = JavadocParser(logger)
private val javadocParser: JavaDocumentationParser = JavadocParser(logger, facade)

private val cachedBounds = hashMapOf<String, Bound>()

Expand Down Expand Up @@ -207,15 +211,15 @@ class DefaultPsiToDocumentableTranslator(
}
parseSupertypes(superTypes)
val (regularFunctions, accessors) = splitFunctionsAndAccessors()
val overriden = regularFunctions.flatMap { it.findSuperMethods().toList() }
val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent()
val allFunctions = async {
regularFunctions.parallelMapNotNull {
if (!it.isConstructor) parseFunction(
it,
parentDRI = dri
) else null
} +
superMethods.parallelMap { parseFunction(it.first, inheritedFrom = it.second) }
} + superMethods.filter { it.first !in overriden }.parallelMap { parseFunction(it.first, inheritedFrom = it.second) }
}
val source = PsiDocumentableSource(this).toSourceSetDependent()
val classlikes = async { innerClasses.asIterable().parallelMap { parseClasslike(it, dri) } }
Expand Down
Expand Up @@ -7,6 +7,7 @@ import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.javadoc.PsiDocTag
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull

internal data class CommentResolutionContext(
Expand All @@ -21,21 +22,34 @@ internal class InheritDocResolver(
) {
internal fun resolveFromContext(context: CommentResolutionContext) =
when (context.tag) {
JavadocTag.THROWS, JavadocTag.EXCEPTION -> context.name?.let { name -> resolveThrowsTag(context.tag, context.comment, name) }
JavadocTag.PARAM -> context.parameterIndex?.let { paramIndex -> resolveParamTag(context.comment, paramIndex) }
JavadocTag.THROWS, JavadocTag.EXCEPTION -> context.name?.let { name ->
resolveThrowsTag(
context.tag,
context.comment,
name
)
}
JavadocTag.PARAM -> context.parameterIndex?.let { paramIndex ->
resolveParamTag(
context.comment,
paramIndex
)
}
JavadocTag.DEPRECATED -> resolveGenericTag(context.comment, JavadocTag.DESCRIPTION)
JavadocTag.SEE -> emptyList()
else -> context.tag?.let { tag -> resolveGenericTag(context.comment, tag) }
}

private fun resolveGenericTag(currentElement: PsiDocComment, tag: JavadocTag): List<PsiElement> =
private fun resolveGenericTag(currentElement: PsiDocComment, tag: JavadocTag) =
when (val owner = currentElement.owner) {
is PsiClass -> lowestClassWithTag(owner, tag)
is PsiMethod -> lowestMethodWithTag(owner, tag)
else -> null
}?.tagsByName(tag)?.flatMap {
when (it) {
is PsiDocTag -> it.contentElementsWithSiblingIfNeeded()
when {
it is PsiDocumentationContent && it.psiElement is PsiDocTag ->
it.psiElement.contentElementsWithSiblingIfNeeded()
.map { content -> PsiDocumentationContent(content, it.tag) }
else -> listOf(it)
}
}.orEmpty()
Expand All @@ -49,58 +63,70 @@ internal class InheritDocResolver(
tag: JavadocTag,
currentElement: PsiDocComment,
exceptionFqName: String
): List<PsiElement> =
(currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, tag) }
): List<DocumentationContent> {
val closestDocs = (currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, tag) }
.orEmpty().firstOrNull {
findClosestDocComment(it, logger)?.hasTagWithExceptionOfType(tag, exceptionFqName) == true
}?.docComment?.tagsByName(tag)?.flatMap {
}

return when (closestDocs?.language?.id) {
"kotlin" -> closestDocs.toKdocComment()?.tagsByName(tag, exceptionFqName).orEmpty()
else -> closestDocs?.docComment?.tagsByName(tag)?.flatMap {
when (it) {
is PsiDocTag -> it.contentElementsWithSiblingIfNeeded()
else -> listOf(it)
}
}?.withoutReferenceLink().orEmpty()
}?.withoutReferenceLink().orEmpty().map { PsiDocumentationContent(it, tag) }
}
}

private fun resolveParamTag(
currentElement: PsiDocComment,
parameterIndex: Int,
): List<PsiElement> =
): List<DocumentationContent> =
(currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, JavadocTag.PARAM) }
.orEmpty().flatMap {
if (parameterIndex >= it.parameterList.parametersCount || parameterIndex < 0) emptyList()
else {
val closestTag = findClosestDocComment(it, logger)
val hasTag = closestTag?.hasTag(JavadocTag.PARAM)
if (hasTag != true) emptyList()
else {
val parameterName = it.parameterList.parameters[parameterIndex].name
closestTag.tagsByName(JavadocTag.PARAM)
.filterIsInstance<PsiDocTag>().map { it.contentElementsWithSiblingIfNeeded() }.firstOrNull {
it.firstOrNull()?.text == parameterName
}.orEmpty()
when {
hasTag != true -> emptyList()
closestTag is JavaDocComment -> resolveJavaParamTag(closestTag, parameterIndex, it)
.withoutReferenceLink().map { PsiDocumentationContent(it, JavadocTag.PARAM) }
closestTag is KotlinDocComment -> resolveKdocTag(closestTag, parameterIndex)
else -> emptyList()
}
}
}.withoutReferenceLink()
}

private fun resolveJavaParamTag(comment: JavaDocComment, parameterIndex: Int, method: PsiMethod) =
comment.comment.tagsByName(JavadocTag.PARAM)
.filterIsInstance<PsiDocTag>().map { it.contentElementsWithSiblingIfNeeded() }.firstOrNull {
it.firstOrNull()?.text == method.parameterList.parameters[parameterIndex].name
}.orEmpty()

private fun resolveKdocTag(comment: KotlinDocComment, parameterIndex: Int): List<DocumentationContent> =
listOf(comment.tagsByName(JavadocTag.PARAM)[parameterIndex])

//if we are in psi class javadoc only inherits docs from classes and not from interfaces
private fun lowestClassWithTag(baseClass: PsiClass, javadocTag: JavadocTag): PsiDocComment? =
private fun lowestClassWithTag(baseClass: PsiClass, javadocTag: JavadocTag): DocComment? =
baseClass.superClass?.let {
findClosestDocComment(it, logger)?.takeIf { tag -> tag.hasTag(javadocTag) } ?:
lowestClassWithTag(it, javadocTag)
findClosestDocComment(it, logger)?.takeIf { tag -> tag.hasTag(javadocTag) } ?: lowestClassWithTag(
it,
javadocTag
)
}

private fun lowestMethodWithTag(
baseMethod: PsiMethod,
javadocTag: JavadocTag,
): PsiDocComment? =
lowestMethodsWithTag(baseMethod, javadocTag).firstOrNull()?.docComment
): DocComment? =
lowestMethodsWithTag(baseMethod, javadocTag).firstOrNull()
?.let { it.docComment?.let { JavaDocComment(it) } ?: it.toKdocComment() }

private fun lowestMethodsWithTag(baseMethod: PsiMethod, javadocTag: JavadocTag) =
baseMethod.findSuperMethods().filter { findClosestDocComment(it, logger)?.hasTag(javadocTag) == true }

private fun PsiDocComment.hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean =
hasTag(tag) && tagsByName(tag).firstIsInstanceOrNull<PsiDocTag>()
?.resolveToElement()
?.getKotlinFqName()?.asString() == exceptionFqName

private fun List<PsiElement>.withoutReferenceLink(): List<PsiElement> = drop(1)
}

0 comments on commit 670fd25

Please sign in to comment.