aboutsummaryrefslogtreecommitdiff
path: root/subprojects/analysis-kotlin-descriptors
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/analysis-kotlin-descriptors')
-rw-r--r--subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api15
-rw-r--r--subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt9
-rw-r--r--subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/KotlinSampleProvider.kt109
3 files changed, 131 insertions, 2 deletions
diff --git a/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api b/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api
index 7d53418f..470def08 100644
--- a/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api
+++ b/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api
@@ -66,3 +66,18 @@ public abstract class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/c
public final fun get (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisContext;
}
+public class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/KotlinSampleProvider : org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun close ()V
+ public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext;
+ public fun getSample (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/lang/String;)Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider$SampleSnippet;
+ protected fun processBody (Lcom/intellij/psi/PsiElement;)Ljava/lang/String;
+ protected fun processImports (Lcom/intellij/psi/PsiElement;)Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/KotlinSampleProviderFactory : org/jetbrains/dokka/analysis/kotlin/internal/SampleProviderFactory {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun build ()Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider;
+ public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext;
+}
+
diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt
index 6d63fe14..2d79affa 100644
--- a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt
+++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt
@@ -21,6 +21,7 @@ import org.jetbrains.dokka.renderers.PostAction
import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
+@Suppress("unused")
@InternalDokkaApi
class CompilerDescriptorAnalysisPlugin : DokkaPlugin() {
@@ -42,7 +43,7 @@ class CompilerDescriptorAnalysisPlugin : DokkaPlugin() {
plugin<InternalKotlinAnalysisPlugin>().documentableSourceLanguageParser providing { CompilerDocumentableSourceLanguageParser() }
}
- internal val defaultKotlinAnalysis by extending {
+ internal val defaultKotlinAnalysis by extending {
kotlinAnalysis providing { ctx ->
ProjectKotlinAnalysis(
sourceSets = ctx.configuration.sourceSets,
@@ -51,7 +52,7 @@ class CompilerDescriptorAnalysisPlugin : DokkaPlugin() {
}
}
- internal val descriptorToDocumentableTranslator by extending {
+ internal val descriptorToDocumentableTranslator by extending {
CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator
}
@@ -63,6 +64,10 @@ class CompilerDescriptorAnalysisPlugin : DokkaPlugin() {
plugin<InternalKotlinAnalysisPlugin>().fullClassHierarchyBuilder providing { DescriptorFullClassHierarchyBuilder() }
}
+ internal val kotlinSampleProviderFactory by extending {
+ plugin<InternalKotlinAnalysisPlugin>().sampleProviderFactory providing ::KotlinSampleProviderFactory
+ }
+
internal val descriptorSyntheticDocumentableDetector by extending {
plugin<InternalKotlinAnalysisPlugin>().syntheticDocumentableDetector providing { DescriptorSyntheticDocumentableDetector() }
}
diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/KotlinSampleProvider.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/KotlinSampleProvider.kt
new file mode 100644
index 00000000..73f70b9c
--- /dev/null
+++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/KotlinSampleProvider.kt
@@ -0,0 +1,109 @@
+package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl
+
+import com.intellij.psi.PsiElement
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.InternalDokkaApi
+import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin
+import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder
+import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.SamplesKotlinAnalysis
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.analysis.kotlin.internal.SampleProvider
+import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtDeclarationWithBody
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.resolve.lazy.ResolveSession
+
+class KotlinSampleProviderFactory(val context: DokkaContext): SampleProviderFactory {
+ override fun build(): SampleProvider {
+ return KotlinSampleProvider(context)
+ }
+
+}
+/**
+ * It's declared as open since StdLib has its own sample transformer
+ * with [processBody] and [processImports]
+ */
+@InternalDokkaApi
+open class KotlinSampleProvider(val context: DokkaContext): SampleProvider {
+ private val kDocFinder: KDocFinder = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kdocFinder }
+ private val analysis = lazy {
+ /**
+ * Run from the thread of [Dispatchers.Default]. It can help to avoid a memory leaks in `ThreadLocal`s (that keep `URLCLassLoader`)
+ * since we shut down Dispatchers.Default at the end of each task (see [org.jetbrains.dokka.DokkaConfiguration.finalizeCoroutines]).
+ * Currently, all `ThreadLocal`s are in a compiler/IDE codebase.
+ */
+ runBlocking(Dispatchers.Default) {
+ SamplesKotlinAnalysis(
+ sourceSets = context.configuration.sourceSets,
+ context = context,
+ projectKotlinAnalysis = context.plugin<CompilerDescriptorAnalysisPlugin>()
+ .querySingle { kotlinAnalysis }
+ )
+ }
+ }
+ protected open 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() }.minOrNull() ?: 0
+ return lines.joinToString("\n") { it.drop(indent) }
+ }
+
+ private fun processSampleBody(psiElement: PsiElement): String = when (psiElement) {
+ is KtDeclarationWithBody -> {
+ when (val bodyExpression = psiElement.bodyExpression) {
+ is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}")
+ else -> bodyExpression!!.text
+ }
+ }
+ else -> psiElement.text
+ }
+
+ protected open fun processImports(psiElement: PsiElement): String {
+ val psiFile = psiElement.containingFile
+ return when(val text = (psiFile as? KtFile)?.importList?.text) {
+ is String -> text
+ else -> ""
+ }
+ }
+
+ /**
+ * @return [SampleProvider.SampleSnippet] or null if it has not found by [fqLink]
+ */
+ override fun getSample(sourceSet: DokkaConfiguration.DokkaSourceSet, fqLink: String): SampleProvider.SampleSnippet? {
+ return runBlocking(Dispatchers.Default) {
+ val resolveSession = analysis.value[sourceSet].resolveSession
+ val psiElement = fqNameToPsiElement(resolveSession, fqLink, sourceSet)
+ ?: return@runBlocking null.also { context.logger.warn("Cannot find PsiElement corresponding to $fqLink") }
+ val imports =
+ processImports(psiElement)
+ val body = processBody(psiElement)
+ return@runBlocking SampleProvider.SampleSnippet(imports, body)
+ }
+ }
+ override fun close() {
+ if(analysis.isInitialized())
+ analysis.value.close()
+ }
+
+ private fun fqNameToPsiElement(resolveSession: ResolveSession, functionName: String, dokkaSourceSet: DokkaConfiguration.DokkaSourceSet): PsiElement? {
+ val packageName = functionName.takeWhile { it != '.' }
+ val descriptor = resolveSession.getPackageFragment(FqName(packageName))
+ ?: return null.also { context.logger.warn("Cannot find descriptor for package $packageName") }
+
+ with (kDocFinder) {
+ val symbol = resolveKDocLink(
+ descriptor,
+ functionName,
+ dokkaSourceSet,
+ emptyBindingContext = true
+ ).firstOrNull() ?: return null.also { context.logger.warn("Unresolved function $functionName in @sample") }
+ return org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration(symbol)
+ }
+ }
+} \ No newline at end of file