diff options
author | Andrzej Ratajczak <andrzej.ratajczak98@gmail.com> | 2020-04-10 16:59:25 +0200 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-05-13 10:03:48 +0200 |
commit | 6166ddfeb6ee977a302d4cacc80dac23cc7e2baf (patch) | |
tree | 1fb93e53c8fbd5f76cf2f1438fa0ff4cdfe3494e /plugins/base/src | |
parent | 79d1827b5d249c0a597c6c2f9cb91ff8f5689d94 (diff) | |
download | dokka-6166ddfeb6ee977a302d4cacc80dac23cc7e2baf.tar.gz dokka-6166ddfeb6ee977a302d4cacc80dac23cc7e2baf.tar.bz2 dokka-6166ddfeb6ee977a302d4cacc80dac23cc7e2baf.zip |
Working tests for includes, sources and samples. Minor bugfixes
Diffstat (limited to 'plugins/base/src')
13 files changed, 312 insertions, 48 deletions
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt index a5d6540e..b69c43d3 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt @@ -2,6 +2,8 @@ package org.jetbrains.dokka.base.transformers.documentables import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.PlatformDependent +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.dokka.parsers.MarkdownParser import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer @@ -66,15 +68,15 @@ internal object ModuleAndPackageDocumentationTransformer : PreMergeDocumentableT } }.toMap() - val packagesDocumentation = module.packages.map { - it.name to it.platformData.mapNotNull { pd -> - val doc = modulesAndPackagesDocumentation[Pair(module.name, pd)] - val facade = context.platforms[pd]?.facade - ?: return@mapNotNull null.also { context.logger.warn("Could not find platform data for ${pd.name}") } - val descriptor = facade.resolveSession.getPackageFragment(FqName(it.name)) - ?: return@mapNotNull null.also { context.logger.warn("Could not find descriptor for $") } - doc?.get("Package")?.get(it.name)?.run { - pd to MarkdownParser( + val packagesDocumentation = module.packages.map { dPackage -> + dPackage.name to dPackage.platformData.mapNotNull { platformData -> + val doc = modulesAndPackagesDocumentation[Pair(module.name, platformData)] + val facade = context.platforms[platformData]?.facade + ?: return@mapNotNull null.also { context.logger.warn("Could not find platform data for ${platformData.name}") } + val descriptor = facade.resolveSession.getPackageFragment(FqName(dPackage.name)) + ?: return@mapNotNull null.also { context.logger.warn("Could not find descriptor for ${dPackage.name}") } + doc?.get("Package")?.get(dPackage.name)?.run { + platformData to MarkdownParser( facade, descriptor, context.logger @@ -84,11 +86,12 @@ internal object ModuleAndPackageDocumentationTransformer : PreMergeDocumentableT }.toMap() module.copy( - documentation = module.documentation.let { PlatformDependent(it.map + moduleDocumentation) }, + documentation = module.documentation.let { mergeDocumentation(it.map, moduleDocumentation) }, packages = module.packages.map { - if (packagesDocumentation[it.name] != null) + val packageDocumentation = packagesDocumentation[it.name] + if (packageDocumentation != null && packageDocumentation.isNotEmpty()) it.copy(documentation = it.documentation.let { value -> - PlatformDependent(value.map + packagesDocumentation[it.name]!!) + mergeDocumentation(value.map, packageDocumentation) }) else it @@ -96,4 +99,11 @@ internal object ModuleAndPackageDocumentationTransformer : PreMergeDocumentableT ) } } + + private fun mergeDocumentation(origin: Map<PlatformData, DocumentationNode>, new: Map<PlatformData, DocumentationNode>) = PlatformDependent( + (origin.asSequence() + new.asSequence()) + .distinct() + .groupBy({ it.key }, { it.value }) + .mapValues { (_, values) -> DocumentationNode(values.flatMap { it.children }) } + ) } diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt index bd10a923..b41b07f5 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt @@ -6,6 +6,7 @@ import org.jetbrains.dokka.EnvironmentAndFacade import org.jetbrains.dokka.Platform import org.jetbrains.dokka.analysis.AnalysisEnvironment import org.jetbrains.dokka.analysis.DokkaResolutionFacade +import org.jetbrains.dokka.base.renderers.platforms import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.Sample import org.jetbrains.dokka.model.properties.PropertyContainer @@ -30,9 +31,9 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { val analysis = setUpAnalysis(context) return input.transformContentPagesTree { page -> - page.documentable?.documentation?.map?.entries?.fold(page) { acc, entry -> - entry.value.children.filterIsInstance<Sample>().fold(acc) { acc, sample -> - acc.modified(content = acc.content.addSample(page, entry.key, sample.name, analysis)) + page.documentable?.documentation?.allEntries?.fold(page) { acc, entry -> + entry.second.children.filterIsInstance<Sample>().fold(acc) { acc, sample -> + acc.modified(content = acc.content.addSample(page, entry.first, sample.name, analysis)) } } ?: page } @@ -55,19 +56,35 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { } }.toMap() - private fun ContentNode.addSample(contentPage: ContentPage, platform: PlatformData, fqName: String, analysis: Map<PlatformData, EnvironmentAndFacade>): ContentNode { - val facade = analysis[platform]?.facade ?: - return this.also { context.logger.warn("Cannot resolve facade for platform ${platform.name}")} + private fun ContentNode.addSample(contentPage: ContentPage, platform: PlatformData?, fqName: String, analysis: Map<PlatformData, EnvironmentAndFacade>): ContentNode { + val facade = if(platform == null) { + analysis.entries.find { it.key.platformType.name == "common" }?.value + } else { + analysis[platform] + }?.facade ?: return this.also { context.logger.warn("Cannot resolve facade for platform ${platform?.name ?: "expect"}") } val psiElement = fqNameToPsiElement(facade, fqName) ?: return this.also { context.logger.warn("Cannot find PsiElement corresponding to $fqName") } val imports = processImports(psiElement) // TODO: Process somehow imports. Maybe just attach them at the top of each body val body = processBody(psiElement) - val node = platformHintedContentCode(platform, contentPage.dri, body, "kotlin") - return this.safeAs<ContentGroup>()?.run { copy( - children = children.indexOfFirst { contentNode -> - contentNode.safeAs<ContentHeader>()?.children?.firstOrNull()?.safeAs<ContentText>()?.text == "Sample" - }.takeIf { it != -1 }?.let { children.apply { this.safeAs<MutableList<ContentNode>>()?.add(it+1, node) } } ?: children.also { context.logger.warn("Not found Sample block in ${contentPage.dri}")} - ) } ?: this.also { context.logger.warn("ContentPage ${contentPage.dri} cannot be cast to ContentGroup") } + val node = contentCode(contentPage.platforms(), contentPage.dri, body, "kotlin") + + return bfs(fqName, node) + } + + private fun ContentNode.bfs(fqName: String, node: ContentCode): ContentNode { + return when(this) { + is ContentHeader -> copy(children.map { it.bfs(fqName, node) }) + is ContentCode -> copy(children.map { it.bfs(fqName, node) }) + is ContentDRILink -> copy(children.map { it.bfs(fqName, node) }) + is ContentResolvedLink -> copy(children.map { it.bfs(fqName, node) }) + is ContentEmbeddedResource -> copy(children.map { it.bfs(fqName, node) }) + is ContentTable -> copy(children.map { it.bfs(fqName, node) as ContentGroup }) + is ContentList -> copy(children.map { it.bfs(fqName, node) }) + is ContentGroup -> copy(children.map { it.bfs(fqName, node) }) + is PlatformHintedContent -> copy(inner.bfs(fqName, node)) + is ContentText -> if (text == fqName) node else this + else -> this + } } private fun fqNameToPsiElement(resolutionFacade: DokkaResolutionFacade, functionName: String): PsiElement? { @@ -79,24 +96,21 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { return DescriptorToSourceUtils.descriptorToDeclaration(symbol) } - private fun platformHintedContentCode(platformData: PlatformData, dri: Set<DRI>, content: String, language: String) = - PlatformHintedContent( - inner = ContentCode( - children = listOf( - ContentText( - text = content, - dci = DCI(dri, ContentKind.BriefComment), - platforms = setOf(platformData), - style = emptySet(), - extra = PropertyContainer.empty() - ) - ), - language = language, - extra = PropertyContainer.empty(), - dci = DCI(dri, ContentKind.Source), - platforms = setOf(platformData), - style = emptySet() + private fun contentCode(platforms: List<PlatformData>, dri: Set<DRI>, content: String, language: String) = + ContentCode( + children = listOf( + ContentText( + text = content, + dci = DCI(dri, ContentKind.BriefComment), + platforms = platforms.toSet(), + style = emptySet(), + extra = PropertyContainer.empty() + ) ), - platforms = setOf(platformData) + language = language, + extra = PropertyContainer.empty(), + dci = DCI(dri, ContentKind.Source), + platforms = platforms.toSet(), + style = emptySet() ) }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index be18c592..00e3239c 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -500,12 +500,15 @@ private class DokkaDescriptorVisitor( .filter { it.kind == ClassKind.ENUM_ENTRY } .map { enumEntryDescriptor(it, parent) } - - private fun DeclarationDescriptor.resolveDescriptorData(platformData: PlatformData?): PlatformDependent<DocumentationNode> = - if (platformData != null) PlatformDependent.from( - platformData, - getDocumentation() - ) else PlatformDependent.expectFrom(getDocumentation()) + private fun DeclarationDescriptor.resolveDescriptorData(platformData: PlatformData?): PlatformDependent<DocumentationNode> { + val documentation = getDocumentation() + return if (documentation.children.isEmpty()) + PlatformDependent.empty() + else if (platformData != null) + PlatformDependent.from(platformData, documentation) + else + PlatformDependent.expectFrom(documentation) + } private fun ClassDescriptor.resolveClassDescriptionData(platformData: PlatformData?): ClassInfo { return ClassInfo( diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 424e6aa4..dd28b533 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -284,6 +284,23 @@ open class DefaultPageCreator( } } + fun DocumentableContentBuilder.contentForSamples() { + val samples = tags.withTypeNamed<Sample>() + if (samples.isNotEmpty()) { + platforms.forEach { platformData -> + val content = samples.filter { it.value.isEmpty() || platformData in it.value } + if (content.isNotEmpty()) { + group(platformData = setOf(platformData)) { + header(4, kind = ContentKind.Comment) { text("Samples") } + content.forEach { + comment(Text(it.key)) + } + } + } + } + } + } + fun DocumentableContentBuilder.contentForUnnamedTags() { val unnamedTags: List<PlatformDependent<TagWrapper>> = tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags } @@ -305,6 +322,7 @@ open class DefaultPageCreator( header(3) { text("Description") } platformDependentHint(platformData = platforms.toSet()) { contentForDescription() + contentForSamples() contentForParams() contentForUnnamedTags() contentForSeeAlso() diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt new file mode 100644 index 00000000..71854035 --- /dev/null +++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt @@ -0,0 +1,161 @@ +package linkableContent + +import org.jetbrains.dokka.SourceLinkDefinitionImpl +import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer +import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class LinkableContentTest : AbstractCoreTest() { + + @Test + fun `Include module and package documentation`() { + + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + val includesDir = getTestDataDir("linkable/includes").toAbsolutePath() + + val configuration = dokkaConfiguration { + passes { + pass { + moduleName = "example" + analysisPlatform = "js" + targets = listOf("js") + sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { + "$testDataDir/$it/kotlin" + } + includes = listOf("$includesDir/include2.md") + } + pass { + moduleName = "example" + analysisPlatform = "jvm" + targets = listOf("jvm") + sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { + "$testDataDir/$it/kotlin" + } + includes = listOf("$includesDir/include1.md") + } + } + } + + testFromData(configuration) { + documentablesMergingStage = { + Assertions.assertEquals(2, it.documentation.size) + Assertions.assertEquals(2, it.packages.size) + Assertions.assertEquals(1, it.packages.first().documentation.size) + Assertions.assertEquals(1, it.packages.last().documentation.size) + } + } + + } + + @Test + fun `Sources multiplatform class documentation`() { + + val testDataDir = getTestDataDir("linkable/sources").toAbsolutePath() + + val configuration = dokkaConfiguration { + passes { + pass { + moduleName = "example" + analysisPlatform = "js" + targets = listOf("js") + sourceRoots = listOf("$testDataDir/jsMain/kotlin") + sourceLinks = listOf( + SourceLinkDefinitionImpl( + path = "jsMain/kotlin", + url = "https://github.com/user/repo/tree/master/src/jsMain/kotlin", + lineSuffix = "#L" + ) + ) + } + pass { + moduleName = "example" + analysisPlatform = "jvm" + targets = listOf("jvm") + sourceRoots = listOf("$testDataDir/jvmMain/kotlin") + sourceLinks = listOf( + SourceLinkDefinitionImpl( + path = "jvmMain/kotlin", + url = "https://github.com/user/repo/tree/master/src/jvmMain/kotlin", + lineSuffix = "#L" + ) + ) + } + } + } + + testFromData(configuration) { + renderingStage = { rootPageNode, dokkaContext -> + val newRoot = SourceLinksTransformer(dokkaContext).invoke(rootPageNode) + + val moduleChildren = newRoot.children + Assertions.assertEquals(1, moduleChildren.size) + val packageChildren = moduleChildren.first().children + Assertions.assertEquals(2, packageChildren.size) + packageChildren.forEach { + val name = it.name.substringBefore("Class") + val crl = it.safeAs<ClasslikePageNode>()?.content?.safeAs<ContentGroup>()?.children?.last() + ?.safeAs<PlatformHintedContent>()?.children?.singleOrNull().safeAs<ContentResolvedLink>() + Assertions.assertEquals( + "https://github.com/user/repo/tree/master/src/${name.toLowerCase()}Main/kotlin/${name}Class.kt#L3", + crl?.address + ) + } + } + } + } + + @Test + fun `Samples multiplatform documentation`() { + + val testDataDir = getTestDataDir("linkable/samples").toAbsolutePath() + + val configuration = dokkaConfiguration { + passes { + pass { + moduleName = "example" + analysisPlatform = "js" + targets = listOf("js") + sourceRoots = listOf("$testDataDir/jsMain/kotlin") + samples = listOf("$testDataDir/jsMain/resources/Samples.kt") + } + pass { + moduleName = "example" + analysisPlatform = "jvm" + targets = listOf("jvm") + sourceRoots = listOf("$testDataDir/jvmMain/kotlin") + samples = listOf("$testDataDir/jvmMain/resources/Samples.kt") + } + } + } + + testFromData(configuration) { + renderingStage = { rootPageNode, dokkaContext -> + val newRoot = DefaultSamplesTransformer(dokkaContext).invoke(rootPageNode) + + val moduleChildren = newRoot.children + Assertions.assertEquals(1, moduleChildren.size) + val packageChildren = moduleChildren.first().children + Assertions.assertEquals(2, packageChildren.size) + packageChildren.forEach { + val name = it.name.substringBefore("Class") + val classChildren = it.children + Assertions.assertEquals(2, classChildren.size) + val function = classChildren.find { it.name == "printWithExclamation" } + val text = function.safeAs<MemberPageNode>()?.content?.safeAs<ContentGroup>()?.children?.last() + ?.safeAs<PlatformHintedContent>()?.children?.singleOrNull() + ?.safeAs<ContentGroup>()?.children?.singleOrNull()?.safeAs<ContentGroup>()?.children?.last() + ?.safeAs<ContentGroup>()?.children?.singleOrNull() + ?.safeAs<ContentCode>()?.children?.singleOrNull()?.safeAs<ContentText>()?.text + Assertions.assertEquals( + "${name}Class().printWithExclamation(\"Hi, $name\")", + text + ) + } + } + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/includes/include1.md b/plugins/base/src/test/resources/linkable/includes/include1.md new file mode 100644 index 00000000..03d9037d --- /dev/null +++ b/plugins/base/src/test/resources/linkable/includes/include1.md @@ -0,0 +1,7 @@ +# Module example + +This is JVM documentation for module example + +# Package example + +This is JVM documentation for package example
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/includes/include2.md b/plugins/base/src/test/resources/linkable/includes/include2.md new file mode 100644 index 00000000..1574003d --- /dev/null +++ b/plugins/base/src/test/resources/linkable/includes/include2.md @@ -0,0 +1,7 @@ +# Module example + +This is JS documentation for module example + +# Package greeteer + +This is JS documentation for package greeteer
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt b/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt new file mode 100644 index 00000000..b61ce704 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt @@ -0,0 +1,9 @@ +package p2 + +class JsClass { + + /** + * @sample samples.SamplesJs.exampleUsage + */ + fun printWithExclamation(msg: String) = println(msg + "!") +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt b/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt new file mode 100644 index 00000000..55be0ad8 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt @@ -0,0 +1,10 @@ +package samples + +import p2.JsClass + +class SamplesJs { + + fun exampleUsage() { + JsClass().printWithExclamation("Hi, Js") + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt b/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt new file mode 100644 index 00000000..960184e6 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt @@ -0,0 +1,9 @@ +package p2 + +class JvmClass { + + /** + * @sample samples.SamplesJvm.exampleUsage + */ + fun printWithExclamation(msg: String) = println(msg + "!") +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt b/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt new file mode 100644 index 00000000..69418fa9 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt @@ -0,0 +1,10 @@ +package samples + +import p2.JvmClass + +class SamplesJvm { + + fun exampleUsage() { + JvmClass().printWithExclamation("Hi, Jvm") + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt b/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt new file mode 100644 index 00000000..00dd009b --- /dev/null +++ b/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt @@ -0,0 +1,3 @@ +package p1 + +class JsClass
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt b/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt new file mode 100644 index 00000000..2113c589 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt @@ -0,0 +1,3 @@ +package p1 + +class JvmClass
\ No newline at end of file |