From 34d6927e469c8c64e15fa41261c5f38905485fd8 Mon Sep 17 00:00:00 2001 From: Vadim Mishenev Date: Wed, 16 Aug 2023 20:59:07 +0300 Subject: Remove `DefaultSamplesTransformer` from `analysis-kotlin-descriptors` and make `kotlinSampleProviderFactory` public (#3110) * Remove `DefaultSamplesTransformer` from `analysis-kotlin-descriptors` * Make `CompilerDescriptorAnalysisPlugin.kotlinSampleProviderFactory` public --- .../compiler/api/compiler.api | 1 + .../compiler/CompilerDescriptorAnalysisPlugin.kt | 10 +- .../compiler/impl/DefaultSamplesTransformer.kt | 35 ----- .../compiler/impl/SamplesTransformerImpl.kt | 153 --------------------- 4 files changed, 7 insertions(+), 192 deletions(-) delete mode 100644 subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DefaultSamplesTransformer.kt delete mode 100644 subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/SamplesTransformerImpl.kt (limited to 'subprojects') diff --git a/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api b/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api index 470def08..7a85bc7a 100644 --- a/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api +++ b/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api @@ -10,6 +10,7 @@ public final class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/Comp public final fun getKdocFinder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getKlibService ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getKotlinAnalysis ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKotlinSampleProviderFactory ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getMockApplicationHack ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; } 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 2d79affa..805cfe86 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 @@ -56,15 +56,17 @@ class CompilerDescriptorAnalysisPlugin : DokkaPlugin() { CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator } - internal val defaultSamplesTransformer by extending { - CoreExtensions.pageTransformer providing ::DefaultSamplesTransformer - } internal val descriptorFullClassHierarchyBuilder by extending { plugin().fullClassHierarchyBuilder providing { DescriptorFullClassHierarchyBuilder() } } - internal val kotlinSampleProviderFactory by extending { + /** + * StdLib has its own a sample provider + * So it should have a possibility to override this extension + */ + @InternalDokkaApi + val kotlinSampleProviderFactory by extending { plugin().sampleProviderFactory providing ::KotlinSampleProviderFactory } diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DefaultSamplesTransformer.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DefaultSamplesTransformer.kt deleted file mode 100644 index 22fecdf8..00000000 --- a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DefaultSamplesTransformer.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl - -import com.intellij.psi.PsiElement -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtDeclarationWithBody -import org.jetbrains.kotlin.psi.KtFile - -internal class DefaultSamplesTransformer(context: DokkaContext) : SamplesTransformerImpl(context) { - - 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() }.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 - } - - override fun processImports(psiElement: PsiElement): String { - val psiFile = psiElement.containingFile - return when(val text = (psiFile as? KtFile)?.importList?.text) { - is String -> text - else -> "" - } - } -} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/SamplesTransformerImpl.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/SamplesTransformerImpl.kt deleted file mode 100644 index f1924708..00000000 --- a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/SamplesTransformerImpl.kt +++ /dev/null @@ -1,153 +0,0 @@ -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.DokkaSourceSet -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.KotlinAnalysis -import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.SamplesKotlinAnalysis -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.doc.Sample -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.pages.PageTransformer -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.lazy.ResolveSession - -internal const val KOTLIN_PLAYGROUND_SCRIPT = "" - -internal abstract class SamplesTransformerImpl(val context: DokkaContext) : PageTransformer { - - private val kDocFinder: KDocFinder = context.plugin().querySingle { kdocFinder } - - abstract fun processBody(psiElement: PsiElement): String - abstract fun processImports(psiElement: PsiElement): String - - final override fun invoke(input: RootPageNode): RootPageNode = - /** - * 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) { - val analysis = SamplesKotlinAnalysis( - sourceSets = context.configuration.sourceSets, - context = context, - projectKotlinAnalysis = context.plugin().querySingle { kotlinAnalysis } - ) - analysis.use { - input.transformContentPagesTree { page -> - val samples = (page as? WithDocumentables)?.documentables?.flatMap { - it.documentation.entries.flatMap { entry -> - entry.value.children.filterIsInstance().map { entry.key to it } - } - } - - samples?.fold(page as ContentPage) { acc, (sampleSourceSet, sample) -> - acc.modified( - content = acc.content.addSample(page, sampleSourceSet, sample.name, it), - embeddedResources = acc.embeddedResources + KOTLIN_PLAYGROUND_SCRIPT - ) - } ?: page - } - } - } - - private fun ContentNode.addSample( - contentPage: ContentPage, - sourceSet: DokkaSourceSet, - fqName: String, - analysis: KotlinAnalysis - ): ContentNode { - val resolveSession = analysis[sourceSet].resolveSession - val psiElement = fqNameToPsiElement(resolveSession, fqName, sourceSet) - ?: return this.also { context.logger.warn("Cannot find PsiElement corresponding to $fqName") } - val imports = - processImports(psiElement) - val body = processBody(psiElement) - val node = contentCode(contentPage.content.sourceSets, contentPage.dri, createSampleBody(imports, body), "kotlin") - - return dfs(fqName, node) - } - - protected open fun createSampleBody(imports: String, body: String) = - """ |$imports - |fun main() { - | //sampleStart - | $body - | //sampleEnd - |}""".trimMargin() - - private fun ContentNode.dfs(fqName: String, node: ContentCodeBlock): ContentNode { - return when (this) { - is ContentHeader -> copy(children.map { it.dfs(fqName, node) }) - is ContentDivergentGroup -> @Suppress("UNCHECKED_CAST") copy(children.map { - it.dfs(fqName, node) - } as List) - is ContentDivergentInstance -> copy( - before.let { it?.dfs(fqName, node) }, - divergent.dfs(fqName, node), - after.let { it?.dfs(fqName, node) }) - is ContentCodeBlock -> copy(children.map { it.dfs(fqName, node) }) - is ContentCodeInline -> copy(children.map { it.dfs(fqName, node) }) - is ContentDRILink -> copy(children.map { it.dfs(fqName, node) }) - is ContentResolvedLink -> copy(children.map { it.dfs(fqName, node) }) - is ContentEmbeddedResource -> copy(children.map { it.dfs(fqName, node) }) - is ContentTable -> copy(children = children.map { it.dfs(fqName, node) as ContentGroup }) - is ContentList -> copy(children.map { it.dfs(fqName, node) }) - is ContentGroup -> copy(children.map { it.dfs(fqName, node) }) - is PlatformHintedContent -> copy(inner.dfs(fqName, node)) - is ContentText -> if (text == fqName) node else this - is ContentBreakLine -> this - else -> this.also { context.logger.error("Could not recognize $this ContentNode in SamplesTransformer") } - } - } - - private fun fqNameToPsiElement(resolveSession: ResolveSession, functionName: String, dokkaSourceSet: 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 DescriptorToSourceUtils.descriptorToDeclaration(symbol) - } - } - - private fun contentCode( - sourceSets: Set, - dri: Set, - content: String, - language: String, - styles: Set