1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package org.jetbrains.dokka.allModulesPage
import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
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.plugin
import org.jetbrains.dokka.plugability.querySingle
import org.jetbrains.dokka.transformers.pages.PageCreator
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
import java.io.File
public class MultimodulePageCreator(
private val context: DokkaContext,
) : PageCreator<AllModulesPageGeneration.DefaultAllModulesContext> {
private val commentsConverter by lazy { context.plugin<DokkaBase>().querySingle { commentsToContentConverter } }
private val signatureProvider by lazy { context.plugin<DokkaBase>().querySingle { signatureProvider } }
private val moduleDocumentationReader by lazy { context.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } }
override fun invoke(creationContext: AllModulesPageGeneration.DefaultAllModulesContext): RootPageNode {
val modules = context.configuration.modules
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
) {
getMultiModuleDocumentation(context.configuration.includes).takeIf { it.isNotEmpty() }?.let { nodes ->
group(kind = ContentKind.Cover) {
nodes.forEach { node ->
group {
node.children.forEach { comment(it.root) }
}
}
}
}
header(2, "All modules:")
table(styles = setOf(MultimoduleTable)) {
header { group { text("Name") } }
modules.filter { it.name in creationContext.nonEmptyModules }.sortedBy { it.name }
.forEach { 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))
row(setOf(dri), emptySet(), styles = emptySet(), extra = extraWithAnchor) {
+linkNode(module.name, dri, DCI(setOf(dri), ContentKind.Main), extra = extraWithAnchor)
+ContentGroup(
children =
if (displayedModuleDocumentation != null)
DocTagToContentConverter().buildContent(
displayedModuleDocumentation,
dci,
emptySet()
)
else emptyList(),
dci = dci,
sourceSets = emptySet(),
style = emptySet()
)
}
}
}
}
return MultimoduleRootPageNode(
setOf(MULTIMODULE_ROOT_DRI),
contentNode
)
}
private fun getMultiModuleDocumentation(files: Set<File>): List<DocumentationNode> =
files.map { MarkdownParser({ null }, it.absolutePath).parse(it.readText()) }
private fun getDisplayedModuleDocumentation(module: DokkaModuleDescription): P? {
return moduleDocumentationReader.read(module)?.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
}
public companion object {
public const val MULTIMODULE_PACKAGE_PLACEHOLDER: String = ".ext"
public val MULTIMODULE_ROOT_DRI: DRI =
DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = "allModules")
}
}
|