aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/pages
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-07-17 16:36:09 +0200
committerPaweł Marks <pmarks@virtuslab.com>2020-07-17 16:36:09 +0200
commit6996b1135f61c7d2cb60b0652c6a2691dda31990 (patch)
treed568096c25e31c28d14d518a63458b5a7526b896 /core/src/main/kotlin/pages
parentde56cab76f556e5b4af0b8c8cb08d8b482b86d0a (diff)
parent1c3530dcbb50c347f80bef694829dbefe89eca77 (diff)
downloaddokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.tar.gz
dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.tar.bz2
dokka-6996b1135f61c7d2cb60b0652c6a2691dda31990.zip
Merge branch 'dev-0.11.0'
Diffstat (limited to 'core/src/main/kotlin/pages')
-rw-r--r--core/src/main/kotlin/pages/ContentNodes.kt266
-rw-r--r--core/src/main/kotlin/pages/PageNodes.kt181
-rw-r--r--core/src/main/kotlin/pages/RendererSpecificPage.kt40
-rw-r--r--core/src/main/kotlin/pages/contentNodeProperties.kt12
-rw-r--r--core/src/main/kotlin/pages/utils.kt31
5 files changed, 530 insertions, 0 deletions
diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt
new file mode 100644
index 00000000..5129dfcf
--- /dev/null
+++ b/core/src/main/kotlin/pages/ContentNodes.kt
@@ -0,0 +1,266 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.WithChildren
+import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+data class DCI(val dri: Set<DRI>, val kind: Kind) {
+ override fun toString() = "$dri[$kind]"
+}
+
+interface ContentNode : WithExtraProperties<ContentNode>, WithChildren<ContentNode> {
+ val dci: DCI
+ val sourceSets: Set<DokkaSourceSet>
+ val style: Set<Style>
+
+ fun hasAnyContent(): Boolean
+
+ override val children: List<ContentNode>
+ get() = emptyList()
+}
+
+/** Simple text */
+data class ContentText(
+ val text: String,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style> = emptySet(),
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentNode {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras)
+
+ override fun hasAnyContent(): Boolean = !text.isBlank()
+}
+
+// TODO: Remove
+data class ContentBreakLine(
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val dci: DCI = DCI(emptySet(), ContentKind.Empty),
+ override val style: Set<Style> = emptySet(),
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentNode {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras)
+
+ override fun hasAnyContent(): Boolean = true
+}
+
+/** Headers */
+data class ContentHeader(
+ override val children: List<ContentNode>,
+ val level: Int,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = 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<ContentNode>): ContentHeader = copy(extra = newExtras)
+}
+
+interface ContentCode : ContentComposite
+
+/** Code blocks */
+data class ContentCodeBlock(
+ override val children: List<ContentNode>,
+ val language: String,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentCode {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentCodeBlock = copy(extra = newExtras)
+}
+
+data class ContentCodeInline(
+ override val children: List<ContentNode>,
+ val language: String,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentCode {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentCodeInline = copy(extra = newExtras)
+}
+
+/** Union type replacement */
+interface ContentLink : ContentComposite
+
+/** All links to classes, packages, etc. that have te be resolved */
+data class ContentDRILink(
+ override val children: List<ContentNode>,
+ val address: DRI,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style> = emptySet(),
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentLink {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDRILink = copy(extra = newExtras)
+}
+
+/** All links that do not need to be resolved */
+data class ContentResolvedLink(
+ override val children: List<ContentNode>,
+ val address: String,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style> = emptySet(),
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentLink {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentResolvedLink =
+ copy(extra = newExtras)
+}
+
+/** Embedded resources like images */
+data class ContentEmbeddedResource(
+ override val children: List<ContentNode> = emptyList(),
+ val address: String,
+ val altText: String?,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style> = emptySet(),
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentLink {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentEmbeddedResource =
+ copy(extra = newExtras)
+}
+
+/** Logical grouping of [ContentNode]s */
+interface ContentComposite : ContentNode {
+ override val children: List<ContentNode> // overwrite to make it abstract once again
+
+ override fun hasAnyContent(): Boolean = children.any { it.hasAnyContent() }
+}
+
+/** Tables */
+data class ContentTable(
+ val header: List<ContentGroup>,
+ override val children: List<ContentGroup>,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentComposite {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentTable = copy(extra = newExtras)
+}
+
+/** Lists */
+data class ContentList(
+ override val children: List<ContentNode>,
+ val ordered: Boolean,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentComposite {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentList = copy(extra = newExtras)
+}
+
+/** Default group, eg. for blocks of Functions, Properties, etc. **/
+data class ContentGroup(
+ override val children: List<ContentNode>,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentComposite {
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentGroup = copy(extra = newExtras)
+}
+
+/**
+ * @property groupID is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s
+ */
+data class ContentDivergentGroup(
+ override val children: List<ContentDivergentInstance>,
+ override val dci: DCI,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode>,
+ val groupID: GroupID,
+ val implicitlySourceSetHinted: Boolean = true
+) : ContentComposite {
+ data class GroupID(val name: String)
+
+ override val sourceSets: Set<DokkaSourceSet>
+ get() = children.flatMap { it.sourceSets }.distinct().toSet()
+
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDivergentGroup =
+ copy(extra = newExtras)
+}
+
+/** Instance of a divergent content */
+data class ContentDivergentInstance(
+ val before: ContentNode?,
+ val divergent: ContentNode,
+ val after: ContentNode?,
+ override val dci: DCI,
+ override val sourceSets: Set<DokkaSourceSet>,
+ override val style: Set<Style>,
+ override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+) : ContentComposite {
+ override val children: List<ContentNode>
+ get() = listOfNotNull(before, divergent, after)
+
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDivergentInstance =
+ copy(extra = newExtras)
+}
+
+data class PlatformHintedContent(
+ val inner: ContentNode,
+ override val sourceSets: Set<DokkaSourceSet>
+) : ContentComposite {
+ override val children = listOf(inner)
+
+ override val dci: DCI
+ get() = inner.dci
+
+ override val extra: PropertyContainer<ContentNode>
+ get() = inner.extra
+
+ override val style: Set<Style>
+ get() = inner.style
+
+ override fun withNewExtras(newExtras: PropertyContainer<ContentNode>) =
+ throw UnsupportedOperationException("This method should not be called on this PlatformHintedContent")
+}
+
+interface Style
+interface Kind
+
+enum class ContentKind : Kind {
+
+ Comment, Constructors, Functions, Parameters, Properties, Classlikes, Packages, Symbol, Sample, Main, BriefComment,
+ Empty, Source, TypeAliases, Cover, Inheritors, SourceSetDependentHint, Extensions, Annotations;
+
+ companion object {
+ private val platformTagged =
+ setOf(Constructors, Functions, Properties, Classlikes, Packages, Source, TypeAliases, Inheritors, Extensions)
+
+ fun shouldBePlatformTagged(kind: Kind): Boolean = kind in platformTagged
+ }
+}
+
+enum class TextStyle : Style {
+ Bold, Italic, Strong, Strikethrough, Paragraph, Block, Span, Monospace, Indented, Cover, UnderCoverText, BreakableAfter, Breakable
+}
+
+enum class ContentStyle : Style {
+ RowTitle, TabbedContent, WithExtraAttributes, RunnableSample, InDocumentationAnchor
+}
+
+object CommentTable : Style
+
+object MultimoduleTable : Style
+
+fun ContentNode.dfs(predicate: (ContentNode) -> Boolean): ContentNode? = if (predicate(this)) {
+ this
+} else {
+ if (this is ContentComposite) {
+ this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull()
+ } else {
+ null
+ }
+}
+
+fun ContentNode.hasStyle(style: Style) = this.style.contains(style)
diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt
new file mode 100644
index 00000000..71ec8597
--- /dev/null
+++ b/core/src/main/kotlin/pages/PageNodes.kt
@@ -0,0 +1,181 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.WithChildren
+import java.util.*
+
+interface PageNode: WithChildren<PageNode> {
+ val name: String
+
+ fun modified(
+ name: String = this.name,
+ children: List<PageNode> = this.children
+ ): PageNode
+}
+
+interface ContentPage: PageNode {
+ val content: ContentNode
+ val dri: Set<DRI>
+ val documentable: Documentable?
+ val embeddedResources: List<String>
+
+ fun modified(
+ name: String = this.name,
+ content: ContentNode = this.content,
+ dri: Set<DRI> = this.dri,
+ embeddedResources: List<String> = this.embeddedResources,
+ children: List<PageNode> = this.children
+ ): ContentPage
+}
+
+abstract class RootPageNode: PageNode {
+ val parentMap: Map<PageNode, PageNode> by lazy {
+ IdentityHashMap<PageNode, PageNode>().apply {
+ fun process(parent: PageNode) {
+ parent.children.forEach { child ->
+ put(child, parent)
+ process(child)
+ }
+ }
+ process(this@RootPageNode)
+ }
+ }
+
+ fun transformPageNodeTree(operation: (PageNode) -> PageNode) =
+ this.transformNode(operation) as RootPageNode
+
+ fun transformContentPagesTree(operation: (ContentPage) -> ContentPage) = transformPageNodeTree {
+ if (it is ContentPage) operation(it) else it
+ }
+
+ private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode =
+ operation(this).let { newNode ->
+ newNode.modified(children = newNode.children.map { it.transformNode(operation) })
+ }
+
+ abstract override fun modified(
+ name: String,
+ children: List<PageNode>
+ ): RootPageNode
+}
+
+class ModulePageNode(
+ override val name: String,
+ override val content: ContentNode,
+ override val documentable: Documentable?,
+ override val children: List<PageNode>,
+ override val embeddedResources: List<String> = listOf()
+) : RootPageNode(), ContentPage {
+ override val dri: Set<DRI> = setOf(DRI.topLevel)
+
+ override fun modified(name: String, children: List<PageNode>): ModulePageNode =
+ modified(name = name, content = this.content, dri = dri, children = children)
+
+ override fun modified(
+ name: String,
+ content: ContentNode,
+ dri: Set<DRI>,
+ embeddedResources: List<String>,
+ children: List<PageNode>
+ ): ModulePageNode =
+ if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
+ else ModulePageNode(name, content, documentable, children, embeddedResources)
+}
+
+class PackagePageNode(
+ override val name: String,
+ override val content: ContentNode,
+ override val dri: Set<DRI>,
+ override val documentable: Documentable?,
+ override val children: List<PageNode>,
+ override val embeddedResources: List<String> = listOf()
+) : ContentPage {
+ override fun modified(name: String, children: List<PageNode>): PackagePageNode =
+ modified(name = name, content = this.content, children = children)
+
+ override fun modified(
+ name: String,
+ content: ContentNode,
+ dri: Set<DRI>,
+ embeddedResources: List<String>,
+ children: List<PageNode>
+ ): PackagePageNode =
+ if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
+ else PackagePageNode(name, content, dri, documentable, children, embeddedResources)
+}
+
+class ClasslikePageNode(
+ override val name: String,
+ override val content: ContentNode,
+ override val dri: Set<DRI>,
+ override val documentable: Documentable?,
+ override val children: List<PageNode>,
+ override val embeddedResources: List<String> = listOf()
+) : ContentPage {
+ override fun modified(name: String, children: List<PageNode>): ClasslikePageNode =
+ modified(name = name, content = this.content, children = children)
+
+ override fun modified(
+ name: String,
+ content: ContentNode,
+ dri: Set<DRI>,
+ embeddedResources: List<String>,
+ children: List<PageNode>
+ ): ClasslikePageNode =
+ if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
+ else ClasslikePageNode(name, content, dri, documentable, children, embeddedResources)
+}
+
+class MemberPageNode(
+ override val name: String,
+ override val content: ContentNode,
+ override val dri: Set<DRI>,
+ override val documentable: Documentable?,
+ override val children: List<PageNode> = emptyList(),
+ override val embeddedResources: List<String> = listOf()
+) : ContentPage {
+ override fun modified(name: String, children: List<PageNode>): MemberPageNode =
+ modified(name = name, content = this.content, children = children) as MemberPageNode
+
+ override fun modified(
+ name: String,
+ content: ContentNode,
+ dri: Set<DRI>,
+ embeddedResources: List<String>,
+ children: List<PageNode>
+ ): MemberPageNode =
+ if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
+ else MemberPageNode(name, content, dri, documentable, children, embeddedResources)
+}
+
+
+class MultimoduleRootPageNode(
+ override val name: String,
+ override val dri: Set<DRI>,
+ override val content: ContentNode,
+ override val embeddedResources: List<String> = emptyList()
+) : RootPageNode(), ContentPage {
+
+ override val children: List<PageNode> = emptyList()
+
+ override val documentable: Documentable? = null
+
+ override fun modified(name: String, children: List<PageNode>): RootPageNode =
+ MultimoduleRootPageNode(name, dri, content, embeddedResources)
+
+ override fun modified(
+ name: String,
+ content: ContentNode,
+ dri: Set<DRI>,
+ embeddedResources: List<String>,
+ children: List<PageNode>
+ ) =
+ if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this
+ else MultimoduleRootPageNode(name, dri, content, embeddedResources)
+}
+
+inline fun <reified T: PageNode> PageNode.children() = children.filterIsInstance<T>()
+
+private infix fun <T> List<T>.shallowEq(other: List<T>) =
+ this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b })
diff --git a/core/src/main/kotlin/pages/RendererSpecificPage.kt b/core/src/main/kotlin/pages/RendererSpecificPage.kt
new file mode 100644
index 00000000..85e6d530
--- /dev/null
+++ b/core/src/main/kotlin/pages/RendererSpecificPage.kt
@@ -0,0 +1,40 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.renderers.Renderer
+import kotlin.reflect.KClass
+
+interface RendererSpecificPage : PageNode {
+ val strategy: RenderingStrategy
+}
+
+class RendererSpecificRootPage(
+ override val name: String,
+ override val children: List<PageNode>,
+ override val strategy: RenderingStrategy
+) : RootPageNode(), RendererSpecificPage {
+ override fun modified(name: String, children: List<PageNode>): RendererSpecificRootPage =
+ RendererSpecificRootPage(name, children, strategy)
+}
+
+class RendererSpecificResourcePage(
+ override val name: String,
+ override val children: List<PageNode>,
+ override val strategy: RenderingStrategy
+): RendererSpecificPage {
+ override fun modified(name: String, children: List<PageNode>): RendererSpecificResourcePage =
+ RendererSpecificResourcePage(name, children, strategy)
+}
+
+sealed class RenderingStrategy {
+ class Callback(val instructions: Renderer.(PageNode) -> String): RenderingStrategy()
+ data class Copy(val from: String) : RenderingStrategy()
+ data class Write(val text: String) : RenderingStrategy()
+ object DoNothing : RenderingStrategy()
+
+ companion object {
+ inline operator fun <reified T: Renderer> invoke(crossinline instructions: T.(PageNode) -> String) =
+ Callback { if (this is T) instructions(it) else throw WrongRendererTypeException(T::class) }
+ }
+}
+
+data class WrongRendererTypeException(val expectedType: KClass<*>): Exception() \ No newline at end of file
diff --git a/core/src/main/kotlin/pages/contentNodeProperties.kt b/core/src/main/kotlin/pages/contentNodeProperties.kt
new file mode 100644
index 00000000..67acef6d
--- /dev/null
+++ b/core/src/main/kotlin/pages/contentNodeProperties.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.dokka.pages
+
+import org.jetbrains.dokka.model.properties.ExtraProperty
+
+class SimpleAttr(val extraKey: String, val extraValue: String) : ExtraProperty<ContentNode> {
+ data class SimpleAttrKey(val key: String) : ExtraProperty.Key<ContentNode, SimpleAttr>
+ override val key: ExtraProperty.Key<ContentNode, SimpleAttr> = SimpleAttrKey(extraKey)
+
+ companion object {
+ fun header(value: String) = SimpleAttr("data-togglable", value)
+ }
+}
diff --git a/core/src/main/kotlin/pages/utils.kt b/core/src/main/kotlin/pages/utils.kt
new file mode 100644
index 00000000..c9039416
--- /dev/null
+++ b/core/src/main/kotlin/pages/utils.kt
@@ -0,0 +1,31 @@
+package org.jetbrains.dokka.pages
+
+import kotlin.reflect.KClass
+
+inline fun <reified T : ContentNode, R : ContentNode> R.mapTransform(noinline operation: (T) -> T): R =
+ mapTransform(T::class, operation)
+
+@PublishedApi
+@Suppress("UNCHECKED_CAST")
+internal fun <T : ContentNode, R : ContentNode> R.mapTransform(type: KClass<T>, operation: (T) -> T): R {
+ if (this::class == type) {
+ return operation(this as T) as R
+ }
+ val new = when (this) {
+ is ContentGroup -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentHeader -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentCodeBlock -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentCodeInline -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentTable -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentList -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentDivergentGroup -> this.copy(children.map { it.mapTransform(type, operation) })
+ is ContentDivergentInstance -> this.copy(
+ before = before?.mapTransform(type, operation),
+ divergent = divergent.mapTransform(type, operation),
+ after = after?.mapTransform(type, operation)
+ )
+ is PlatformHintedContent -> this.copy(inner.mapTransform(type, operation))
+ else -> this
+ }
+ return new as R
+}