diff options
Diffstat (limited to 'plugins')
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" } |