diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-07-17 16:36:09 +0200 |
---|---|---|
committer | Paweł Marks <pmarks@virtuslab.com> | 2020-07-17 16:36:09 +0200 |
commit | 6996b1135f61c7d2cb60b0652c6a2691dda31990 (patch) | |
tree | d568096c25e31c28d14d518a63458b5a7526b896 /core/src/main/kotlin/pages | |
parent | de56cab76f556e5b4af0b8c8cb08d8b482b86d0a (diff) | |
parent | 1c3530dcbb50c347f80bef694829dbefe89eca77 (diff) | |
download | dokka-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.kt | 266 | ||||
-rw-r--r-- | core/src/main/kotlin/pages/PageNodes.kt | 181 | ||||
-rw-r--r-- | core/src/main/kotlin/pages/RendererSpecificPage.kt | 40 | ||||
-rw-r--r-- | core/src/main/kotlin/pages/contentNodeProperties.kt | 12 | ||||
-rw-r--r-- | core/src/main/kotlin/pages/utils.kt | 31 |
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 +} |