diff options
5 files changed, 169 insertions, 54 deletions
diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index 00a26d90..fec94537 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -37,9 +37,27 @@ data class PlatformDependent<out T>( yieldAll(map.values) } + val allEntries: Sequence<Pair<PlatformData?, T>> = sequence { + expect?.also { yield(null to it) } + map.forEach { (k, v) -> yield(k to v) } + } + + fun getOrExpect(platform: PlatformData): T? = map[platform] ?: expect + companion object { fun <T> empty(): PlatformDependent<T> = PlatformDependent(emptyMap()) + fun <T> from(platformData: PlatformData, element: T) = PlatformDependent(mapOf(platformData to element)) + + @Suppress("UNCHECKED_CAST") + fun <T> from(pairs: Iterable<Pair<PlatformData?, T>>) = + PlatformDependent( + pairs.filter { it.first != null }.toMap() as Map<PlatformData, T>, + pairs.firstOrNull { it.first == null }?.second + ) + + fun <T> from(vararg pairs: Pair<PlatformData?, T>) = from(pairs.asIterable()) + fun <T> expectFrom(element: T) = PlatformDependent(map = emptyMap(), expect = element) } } @@ -330,12 +348,18 @@ sealed class Projection sealed class Bound : Projection() data class OtherParameter(val name: String) : Bound() object Star : Projection() -data class TypeConstructor(val dri: DRI, val projections: List<Projection>, val modifier: FunctionModifiers = FunctionModifiers.NONE) : Bound() +data class TypeConstructor( + val dri: DRI, + val projections: List<Projection>, + val modifier: FunctionModifiers = FunctionModifiers.NONE +) : Bound() + data class Nullable(val inner: Bound) : Bound() data class Variance(val kind: Kind, val inner: Bound) : Projection() { enum class Kind { In, Out } } -data class PrimitiveJavaType(val name: String): Bound() + +data class PrimitiveJavaType(val name: String) : Bound() object Void : Bound() object JavaObject : Bound() @@ -376,6 +400,7 @@ sealed class JavaVisibility(name: String) : Visibility(name) { } fun <T> PlatformDependent<T>?.orEmpty(): PlatformDependent<T> = this ?: PlatformDependent.empty() + sealed class DocumentableSource(val path: String) class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index 41d4b917..177d5021 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -3,15 +3,21 @@ package org.jetbrains.dokka.base.translators.documentables import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.DFunction -import org.jetbrains.dokka.model.doc.Property -import org.jetbrains.dokka.model.doc.TagWrapper +import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.kotlin.backend.common.phaser.defaultDumper +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf + +private typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<PlatformData?, TagWrapper>>> + +private val specialTags: Set<KClass<out TagWrapper>> = + setOf(Property::class, Description::class, Constructor::class, Receiver::class, Param::class) + open class DefaultPageCreator( commentsToContentConverter: CommentsToContentConverter, @@ -51,12 +57,12 @@ open class DefaultPageCreator( open fun pageForFunction(f: DFunction) = MemberPageNode(f.name, contentForFunction(f), setOf(f.dri), f) private val WithScope.filteredFunctions - get() = functions.filter { it.extra[InheritedFunction]?.isInherited != true } + get() = functions.filter { it.extra[InheritedFunction]?.isInherited != true } protected open fun contentForModule(m: DModule) = contentBuilder.contentFor(m) { header(1) { text(m.name) } block("Packages", 2, ContentKind.Packages, m.packages, m.platformData.toSet()) { - link(it.name, it.dri) + link(it.name, it.dri) } // text("Index\n") TODO // text("Link to allpage here") @@ -123,7 +129,7 @@ open class DefaultPageCreator( header(1) { text(e.name.orEmpty()) } +buildSignature(e) - +contentForComments(e) { it !is Property } + +contentForComments(e) +contentForScope(e, e.dri, e.platformData) } @@ -135,7 +141,8 @@ open class DefaultPageCreator( +buildSignature(c) } } - +contentForComments(c) { it !is Property } + breakLine() + +contentForComments(c) if (c is WithConstructors) { block( @@ -173,33 +180,98 @@ open class DefaultPageCreator( +contentForScope(c, c.dri, c.platformData) } + @Suppress("UNCHECKED_CAST") + private inline fun <reified T: TagWrapper> GroupedTags.withTypeUnnamed(): PlatformDependent<T> = + (this[T::class] as List<Pair<PlatformData, T>>?) + ?.let { PlatformDependent.from(it) }.orEmpty() + + @Suppress("UNCHECKED_CAST") + private inline fun <reified T: NamedTagWrapper> GroupedTags.withTypeNamed(): Map<String, PlatformDependent<T>> = + (this[T::class] as List<Pair<PlatformData, T>>?) + ?.groupBy { it.second.name } + ?.mapValues { (_, v) -> PlatformDependent.from(v) } + .orEmpty() + protected open fun contentForComments( - d: Documentable, - filtering: (TagWrapper) -> Boolean = { true } - ) = contentBuilder.contentFor(d) { - d.documentation.map{(k,v) -> (k to v.children.filter(filtering).map{p -> (k to p)})}.flatMap { it.second } - .groupBy { it.second.toHeaderString() }.mapValues {(k,v) -> v.groupBy { it.first }} - .forEach{ groupedByHeader -> - header(3) { text(groupedByHeader.key) } - d.documentation.expect?.also{ - it.children.filter(filtering).filter{it.toHeaderString() == groupedByHeader.key} - .forEach { + d: Documentable + ): List<ContentNode> { + val tags: GroupedTags = d.documentation.allEntries.flatMap { (pd, doc) -> + doc.children.asSequence().map { pd to it } + }.groupBy { it.second::class } + + val platforms = d.platformData + + fun DocumentableContentBuilder.contentForDescription() { + val description = tags.withTypeUnnamed<Description>() + if (description.any { it.value.root.children.isNotEmpty() }) { + platforms.forEach { platform -> + description.getOrExpect(platform)?.also { + group(platformData = setOf(platform)) { comment(it.root) breakLine() } + } } - platformDependentHint(d.dri,groupedByHeader.value.keys){ - groupedByHeader.value.forEach{ - group(d.dri, setOf(it.key)){ - it.value.forEach { - comment(it.second.root) + } + } + + fun DocumentableContentBuilder.contentForParams() { + val receiver = tags.withTypeUnnamed<Receiver>() + val params = tags.withTypeNamed<Param>() + + if (params.isNotEmpty()) { + header(4, kind = ContentKind.Parameters) { text("Parameters") } + table(kind = ContentKind.Parameters) { + platforms.flatMap { platform -> + val receiverRow = receiver.getOrExpect(platform)?.let { + buildGroup(platformData = setOf(platform)) { + text("<receiver>") + comment(it.root) } - breakLine() } + + val paramRows = params.mapNotNull { (_, param) -> + param.getOrExpect(platform)?.let { + buildGroup(platformData = setOf(platform)) { + text(it.name) + comment(it.root) + } + } + } + + listOfNotNull(receiverRow) + paramRows } } } - }.children + } + + fun DocumentableContentBuilder.contentForUnnamedTags() { + val unnamedTags: List<PlatformDependent<TagWrapper>> = + tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags } + .map { (_, v) -> PlatformDependent.from(v) } + platforms.forEach { platform -> + unnamedTags.forEach { pdTag -> + pdTag.getOrExpect(platform)?.also { tag -> + group(platformData = setOf(platform)) { + header(4) { text(tag.toHeaderString()) } + comment(tag.root) + } + } + } + } + } + + return contentBuilder.contentFor(d) { + if (tags.isNotEmpty()) { + header(3) { text("Description") } + platformDependentHint(platformData = platforms.toSet()) { + contentForDescription() + contentForParams() + contentForUnnamedTags() + } + } + }.children + } protected open fun contentForFunction(f: DFunction) = contentBuilder.contentFor(f) { group(f.dri, f.platformData.toSet(), ContentKind.Functions) { @@ -221,4 +293,4 @@ open class DefaultPageCreator( // ?.firstOrNull() // ?.root // ?.docTagSummary() ?: "" -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index 516e5524..f77b4592 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -114,6 +114,22 @@ open class PageContentBuilder( ) } + fun table( + dri: DRI = mainDRI, + kind: Kind = ContentKind.Main, + platformData: Set<PlatformData> = mainPlatformData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + operation: DocumentableContentBuilder.() -> List<ContentGroup> + ) { + contents += ContentTable( + emptyList(), + operation(), + DCI(setOf(mainDRI), kind), + platformData, styles, extra + ) + } + fun <T : Documentable> block( name: String, level: Int, diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt index 9f8ebb00..6b86d727 100644 --- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt +++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test import utils.pWrapped import utils.signature import utils.signatureWithReceiver +import utils.unnamedTag class ContentForParamsTest : AbstractCoreTest() { private val testConfiguration = dokkaConfiguration { @@ -94,10 +95,8 @@ class ContentForParamsTest : AbstractCoreTest() { signature("function", null, "abc" to "String") header(3) { +"Description" } platformHinted { - header(4) { +"Author" } - +"Kordyjan" - header(4) { +"Since" } - +"0.11" + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } } } } @@ -129,10 +128,8 @@ class ContentForParamsTest : AbstractCoreTest() { header(3) { +"Description" } platformHinted { pWrapped("comment to function") - header(4) { +"Author" } - +"Kordyjan" - header(4) { +"Since" } - +"0.11" + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } } } } @@ -167,7 +164,7 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"abc" - +"comment to param" + group { +"comment to param" } } } } @@ -206,15 +203,15 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"first" - +"comment to first param" + group { +"comment to first param" } } group { +"second" - +"comment to second param" + group { +"comment to second param" } } group { +"third" - +"comment to third param" + group { +"comment to third param" } } } } @@ -251,15 +248,15 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"first" - +"comment to first param" + group { +"comment to first param" } } group { +"second" - +"comment to second param" + group { +"comment to second param" } } group { +"third" - +"comment to third param" + group { +"comment to third param" } } } } @@ -297,11 +294,11 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"<receiver>" - +"comment to receiver" + group { +"comment to receiver" } } group { +"abc" - +"comment to param" + group { +"comment to param" } } } } @@ -339,11 +336,11 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"first" - +"comment to first param" + group { +"comment to first param" } } group { +"third" - +"comment to third param" + group { +"comment to third param" } } } } @@ -384,21 +381,19 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"first" - +"comment to first param" + group { +"comment to first param" } } group { +"second" - +"comment to second param" + group { +"comment to second param" } } group { +"third" - +"comment to third param" + group { +"comment to third param" } } } - header(4) { +"Author" } - +"Kordyjan" - header(4) { +"Since" } - +"0.11" + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } } } } diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt index 4bb36553..1e19058a 100644 --- a/plugins/base/src/test/kotlin/utils/contentUtils.kt +++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt @@ -1,6 +1,7 @@ package utils import matchers.content.* +import org.jetbrains.dokka.pages.ContentGroup //TODO: Try to unify those functions after update to 1.4 fun ContentMatcherBuilder<*>.signature( @@ -53,4 +54,10 @@ fun ContentMatcherBuilder<*>.pWrapped(text: String) = group {// TODO: remove it when double wrapping for descriptions will be resolved group { +text } br() + } + +fun ContentMatcherBuilder<*>.unnamedTag(tag: String, content: ContentMatcherBuilder<ContentGroup>.() -> Unit) = + group { + header(4) { +tag } + group { content() } }
\ No newline at end of file |