aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src
diff options
context:
space:
mode:
authorVadim Mishenev <vad-mishenev@yandex.ru>2023-08-08 17:15:51 +0300
committerGitHub <noreply@github.com>2023-08-08 17:15:51 +0300
commit2fd8e9096706545f8b77e1e66bcc876d7e29f82c (patch)
treeae26f4e51569440928692859d5ecd11ade69e587 /plugins/base/src
parent80549e1f061d1eb22b12fc9a068536655ae299f7 (diff)
downloaddokka-2fd8e9096706545f8b77e1e66bcc876d7e29f82c.tar.gz
dokka-2fd8e9096706545f8b77e1e66bcc876d7e29f82c.tar.bz2
dokka-2fd8e9096706545f8b77e1e66bcc876d7e29f82c.zip
Fix and refactor Sample Transformer (#3102)
Diffstat (limited to 'plugins/base/src')
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt16
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt17
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt113
-rw-r--r--plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt12
4 files changed, 141 insertions, 17 deletions
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt
index 6da71b4b..9907d9b7 100644
--- a/plugins/base/src/main/kotlin/DokkaBase.kt
+++ b/plugins/base/src/main/kotlin/DokkaBase.kt
@@ -1,5 +1,3 @@
-@file:Suppress("unused")
-
package org.jetbrains.dokka.base
import org.jetbrains.dokka.CoreExtensions
@@ -19,6 +17,7 @@ import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider
import org.jetbrains.dokka.base.signatures.SignatureProvider
import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer
import org.jetbrains.dokka.base.transformers.documentables.*
+import org.jetbrains.dokka.base.transformers.pages.DefaultSamplesTransformer
import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer
import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter
@@ -33,6 +32,7 @@ import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer
import org.jetbrains.dokka.transformers.pages.PageTransformer
+@Suppress("unused")
class DokkaBase : DokkaPlugin() {
val preMergeDocumentableTransformer by extensionPoint<PreMergeDocumentableTransformer>()
@@ -149,7 +149,6 @@ class DokkaBase : DokkaPlugin() {
val pageMerger by extending {
CoreExtensions.pageTransformer providing ::PageMerger order {
- // TODO [beresnev] make last() or at least after samples transformer
}
}
@@ -191,6 +190,12 @@ class DokkaBase : DokkaPlugin() {
htmlPreprocessors with RootCreator applyIf { !delayTemplateSubstitution }
}
+ val defaultSamplesTransformer by extending {
+ CoreExtensions.pageTransformer providing ::DefaultSamplesTransformer order {
+ before(pageMerger)
+ }
+ }
+
val sourceLinksTransformer by extending {
htmlPreprocessors providing ::SourceLinksTransformer order { after(rootCreator) }
}
@@ -270,11 +275,6 @@ class DokkaBase : DokkaPlugin() {
val defaultKotlinAnalysis: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.analysis.KotlinAnalysis, *, *>
get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError()
- @Suppress("DeprecatedCallableAddReplaceWith")
- @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR)
- val defaultSamplesTransformer: org.jetbrains.dokka.plugability.Extension<PageTransformer, *, *>
- get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError()
-
@Suppress("DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith")
@Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR)
val defaultExternalDocumentablesProvider: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider, *, *>
diff --git a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt
index aae2f65d..4f4cdd7c 100644
--- a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt
@@ -91,29 +91,30 @@ class DefaultTemplateModelFactory(val context: DokkaContext) : TemplateModelFact
get() = URI(this).isAbsolute
private fun Appendable.resourcesForPage(pathToRoot: String, resources: List<String>): Unit =
- resources.forEach {
+ resources.forEach { resource ->
+
val resourceHtml = with(createHTML()) {
when {
- it.URIExtension == "css" ->
+
+ resource.URIExtension == "css" ->
link(
rel = LinkRel.stylesheet,
- href = if (it.isAbsolute) it else "$pathToRoot$it"
+ href = if (resource.isAbsolute) resource else "$pathToRoot$resource"
)
- it.URIExtension == "js" ->
+ resource.URIExtension == "js" ->
script(
type = ScriptType.textJavaScript,
- src = if (it.isAbsolute) it else "$pathToRoot$it"
+ src = if (resource.isAbsolute) resource else "$pathToRoot$resource"
) {
- if (it == "scripts/main.js" || it.endsWith("_deferred.js"))
+ if (resource == "scripts/main.js" || resource.endsWith("_deferred.js"))
defer = true
else
async = true
}
- it.isImage() -> link(href = if (it.isAbsolute) it else "$pathToRoot$it")
+ resource.isImage() -> link(href = if (resource.isAbsolute) resource else "$pathToRoot$resource")
else -> null
}
}
-
if (resourceHtml != null) {
append(resourceHtml)
}
diff --git a/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt
new file mode 100644
index 00000000..0602bde2
--- /dev/null
+++ b/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt
@@ -0,0 +1,113 @@
+package org.jetbrains.dokka.base.transformers.pages
+
+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.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
+import org.jetbrains.dokka.analysis.kotlin.internal.SampleProvider
+import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory
+
+internal const val KOTLIN_PLAYGROUND_SCRIPT = "https://unpkg.com/kotlin-playground@1/dist/playground.min.js"
+
+internal class DefaultSamplesTransformer(val context: DokkaContext) : PageTransformer {
+
+ private val sampleProviderFactory: SampleProviderFactory = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { sampleProviderFactory }
+
+ override fun invoke(input: RootPageNode): RootPageNode {
+ return sampleProviderFactory.build().use { sampleProvider ->
+ input.transformContentPagesTree { page ->
+ val samples = (page as? WithDocumentables)?.documentables?.flatMap {
+ it.documentation.entries.flatMap { entry ->
+ entry.value.children.filterIsInstance<Sample>().map { entry.key to it }
+ }
+ } ?: return@transformContentPagesTree page
+
+ val newContent = samples.fold(page.content) { acc, (sampleSourceSet, sample) ->
+ sampleProvider.getSample(sampleSourceSet, sample.name)
+ ?.let {
+ acc.addSample(page, sample.name, it)
+ } ?: acc
+ }
+
+ page.modified(
+ content = newContent,
+ embeddedResources = page.embeddedResources + KOTLIN_PLAYGROUND_SCRIPT
+ )
+ }
+ }
+ }
+
+
+ private fun ContentNode.addSample(
+ contentPage: ContentPage,
+ fqLink: String,
+ sample: SampleProvider.SampleSnippet,
+ ): ContentNode {
+ val node = contentCode(contentPage.content.sourceSets, contentPage.dri, createSampleBody(sample.imports, sample.body), "kotlin")
+ return dfs(fqLink, node)
+ }
+
+ 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<ContentDivergentInstance>)
+ 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 contentCode(
+ sourceSets: Set<DisplaySourceSet>,
+ dri: Set<DRI>,
+ content: String,
+ language: String,
+ styles: Set<Style> = emptySet(),
+ extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+ ) =
+ ContentCodeBlock(
+ children = listOf(
+ ContentText(
+ text = content,
+ dci = DCI(dri, ContentKind.Sample),
+ sourceSets = sourceSets,
+ style = emptySet(),
+ extra = PropertyContainer.empty()
+ )
+ ),
+ language = language,
+ dci = DCI(dri, ContentKind.Sample),
+ sourceSets = sourceSets,
+ style = styles + ContentStyle.RunnableSample + TextStyle.Monospace,
+ extra = extra
+ )
+}
diff --git a/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt b/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
index 37009e46..9b5ab7ad 100644
--- a/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
+++ b/plugins/base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
@@ -2,12 +2,15 @@ package content.samples
import matchers.content.*
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.transformers.pages.KOTLIN_PLAYGROUND_SCRIPT
import org.jetbrains.dokka.model.DisplaySourceSet
import org.junit.jupiter.api.Test
+import utils.TestOutputWriterPlugin
import utils.classSignature
import utils.findTestType
import java.nio.file.Paths
import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
class ContentForSamplesTest : BaseAbstractTest() {
private val testDataDir = getTestDataDir("content/samples").toAbsolutePath()
@@ -61,6 +64,7 @@ class ContentForSamplesTest : BaseAbstractTest() {
@Test
fun `samples block is rendered in the description`() {
+ val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/main/kotlin/test/source.kt
@@ -70,10 +74,12 @@ class ContentForSamplesTest : BaseAbstractTest() {
| * @sample [test.sampleForClassDescription]
| */
|class Foo
- """.trimIndent(), testConfiguration
+ """.trimIndent(), testConfiguration,
+ pluginOverrides = listOf(writerPlugin)
) {
pagesTransformationStage = { module ->
val page = module.findTestType("test", "Foo")
+ assert(KOTLIN_PLAYGROUND_SCRIPT in page.embeddedResources)
page.content.assertNode {
group {
header(1) { +"Foo" }
@@ -101,6 +107,9 @@ class ContentForSamplesTest : BaseAbstractTest() {
skipAllNotMatching()
}
}
+ renderingStage = { _, _ ->
+ assertNotEquals(-1, writerPlugin.writer.contents["root/test/-foo/index.html"]?.indexOf(KOTLIN_PLAYGROUND_SCRIPT))
+ }
}
}
@@ -134,6 +143,7 @@ class ContentForSamplesTest : BaseAbstractTest() {
) {
pagesTransformationStage = { module ->
val page = module.findTestType("pageMerger", "Parent")
+ assert(KOTLIN_PLAYGROUND_SCRIPT in page.embeddedResources)
page.content.assertNode {
group {
header(1) { +"Parent" }