From 6dc9498ca849645ecb4ec923bb7116b245dca706 Mon Sep 17 00:00:00 2001 From: Błażej Kardyś Date: Wed, 27 May 2020 01:16:48 +0200 Subject: All modules page generation --- .idea/compiler.xml | 2 + core/.gitignore | 2 - core/src/main/kotlin/CoreExtensions.kt | 3 + core/src/main/kotlin/DokkaBootstrapImpl.kt | 2 +- core/src/main/kotlin/DokkaGenerator.kt | 25 ++++++ .../main/kotlin/DokkaMultimoduleBootstrapImpl.kt | 25 ++++++ core/src/main/kotlin/configuration.kt | 7 ++ core/src/main/kotlin/defaultConfiguration.kt | 8 +- core/src/main/kotlin/pages/ContentNodes.kt | 3 +- core/src/main/kotlin/pages/PageNodes.kt | 25 ++++++ core/src/main/kotlin/parsers/MarkdownParser.kt | 22 ++--- .../main/kotlin/transformers/pages/PageCreator.kt | 8 ++ plugins/.gitignore | 2 + plugins/base/src/main/kotlin/DokkaBase.kt | 7 +- .../kotlin/allModulePage/MultimodulePageCreator.kt | 65 +++++++++++++++ .../src/main/kotlin/renderers/html/HtmlRenderer.kt | 17 ++-- .../local/DefaultLocationProviderFactory.kt | 4 +- .../resolvers/local/MultimoduleLocationProvider.kt | 32 +++++++ .../documentables/PageContentBuilder.kt | 23 +++-- .../base/src/main/resources/dokka/scripts/main.js | 2 +- .../src/main/resources/dokka/scripts/main.js.map | 2 +- .../test/kotlin/renderers/RenderingOnlyTestBase.kt | 2 +- runners/cli/src/main/kotlin/cli/main.kt | 2 + .../jetbrains/dokka/gradle/DokkaMultimoduleTask.kt | 97 ++++++++++++++++++++++ .../dokka/gradle/configurationImplementations.kt | 7 ++ .../main/kotlin/org/jetbrains/dokka/gradle/main.kt | 24 +++++- runners/maven-plugin/src/main/kotlin/DokkaMojo.kt | 3 +- .../main/kotlin/testApi/testRunner/TestRunner.kt | 3 +- 28 files changed, 387 insertions(+), 37 deletions(-) create mode 100644 core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt create mode 100644 core/src/main/kotlin/transformers/pages/PageCreator.kt create mode 100644 plugins/.gitignore create mode 100644 plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt create mode 100644 plugins/base/src/main/kotlin/resolvers/local/MultimoduleLocationProvider.kt create mode 100644 runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt diff --git a/.idea/compiler.xml b/.idea/compiler.xml index a7e03ba2..b87e1115 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -45,6 +45,8 @@ + + diff --git a/core/.gitignore b/core/.gitignore index afd3caa6..1e1b410b 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,3 +1 @@ -src/main/resources/dokka/scripts/main.js -src/main/resources/dokka/scripts/main.js.map src/main/resources/dokka/scripts/*.svg \ No newline at end of file diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt index 7b4c9baa..b8689154 100644 --- a/core/src/main/kotlin/CoreExtensions.kt +++ b/core/src/main/kotlin/CoreExtensions.kt @@ -6,6 +6,7 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableMerger import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.dokka.transformers.pages.PageCreator import org.jetbrains.dokka.transformers.pages.PageTransformer import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator import kotlin.reflect.KProperty @@ -16,7 +17,9 @@ object CoreExtensions { val documentableMerger by coreExtension() val documentableTransformer by coreExtension() val documentableToPageTranslator by coreExtension() + val allModulePageCreator by coreExtension() val pageTransformer by coreExtension() + val allModulePageTransformer by coreExtension() val renderer by coreExtension() private fun coreExtension() = object { diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt index f164a3c1..56e837fc 100644 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -32,7 +32,7 @@ fun parsePerPackageOptions(arg: String): List { class DokkaBootstrapImpl : DokkaBootstrap { - private class DokkaProxyLogger(val consumer: BiConsumer) : DokkaLogger { + class DokkaProxyLogger(val consumer: BiConsumer) : DokkaLogger { override var warningsCount: Int = 0 override var errorsCount: Int = 0 diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt index 6e62c033..7f90fe9a 100644 --- a/core/src/main/kotlin/DokkaGenerator.kt +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -60,6 +60,22 @@ class DokkaGenerator( logger.report() }.dump("\n\n === TIME MEASUREMENT ===\n") + fun generateAllModulesPage() = timed { + val sourceSetsCache = SourceSetCache() + val sourceSets = emptyMap() + report("Initializing plugins") + val context = initializePlugins(configuration, logger, sourceSets, sourceSetsCache) + + report("Creating all modules page") + val pages = createAllModulePage(context) + + report("Transforming pages") + val transformedPages = transformAllModulesPage(pages, context) + + report("Rendering") + render(transformedPages, context) + }.dump("\n\n === TIME MEASUREMENT ===\n") + fun setUpAnalysis( configuration: DokkaConfiguration, sourceSetsCache: SourceSetCache @@ -101,11 +117,20 @@ class DokkaGenerator( context: DokkaContext ) = context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) + fun createAllModulePage( + context: DokkaContext + ) = context.single(CoreExtensions.allModulePageCreator).invoke() + fun transformPages( pages: RootPageNode, context: DokkaContext ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } + fun transformAllModulesPage( + pages: RootPageNode, + context: DokkaContext + ) = context[CoreExtensions.allModulePageTransformer].fold(pages) { acc, t -> t(acc) } + fun render( transformedPages: RootPageNode, context: DokkaContext diff --git a/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt b/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt new file mode 100644 index 00000000..6825ce64 --- /dev/null +++ b/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt @@ -0,0 +1,25 @@ +package org.jetbrains.dokka + +import com.google.gson.Gson +import org.jetbrains.dokka.DokkaBootstrapImpl.DokkaProxyLogger +import org.jetbrains.dokka.utilities.DokkaLogger +import java.util.function.BiConsumer + +class DokkaMultimoduleBootstrapImpl : DokkaBootstrap { + + private lateinit var generator: DokkaGenerator + + fun configure(logger: DokkaLogger, configuration: DokkaConfiguration) { + generator = DokkaGenerator(configuration, logger) + } + + override fun configure(logger: BiConsumer, serializedConfigurationJSON: String) = configure( + DokkaProxyLogger(logger), + Gson().fromJson(serializedConfigurationJSON, DokkaConfigurationImpl::class.java) + ) + + override fun generate() { + generator.generateAllModulesPage() + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/configuration.kt b/core/src/main/kotlin/configuration.kt index e74d10d7..c38d0234 100644 --- a/core/src/main/kotlin/configuration.kt +++ b/core/src/main/kotlin/configuration.kt @@ -30,6 +30,7 @@ interface DokkaConfiguration { val generateIndexPages: Boolean val cacheRoot: String? val passesConfigurations: List + val modules: List val impliedPlatforms: List val pluginsClasspath: List val pluginsConfiguration: Map @@ -73,6 +74,12 @@ interface DokkaConfiguration { val lineSuffix: String? } + interface DokkaModuleDescription { + val name: String + val path: String + val docFile: String + } + interface PackageOptions { val prefix: String val includeNonPublic: Boolean diff --git a/core/src/main/kotlin/defaultConfiguration.kt b/core/src/main/kotlin/defaultConfiguration.kt index 9f36606a..acfa55d4 100644 --- a/core/src/main/kotlin/defaultConfiguration.kt +++ b/core/src/main/kotlin/defaultConfiguration.kt @@ -11,7 +11,8 @@ data class DokkaConfigurationImpl( override val impliedPlatforms: List, override val passesConfigurations: List, override val pluginsClasspath: List, - override val pluginsConfiguration: Map + override val pluginsConfiguration: Map, + override val modules: List ) : DokkaConfiguration data class PassConfigurationImpl ( @@ -43,6 +44,11 @@ data class PassConfigurationImpl ( override val sinceKotlin: String? ) : DokkaConfiguration.PassConfiguration +data class DokkaModuleDescriptionImpl( + override val name: String, + override val path: String, + override val docFile: String +): DokkaConfiguration.DokkaModuleDescription data class SourceRootImpl( override val path: String diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt index 7b702841..95b7b371 100644 --- a/core/src/main/kotlin/pages/ContentNodes.kt +++ b/core/src/main/kotlin/pages/ContentNodes.kt @@ -46,7 +46,6 @@ data class ContentHeader( override val extra: PropertyContainer = PropertyContainer.empty() ) : ContentComposite { constructor(level: Int, c: ContentComposite) : this(c.children, level, c.dci, c.sourceSets, c.style, c.extra) - override fun withNewExtras(newExtras: PropertyContainer): ContentHeader = copy(extra = newExtras) } @@ -226,6 +225,8 @@ enum class ContentStyle : Style { object CommentTable: Style +object MultimoduleTable: Style + fun ContentNode.dfs(predicate: (ContentNode) -> Boolean): ContentNode? = if (predicate(this)) { this } else { diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index 32b2846e..b28e36d6 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -163,6 +163,31 @@ fun PageNode.asSequence(): Sequence = sequence { children.asSequence().flatMap { it.asSequence() }.forEach { yield(it) } } +class MultimoduleRootPageNode( + override val name: String, + override val dri: Set, + override val content: ContentNode, + override val embeddedResources: List = emptyList() +) : RootPageNode(), ContentPage { + + override val children: List = emptyList() + + override val documentable: Documentable? = null + + override fun modified(name: String, children: List): RootPageNode = + MultimoduleRootPageNode(name, dri, content, embeddedResources) + + override fun modified( + name: String, + content: ContentNode, + dri: Set, + embeddedResources: List, + children: List + ) = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else MultimoduleRootPageNode(name, dri, content, embeddedResources) +} + inline fun PageNode.children() = children.filterIsInstance() private infix fun List.shallowEq(other: List) = diff --git a/core/src/main/kotlin/parsers/MarkdownParser.kt b/core/src/main/kotlin/parsers/MarkdownParser.kt index 145e085c..308a8fb6 100644 --- a/core/src/main/kotlin/parsers/MarkdownParser.kt +++ b/core/src/main/kotlin/parsers/MarkdownParser.kt @@ -25,8 +25,8 @@ import java.net.MalformedURLException import org.intellij.markdown.parser.MarkdownParser as IntellijMarkdownParser class MarkdownParser( - private val resolutionFacade: DokkaResolutionFacade, - private val declarationDescriptor: DeclarationDescriptor, + private val resolutionFacade: DokkaResolutionFacade? = null, + private val declarationDescriptor: DeclarationDescriptor? = null, private val logger: DokkaLogger ) : Parser() { @@ -110,13 +110,15 @@ class MarkdownParser( null } catch (e: MalformedURLException) { try { - resolveKDocLink( - resolutionFacade.resolveSession.bindingContext, - resolutionFacade, - declarationDescriptor, - null, - link.split('.') - ).minBy { it is ClassDescriptor }?.let { DRI.from(it) } + if (resolutionFacade != null && declarationDescriptor != null) { + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + declarationDescriptor, + null, + link.split('.') + ).minBy { it is ClassDescriptor }?.let { DRI.from(it) } + } else null } catch (e1: IllegalArgumentException) { null } @@ -394,7 +396,7 @@ class MarkdownParser( parseStringToDocNode("[${it.getSubjectName()}]") .let { val link = it.children[0] - if(link is DocumentationLink) link.dri + if (link is DocumentationLink) link.dri else null } ) diff --git a/core/src/main/kotlin/transformers/pages/PageCreator.kt b/core/src/main/kotlin/transformers/pages/PageCreator.kt new file mode 100644 index 00000000..f74b5efa --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageCreator.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +interface PageCreator { + operator fun invoke(): RootPageNode +} \ No newline at end of file diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 00000000..b9e934b8 --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1,2 @@ +base/src/main/resources/dokka/scripts/main.js +base/src/main/resources/dokka/scripts/main.js.map \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index f217bbf1..c99372af 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.base import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.base.allModulePage.MultimodulePageCreator import org.jetbrains.dokka.base.renderers.* import org.jetbrains.dokka.base.renderers.html.* import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider @@ -177,5 +178,9 @@ class DokkaBase : DokkaPlugin() { htmlPreprocessors providing ::SourcesetDependencyAppender order { after(rootCreator)} } - + val allModulePageCreators by extending { + CoreExtensions.allModulePageCreator providing { + MultimodulePageCreator(it) + } + } } \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt b/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt new file mode 100644 index 00000000..ea1d8510 --- /dev/null +++ b/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt @@ -0,0 +1,65 @@ +package org.jetbrains.dokka.base.allModulePage + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.resolvers.local.MultimoduleLocationProvider.Companion.MULTIMODULE_PACKAGE_PLACEHOLDER +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.SourceSetData +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.parsers.MarkdownParser +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.transformers.pages.PageCreator +import org.jetbrains.dokka.utilities.DokkaLogger +import java.io.File + +class MultimodulePageCreator( + val context: DokkaContext +) : PageCreator { + private val logger: DokkaLogger = context.logger + + override fun invoke(): RootPageNode { + val parser = MarkdownParser(logger = logger) + val modules = context.configuration.modules + + val commentsConverter = context.plugin(DokkaBase::class)?.querySingle { commentsToContentConverter } + val signatureProvider = context.plugin(DokkaBase::class)?.querySingle { signatureProvider } + if (commentsConverter == null || signatureProvider == null) + throw IllegalStateException("Both comments converter and signature provider must not be null") + + val sourceSetData = emptySet() + val builder = PageContentBuilder(commentsConverter, signatureProvider, context.logger) + val contentNode = builder.contentFor(dri = DRI(MULTIMODULE_PACKAGE_PLACEHOLDER), kind = ContentKind.Cover, sourceSets = sourceSetData) { + header(2, "All modules:") + table(styles = setOf(MultimoduleTable)) { + modules.mapNotNull { module -> + val paragraph = module.docFile.let(::File).readText().let { parser.parse(it).firstParagraph() } + paragraph?.let { + val dri = DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = module.name) + val dci = DCI(setOf(dri), ContentKind.Main) + val header = + ContentHeader(listOf(linkNode(module.name, dri)), 2, dci, emptySet(), emptySet()) + val content = ContentGroup( + DocTagToContentConverter.buildContent(it, dci, emptySet()), + dci, + emptySet(), + emptySet() + ) + ContentGroup(listOf(header, content), dci, emptySet(), emptySet()) + } + } + } + } + return MultimoduleRootPageNode( + "Modules", + setOf(DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = "allModules")), + contentNode + ) + } + + private fun DocumentationNode.firstParagraph() = + this.children.flatMap { it.root.children }.filterIsInstance

().firstOrNull() +} \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index e73a36b2..76a52a83 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -265,7 +265,8 @@ open class HtmlRenderer( private fun FlowContent.buildRow( node: ContentGroup, pageContext: ContentPage, - sourceSetRestriction: Set? + sourceSetRestriction: Set?, + style: Set