aboutsummaryrefslogtreecommitdiff
path: root/plugins/all-module-page/src/main
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-09-16 16:36:54 +0200
committerBłażej Kardyś <bkardys@virtuslab.com>2020-11-27 03:15:02 +0100
commitc8a83153a88fe6f5b50b6f459295421f90a21583 (patch)
tree4ce52c287dde3a8303549287ce67e58174d9f9fc /plugins/all-module-page/src/main
parent585178984228a9c2e5bd2af9b675094ac6e3fa46 (diff)
downloaddokka-c8a83153a88fe6f5b50b6f459295421f90a21583.tar.gz
dokka-c8a83153a88fe6f5b50b6f459295421f90a21583.tar.bz2
dokka-c8a83153a88fe6f5b50b6f459295421f90a21583.zip
Extracting dokka generation to the dedicated extension point
Diffstat (limited to 'plugins/all-module-page/src/main')
-rw-r--r--plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt31
-rw-r--r--plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt25
-rw-r--r--plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt41
-rw-r--r--plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt100
-rw-r--r--plugins/all-module-page/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
5 files changed, 198 insertions, 0 deletions
diff --git a/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt b/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt
new file mode 100644
index 00000000..815cf160
--- /dev/null
+++ b/plugins/all-module-page/src/main/kotlin/AllModulesPageGeneration.kt
@@ -0,0 +1,31 @@
+package org.jetbrains.dokka.allModulesPage
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.Timer
+import org.jetbrains.dokka.generation.Generation
+import org.jetbrains.dokka.pages.RootPageNode
+import org.jetbrains.dokka.plugability.DokkaContext
+
+class AllModulesPageGeneration(private val context: DokkaContext) : Generation {
+ override fun Timer.generate() {
+ report("Creating all modules page")
+ val pages = createAllModulePage()
+
+ report("Transforming pages")
+ val transformedPages = transformAllModulesPage(pages)
+
+ report("Rendering")
+ render(transformedPages)
+ }
+
+ override val generationName = "index page for project"
+
+ fun createAllModulePage() = context.single(CoreExtensions.allModulePageCreator).invoke()
+
+ fun transformAllModulesPage(pages: RootPageNode) =
+ context[CoreExtensions.allModulePageTransformer].fold(pages) { acc, t -> t(acc) }
+
+ fun render(transformedPages: RootPageNode) {
+ context.single(CoreExtensions.renderer).render(transformedPages)
+ }
+} \ No newline at end of file
diff --git a/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt
new file mode 100644
index 00000000..163f13ab
--- /dev/null
+++ b/plugins/all-module-page/src/main/kotlin/AllModulesPagePlugin.kt
@@ -0,0 +1,25 @@
+package org.jetbrains.dokka.allModulesPage
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.plugability.DokkaPlugin
+
+class AllModulesPagePlugin : DokkaPlugin() {
+ val allModulePageCreators by extending {
+ (CoreExtensions.allModulePageCreator
+ providing ::MultimodulePageCreator)
+ }
+
+ val multimoduleLocationProvider by extending {
+ (plugin<DokkaBase>().locationProviderFactory
+ providing MultimoduleLocationProvider::Factory
+ override plugin<DokkaBase>().locationProvider
+ applyIf { modules.size > 1 })
+ }
+
+ val allModulesPageGeneration by extending {
+ (CoreExtensions.generation
+ providing ::AllModulesPageGeneration
+ override CoreExtensions.singleGeneration)
+ }
+} \ No newline at end of file
diff --git a/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt b/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt
new file mode 100644
index 00000000..29107136
--- /dev/null
+++ b/plugins/all-module-page/src/main/kotlin/MultimoduleLocationProvider.kt
@@ -0,0 +1,41 @@
+package org.jetbrains.dokka.allModulesPage
+
+import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider
+import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider.Companion.identifierToFilename
+import org.jetbrains.dokka.base.resolvers.local.LocationProvider
+import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.DisplaySourceSet
+import org.jetbrains.dokka.pages.PageNode
+import org.jetbrains.dokka.pages.RootPageNode
+import org.jetbrains.dokka.plugability.DokkaContext
+
+class MultimoduleLocationProvider(private val root: RootPageNode, context: DokkaContext) : LocationProvider {
+
+ private val defaultLocationProvider = DokkaLocationProvider(root, context)
+
+ val paths = context.configuration.modules.map {
+ it.name to it.relativePathToOutputDirectory
+ }.toMap()
+
+ override fun resolve(dri: DRI, sourceSets: Set<DisplaySourceSet>, context: PageNode?) =
+ dri.takeIf { it.packageName == MULTIMODULE_PACKAGE_PLACEHOLDER }?.classNames?.let { paths[it] }?.let {
+ "$it/${identifierToFilename(dri.classNames.orEmpty())}/index.html"
+ } ?: defaultLocationProvider.resolve(dri, sourceSets, context)
+
+ override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean) =
+ defaultLocationProvider.resolve(node, context, skipExtension)
+
+ override fun pathToRoot(from: PageNode): String = defaultLocationProvider.pathToRoot(from)
+
+ override fun ancestors(node: PageNode): List<PageNode> = listOf(root)
+
+ companion object {
+ const val MULTIMODULE_PACKAGE_PLACEHOLDER = ".ext"
+ }
+
+ class Factory(private val context: DokkaContext): LocationProviderFactory {
+ override fun getLocationProvider(pageNode: RootPageNode) =
+ MultimoduleLocationProvider(pageNode, context)
+ }
+} \ No newline at end of file
diff --git a/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt b/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt
new file mode 100644
index 00000000..3ad3e0ce
--- /dev/null
+++ b/plugins/all-module-page/src/main/kotlin/MultimodulePageCreator.kt
@@ -0,0 +1,100 @@
+package org.jetbrains.dokka.allModulesPage
+
+import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.Module
+import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentationParsingContext
+import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentation
+import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentationFragments
+import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
+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.doc.DocTag
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.model.doc.P
+import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.dokka.pages.*
+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
+
+class MultimodulePageCreator(
+ private val context: DokkaContext,
+) : PageCreator {
+ private val logger: DokkaLogger = context.logger
+
+ override fun invoke(): RootPageNode {
+ 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<DokkaSourceSet>()
+ 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.map { module ->
+ val displayedModuleDocumentation = getDisplayedModuleDocumentation(module)
+ val dri = DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = module.name)
+ val dci = DCI(setOf(dri), ContentKind.Comment)
+ val extraWithAnchor = PropertyContainer.withAll(SymbolAnchorHint(module.name, ContentKind.Main))
+ val header = linkNode(module.name, dri, DCI(setOf(dri), ContentKind.Main), extra = extraWithAnchor)
+ val content = ContentGroup(
+ children =
+ if (displayedModuleDocumentation != null)
+ DocTagToContentConverter().buildContent(displayedModuleDocumentation, dci, emptySet())
+ else emptyList(),
+ dci = dci,
+ sourceSets = emptySet(),
+ style = emptySet()
+ )
+ ContentGroup(listOf(header, content), dci, emptySet(), emptySet(), extraWithAnchor)
+ }
+ }
+ }
+ return MultimoduleRootPageNode(
+ "Modules",
+ setOf(DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = "allModules")),
+ contentNode
+ )
+ }
+
+ private fun getDisplayedModuleDocumentation(module: DokkaModuleDescription): P? {
+ val parsingContext = ModuleAndPackageDocumentationParsingContext(logger)
+
+ val documentationFragment = module.includes
+ .flatMap { include -> parseModuleAndPackageDocumentationFragments(include) }
+ .firstOrNull { fragment -> fragment.classifier == Module && fragment.name == module.name }
+ ?: return null
+
+ val moduleDocumentation = parseModuleAndPackageDocumentation(parsingContext, documentationFragment)
+ return moduleDocumentation.documentation.firstParagraph()
+ }
+
+ private fun DocumentationNode.firstParagraph(): P? =
+ this.children
+ .map { it.root }
+ .mapNotNull { it.firstParagraph() }
+ .firstOrNull()
+
+ /**
+ * @return The very first, most inner paragraph. If any [P] is wrapped inside another [P], the inner one
+ * is preferred.
+ */
+ private fun DocTag.firstParagraph(): P? {
+ val firstChildParagraph = children.mapNotNull { it.firstParagraph() }.firstOrNull()
+ return if (firstChildParagraph == null && this is P) this
+ else firstChildParagraph
+ }
+}
diff --git a/plugins/all-module-page/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/all-module-page/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
new file mode 100644
index 00000000..3ac59dc6
--- /dev/null
+++ b/plugins/all-module-page/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
@@ -0,0 +1 @@
+org.jetbrains.dokka.allModulesPage.AllModulesPagePlugin