aboutsummaryrefslogtreecommitdiff
path: root/plugins/base
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2022-01-18 10:51:31 +0100
committerGitHub <noreply@github.com>2022-01-18 12:51:31 +0300
commit62f2919f403a4617dfc622a260b8c4fc8fc13716 (patch)
treea16d9bd922cd75fb15658d6a6b38dfacc362e399 /plugins/base
parent6b729257b89ff5a19a52045efa386349feec0bc6 (diff)
downloaddokka-62f2919f403a4617dfc622a260b8c4fc8fc13716.tar.gz
dokka-62f2919f403a4617dfc622a260b8c4fc8fc13716.tar.bz2
dokka-62f2919f403a4617dfc622a260b8c4fc8fc13716.zip
Add external documentable provider (#2307)
* Add external documentable provider * Update the api spec of base plugin * Hide mistakenly exposed fields * Add comments and fix naming
Diffstat (limited to 'plugins/base')
-rw-r--r--plugins/base/api/base.api20
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt13
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt17
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt42
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt12
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt22
-rw-r--r--plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt139
7 files changed, 261 insertions, 4 deletions
diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api
index 9450476b..48a7a14a 100644
--- a/plugins/base/api/base.api
+++ b/plugins/base/api/base.api
@@ -5,6 +5,8 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug
public final fun getBaseSearchbarDataInstaller ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getCommentsToContentConverter ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getCustomResourceInstaller ()Lorg/jetbrains/dokka/plugability/Extension;
+ public final fun getDefaultExternalClasslikesTranslator ()Lorg/jetbrains/dokka/plugability/Extension;
+ public final fun getDefaultExternalDocumentablesProvider ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getDefaultKotlinAnalysis ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getDefaultSamplesTransformer ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getDefaultTabSortingStrategy ()Lorg/jetbrains/dokka/plugability/Extension;
@@ -18,6 +20,8 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug
public final fun getEmptyModulesFilter ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getEmptyPackagesFilter ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getExtensionsExtractor ()Lorg/jetbrains/dokka/plugability/Extension;
+ public final fun getExternalClasslikesTranslator ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
+ public final fun getExternalDocumentablesProvider ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getExternalLocationProviderFactory ()Lorg/jetbrains/dokka/plugability/ExtensionPoint;
public final fun getFallbackMerger ()Lorg/jetbrains/dokka/plugability/Extension;
public final fun getFileWriter ()Lorg/jetbrains/dokka/plugability/Extension;
@@ -1279,16 +1283,30 @@ public final class org/jetbrains/dokka/base/translators/descriptors/DRIWithPlatf
public fun toString ()Ljava/lang/String;
}
-public final class org/jetbrains/dokka/base/translators/descriptors/DefaultDescriptorToDocumentableTranslator : org/jetbrains/dokka/transformers/sources/AsyncSourceToDocumentableTranslator {
+public final class org/jetbrains/dokka/base/translators/descriptors/DefaultDescriptorToDocumentableTranslator : org/jetbrains/dokka/base/translators/descriptors/ExternalClasslikesTranslator, org/jetbrains/dokka/transformers/sources/AsyncSourceToDocumentableTranslator {
public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
public fun invoke (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DModule;
public fun invokeSuspending (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
+ public fun translateClassDescriptor (Lorg/jetbrains/kotlin/descriptors/ClassDescriptor;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike;
}
public final class org/jetbrains/dokka/base/translators/descriptors/DefaultDescriptorToDocumentableTranslatorKt {
public static final fun withEmptyInfo (Lorg/jetbrains/dokka/links/DRI;)Lorg/jetbrains/dokka/base/translators/descriptors/DRIWithPlatformInfo;
}
+public final class org/jetbrains/dokka/base/translators/descriptors/DefaultExternalDocumentablesProvider : org/jetbrains/dokka/base/translators/descriptors/ExternalDocumentablesProvider {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun findClasslike (Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike;
+}
+
+public abstract interface class org/jetbrains/dokka/base/translators/descriptors/ExternalClasslikesTranslator {
+ public abstract fun translateClassDescriptor (Lorg/jetbrains/kotlin/descriptors/ClassDescriptor;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike;
+}
+
+public abstract interface class org/jetbrains/dokka/base/translators/descriptors/ExternalDocumentablesProvider {
+ public abstract fun findClasslike (Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike;
+}
+
public final class org/jetbrains/dokka/base/translators/documentables/BriefFromContentNodesKt {
public static final fun firstParagraphBrief (Lorg/jetbrains/dokka/model/doc/DocTag;)Lorg/jetbrains/dokka/model/doc/DocTag;
public static final fun firstSentenceBriefFromContentNodes (Ljava/util/List;)Ljava/util/List;
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt
index 03fbf64e..a26f6dcd 100644
--- a/plugins/base/src/main/kotlin/DokkaBase.kt
+++ b/plugins/base/src/main/kotlin/DokkaBase.kt
@@ -29,6 +29,9 @@ import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToP
import org.jetbrains.dokka.base.translators.psi.DefaultPsiToDocumentableTranslator
import org.jetbrains.dokka.base.generation.SingleModuleGeneration
import org.jetbrains.dokka.base.renderers.html.command.consumers.ReplaceVersionsConsumer
+import org.jetbrains.dokka.base.translators.descriptors.DefaultExternalDocumentablesProvider
+import org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTranslator
+import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider
import org.jetbrains.dokka.plugability.DokkaPlugin
import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer
import org.jetbrains.dokka.transformers.pages.PageTransformer
@@ -46,6 +49,8 @@ class DokkaBase : DokkaPlugin() {
val kotlinAnalysis by extensionPoint<KotlinAnalysis>()
val tabSortingStrategy by extensionPoint<TabSortingStrategy>()
val immediateHtmlCommandConsumer by extensionPoint<ImmediateHtmlCommandConsumer>()
+ val externalDocumentablesProvider by extensionPoint<ExternalDocumentablesProvider>()
+ val externalClasslikesTranslator by extensionPoint<ExternalClasslikesTranslator>()
val singleGeneration by extending {
CoreExtensions.generation providing ::SingleModuleGeneration
@@ -252,4 +257,12 @@ class DokkaBase : DokkaPlugin() {
val baseSearchbarDataInstaller by extending {
htmlPreprocessors providing ::SearchbarDataInstaller order { after(sourceLinksTransformer) }
}
+
+ val defaultExternalDocumentablesProvider by extending {
+ externalDocumentablesProvider providing ::DefaultExternalDocumentablesProvider
+ }
+
+ val defaultExternalClasslikesTranslator by extending {
+ externalClasslikesTranslator providing ::DefaultDescriptorToDocumentableTranslator
+ }
}
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
index 04716e16..e996a865 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
@@ -2,6 +2,7 @@ package org.jetbrains.dokka.base.translators.descriptors
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.util.PsiLiteralUtil.*
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
@@ -55,6 +56,7 @@ import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass
import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
+import org.jetbrains.kotlin.resolve.descriptorUtil.parents
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
@@ -79,8 +81,8 @@ import org.jetbrains.kotlin.resolve.constants.BooleanValue as ConstantsBooleanVa
import org.jetbrains.kotlin.resolve.constants.NullValue as ConstantsNullValue
class DefaultDescriptorToDocumentableTranslator(
- context: DokkaContext
-) : AsyncSourceToDocumentableTranslator {
+ private val context: DokkaContext
+) : AsyncSourceToDocumentableTranslator, ExternalClasslikesTranslator {
private val kotlinAnalysis: KotlinAnalysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis }
@@ -109,6 +111,15 @@ class DefaultDescriptorToDocumentableTranslator(
)
}
}
+
+ override fun translateClassDescriptor(descriptor: ClassDescriptor, sourceSet: DokkaSourceSet): DClasslike {
+ val driInfo = DRI.from(descriptor.parents.first()).withEmptyInfo()
+
+ return runBlocking(Dispatchers.Default) {
+ DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger)
+ .visitClassDescriptor(descriptor, driInfo)
+ }
+ }
}
data class DRIWithPlatformInfo(
@@ -162,7 +173,7 @@ private class DokkaDescriptorVisitor(
}
}
- private suspend fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClasslike =
+ suspend fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClasslike =
when (descriptor.kind) {
ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent)
ClassKind.OBJECT -> objectDescriptor(descriptor, parent)
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt
new file mode 100644
index 00000000..05982301
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt
@@ -0,0 +1,42 @@
+package org.jetbrains.dokka.base.translators.descriptors
+
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.DClasslike
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
+import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
+
+class DefaultExternalDocumentablesProvider(context: DokkaContext) : ExternalDocumentablesProvider {
+ private val analysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis }
+
+ private val translator = context.plugin<DokkaBase>().querySingle { externalClasslikesTranslator }
+
+ override fun findClasslike(dri: DRI, sourceSet: DokkaSourceSet): DClasslike? {
+ val pkg = dri.packageName?.let { FqName(it) } ?: FqName.ROOT
+ val names = dri.classNames?.split('.') ?: return null
+
+ val packageDsc = analysis[sourceSet].facade.moduleDescriptor.getPackage(pkg)
+ val classDsc = names.fold<String, DeclarationDescriptor?>(packageDsc) { dsc, name ->
+ dsc?.scope?.getDescriptorsFiltered { it.identifier == name }
+ ?.filterIsInstance<ClassDescriptor>()
+ ?.firstOrNull()
+ }
+
+ return (classDsc as? ClassDescriptor)?.let { translator.translateClassDescriptor(it, sourceSet) }
+ }
+
+ private val DeclarationDescriptor.scope: MemberScope
+ get() = when (this) {
+ is PackageViewDescriptor -> memberScope
+ is ClassDescriptor -> unsubstitutedMemberScope
+ else -> throw IllegalArgumentException("Unexpected type of descriptor: ${this::class}")
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt
new file mode 100644
index 00000000..ec4d1ee9
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.dokka.base.translators.descriptors
+
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.model.DClasslike
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+
+/**
+ * Service translating [ClassDescriptor]s of symbols defined outside of documented project to [DClasslike]s.
+ */
+interface ExternalClasslikesTranslator {
+ fun translateClassDescriptor(descriptor: ClassDescriptor, sourceSet: DokkaSourceSet): DClasslike
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt b/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt
new file mode 100644
index 00000000..c1dbfc28
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.dokka.base.translators.descriptors
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.DClasslike
+
+/**
+ * Service that can be queried with [DRI] and source set to obtain a documentable for classlike.
+ *
+ * There are some cases when there is a need to process documentables of classlikes that were not defined
+ * in the project itself but are somehow related to the symbols defined in the documented project (e.g. are supertypes
+ * of classes defined in project).
+ */
+interface ExternalDocumentablesProvider {
+
+ /**
+ * Returns [DClasslike] matching provided [DRI] in specified source set.
+ *
+ * Result is null if compiler haven't generated matching class descriptor.
+ */
+ fun findClasslike(dri: DRI, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike?
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt
new file mode 100644
index 00000000..011ae729
--- /dev/null
+++ b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt
@@ -0,0 +1,139 @@
+package translators
+
+import com.intellij.openapi.application.PathManager
+import kotlinx.coroutines.Job
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.DInterface
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.utilities.cast
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class ExternalDocumentablesTest : BaseAbstractTest() {
+ @Test
+ fun `external documentable from java stdlib`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src")
+ analysisPlatform = "jvm"
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ testInline(
+ """
+ /src/com/sample/MyList.kt
+ package com.sample
+ class MyList: ArrayList<Int>()
+ """.trimIndent(),
+ configuration
+ ) {
+ lateinit var provider: ExternalDocumentablesProvider
+ pluginsSetupStage = {
+ provider = it.plugin<DokkaBase>().querySingle { externalDocumentablesProvider }
+ }
+ documentablesTransformationStage = { mod ->
+ val entry = mod.packages.single().classlikes.single().cast<DClass>().supertypes.entries.single()
+ val res = provider.findClasslike(
+ entry.value.single().typeConstructor.dri,
+ entry.key)
+ assertEquals("ArrayList", res?.name)
+ assertEquals("java.util/ArrayList///PointingToDeclaration/", res?.dri?.toString())
+
+ val supertypes = res?.cast<DClass>()?.supertypes?.values?.single()
+ ?.map { it.typeConstructor.dri.classNames }
+ assertEquals(
+ listOf("AbstractList", "RandomAccess", "Cloneable", "Serializable", "MutableList"),
+ supertypes
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `external documentable from dependency`() {
+ val coroutinesPath =
+ PathManager.getResourceRoot(Job::class.java, "/kotlinx/coroutines/Job.class")
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src")
+ analysisPlatform = "jvm"
+ classpath += listOf(jvmStdlibPath!!, coroutinesPath!!)
+ }
+ }
+ }
+
+ testInline(
+ """
+ /src/com/sample/MyJob.kt
+ package com.sample
+ import kotlinx.coroutines.Job
+ abstract class MyJob: Job
+ """.trimIndent(),
+ configuration
+ ) {
+ lateinit var provider: ExternalDocumentablesProvider
+ pluginsSetupStage = {
+ provider = it.plugin<DokkaBase>().querySingle { externalDocumentablesProvider }
+ }
+ documentablesTransformationStage = { mod ->
+ val entry = mod.packages.single().classlikes.single().cast<DClass>().supertypes.entries.single()
+ val res = provider.findClasslike(
+ entry.value.single().typeConstructor.dri,
+ entry.key)
+ assertEquals("Job", res?.name)
+ assertEquals("kotlinx.coroutines/Job///PointingToDeclaration/", res?.dri?.toString())
+
+ val supertypes = res?.cast<DInterface>()?.supertypes?.values?.single()
+ ?.map { it.typeConstructor.dri.classNames }
+ assertEquals(
+ listOf("CoroutineContext.Element"),
+ supertypes
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `external documentable for nested class`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src")
+ analysisPlatform = "jvm"
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ testInline(
+ """
+ /src/com/sample/MyList.kt
+ package com.sample
+ abstract class MyEntry: Map.Entry<Int, String>
+ """.trimIndent(),
+ configuration
+ ) {
+ lateinit var provider: ExternalDocumentablesProvider
+ pluginsSetupStage = {
+ provider = it.plugin<DokkaBase>().querySingle { externalDocumentablesProvider }
+ }
+ documentablesTransformationStage = { mod ->
+ val entry = mod.packages.single().classlikes.single().cast<DClass>().supertypes.entries.single()
+ val res = provider.findClasslike(
+ entry.value.single().typeConstructor.dri,
+ entry.key)
+ assertEquals("Entry", res?.name)
+ assertEquals("kotlin.collections/Map.Entry///PointingToDeclaration/", res?.dri?.toString())
+ }
+ }
+ }
+} \ No newline at end of file