aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/translators
diff options
context:
space:
mode:
authorPaweł Marks <pmarks@virtuslab.com>2020-02-17 15:05:03 +0100
committerPaweł Marks <Kordyjan@users.noreply.github.com>2020-02-18 13:28:23 +0100
commit6a4eda715f59106530f5f6078ff2c93e49079ac6 (patch)
treeed0fcd48ac8e7e562e4befe845116848a04037fe /plugins/base/src/main/kotlin/translators
parent4dafa915b87f6cc0dc9d47029d61563ed1977c67 (diff)
downloaddokka-6a4eda715f59106530f5f6078ff2c93e49079ac6.tar.gz
dokka-6a4eda715f59106530f5f6078ff2c93e49079ac6.tar.bz2
dokka-6a4eda715f59106530f5f6078ff2c93e49079ac6.zip
Translators moved to separate packages
also typo fixed and unnecessary dependencies removed
Diffstat (limited to 'plugins/base/src/main/kotlin/translators')
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentationTranslator.kt335
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentablesToPageTranslator.kt25
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/PageBuilder.kt159
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt218
4 files changed, 737 insertions, 0 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentationTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentationTranslator.kt
new file mode 100644
index 00000000..bb572034
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentationTranslator.kt
@@ -0,0 +1,335 @@
+package org.jetbrains.dokka.base.translators.descriptors
+
+import org.jetbrains.dokka.analysis.DokkaResolutionFacade
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.withClass
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.Enum
+import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.model.Property
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.pages.PlatformData
+import org.jetbrains.dokka.parsers.MarkdownParser
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.transformers.descriptors.DescriptorToDocumentationTranslator
+import org.jetbrains.kotlin.descriptors.*
+import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
+import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
+import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.MemberScope
+import kotlin.reflect.KClass
+
+class DefaultDescriptorToDocumentationTranslator(
+ private val context: DokkaContext
+) : DescriptorToDocumentationTranslator {
+ override fun invoke(
+ moduleName: String,
+ packageFragments: Iterable<PackageFragmentDescriptor>,
+ platformData: PlatformData
+ ) = DokkaDescriptorVisitor(platformData, context.platforms[platformData]?.facade!!).run {
+ packageFragments.map {
+ visitPackageFragmentDescriptor(
+ it,
+ DRIWithPlatformInfo(DRI.topLevel, null, emptyList())
+ )
+ }
+ }.let { Module(moduleName, it) }
+
+}
+
+
+data class DRIWithPlatformInfo(
+ val dri: DRI,
+ val expected: PlatformInfo?,
+ val actual: List<PlatformInfo>
+)
+
+fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, null, emptyList())
+
+open class DokkaDescriptorVisitor( // TODO: close this class and make it private together with DRIWithPlatformInfo
+ private val platformData: PlatformData,
+ private val resolutionFacade: DokkaResolutionFacade
+) : DeclarationDescriptorVisitorEmptyBodies<Documentable, DRIWithPlatformInfo>() {
+ override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor, parent: DRIWithPlatformInfo): Nothing {
+ throw IllegalStateException("${javaClass.simpleName} should never enter ${descriptor.javaClass.simpleName}")
+ }
+
+ override fun visitPackageFragmentDescriptor(
+ descriptor: PackageFragmentDescriptor,
+ parent: DRIWithPlatformInfo
+ ): Package {
+ val driWithPlatform = DRI(packageName = descriptor.fqName.asString()).withEmptyInfo()
+ val scope = descriptor.getMemberScope()
+
+ return Package(
+ dri = driWithPlatform.dri,
+ functions = scope.functions(driWithPlatform),
+ properties = scope.properties(driWithPlatform),
+ classlikes = scope.classlikes(driWithPlatform)
+ )
+ }
+
+ override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Classlike =
+ when (descriptor.kind) {
+ org.jetbrains.kotlin.descriptors.ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent)
+ else -> classDescriptor(descriptor, parent)
+ }
+
+ fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Enum {
+ val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo()
+ val scope = descriptor.unsubstitutedMemberScope
+ val descriptorData = descriptor.takeUnless { it.isExpect }?.resolveClassDescriptionData()
+
+ return Enum(
+ dri = driWithPlatform.dri,
+ name = descriptor.name.asString(),
+ entries = scope.classlikes(driWithPlatform).filter { it.kind == KotlinClassKindTypes.ENUM_ENTRY }.map {
+ EnumEntry(
+ it
+ )
+ },
+ constructors = descriptor.constructors.map { visitConstructorDescriptor(it, driWithPlatform) },
+ functions = scope.functions(driWithPlatform),
+ properties = scope.properties(driWithPlatform),
+ classlikes = scope.classlikes(driWithPlatform),
+ expected = descriptor.takeIf { it.isExpect }?.resolveClassDescriptionData(),
+ actual = listOfNotNull(descriptorData),
+ extra = mutableSetOf(), // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet()
+ visibility = mapOf(platformData to descriptor.visibility)
+ )
+ }
+
+ fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Class {
+ val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo()
+ val scope = descriptor.unsubstitutedMemberScope
+ val descriptorData = descriptor.takeUnless { it.isExpect }?.resolveClassDescriptionData()
+ val expected = descriptor.takeIf { it.isExpect }?.resolveClassDescriptionData()
+ val actual = listOfNotNull(descriptorData)
+ return Class(
+ dri = driWithPlatform.dri,
+ name = descriptor.name.asString(),
+ kind = KotlinClassKindTypes.valueOf(descriptor.kind.toString()),
+ constructors = descriptor.constructors.map {
+ visitConstructorDescriptor(
+ it,
+ if (it.isPrimary)
+ DRIWithPlatformInfo(
+ driWithPlatform.dri,
+ expected?.info.filterTagWrappers(listOf(Constructor::class)),
+ actual.filterTagWrappers(listOf(Constructor::class))
+ )
+ else
+ DRIWithPlatformInfo(driWithPlatform.dri, null, emptyList())
+ )
+ },
+ functions = scope.functions(driWithPlatform),
+ properties = scope.properties(driWithPlatform),
+ classlikes = scope.classlikes(driWithPlatform),
+ expected = expected,
+ actual = actual,
+ extra = mutableSetOf(), // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet()
+ visibility = mapOf(platformData to descriptor.visibility)
+ )
+ }
+
+ override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): Property {
+ val expected = descriptor.takeIf { it.isExpect }?.resolveDescriptorData()
+ val actual = listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData())
+ val dri = parent.dri.copy(callable = Callable.from(descriptor))
+
+ return Property(
+ dri = dri,
+ name = descriptor.name.asString(),
+ receiver = descriptor.extensionReceiverParameter?.let {
+ visitReceiverParameterDescriptor(
+ it,
+ DRIWithPlatformInfo(
+ dri,
+ expected?.filterTagWrappers(listOf(Receiver::class)),
+ actual.filterTagWrappers(listOf(Receiver::class))
+ )
+ )
+ },
+ expected = expected,
+ actual = actual,
+ accessors = descriptor.accessors.map { visitPropertyAccessorDescriptor(it, descriptor, dri) },
+ visibility = mapOf(platformData to descriptor.visibility)
+ )
+ }
+
+ override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): Function {
+ val expected = descriptor.takeIf { it.isExpect }?.resolveDescriptorData()
+ val actual = listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData())
+ val dri = parent.dri.copy(callable = Callable.from(descriptor))
+
+ return Function(
+ dri = dri,
+ name = descriptor.name.asString(),
+ returnType = descriptor.returnType?.let { KotlinTypeWrapper(it) },
+ isConstructor = false,
+ receiver = descriptor.extensionReceiverParameter?.let {
+ visitReceiverParameterDescriptor(
+ it,
+ DRIWithPlatformInfo(
+ dri,
+ expected?.filterTagWrappers(listOf(Receiver::class)),
+ actual.filterTagWrappers(listOf(Receiver::class))
+ )
+ )
+ },
+ parameters = descriptor.valueParameters.mapIndexed { index, desc ->
+ parameter(
+ index, desc,
+ DRIWithPlatformInfo(
+ dri,
+ expected.filterTagWrappers(listOf(Param::class), desc.name.asString()),
+ actual.filterTagWrappers(listOf(Param::class), desc.name.asString())
+ )
+ )
+ },
+ expected = expected,
+ actual = actual,
+ visibility = mapOf(platformData to descriptor.visibility)
+ )
+ }
+
+ override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): Function {
+ val dri = parent.dri.copy(callable = Callable.from(descriptor))
+ return Function(
+ dri = dri,
+ name = "<init>",
+ returnType = KotlinTypeWrapper(descriptor.returnType),
+ isConstructor = true,
+ receiver = null,
+ parameters = descriptor.valueParameters.mapIndexed { index, desc ->
+ parameter(
+ index, desc,
+ DRIWithPlatformInfo(
+ dri,
+ parent.expected.filterTagWrappers(listOf(Param::class)),
+ parent.actual.filterTagWrappers(listOf(Param::class))
+ )
+ )
+ },
+ expected = parent.expected ?: descriptor.takeIf { it.isExpect }?.resolveDescriptorData(),
+ actual = parent.actual,
+ visibility = mapOf(platformData to descriptor.visibility)
+ )
+ }
+
+ override fun visitReceiverParameterDescriptor(
+ descriptor: ReceiverParameterDescriptor,
+ parent: DRIWithPlatformInfo
+ ) = Parameter(
+ dri = parent.dri.copy(target = 0),
+ name = null,
+ type = KotlinTypeWrapper(descriptor.type),
+ expected = parent.expected,
+ actual = parent.actual
+ )
+
+ open fun visitPropertyAccessorDescriptor(
+ descriptor: PropertyAccessorDescriptor,
+ propertyDescriptor: PropertyDescriptor,
+ parent: DRI
+ ): Function {
+ val dri = parent.copy(callable = Callable.from(descriptor))
+ val isGetter = descriptor is PropertyGetterDescriptor
+
+ fun PropertyDescriptor.asParameter(parent: DRI) =
+ Parameter(
+ parent.copy(target = 1),
+ this.name.asString(),
+ KotlinTypeWrapper(this.type),
+ descriptor.takeIf { it.isExpect }?.resolveDescriptorData(),
+ listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData())
+ )
+
+ val name = run {
+ val modifier = if (isGetter) "get" else "set"
+ val rawName = propertyDescriptor.name.asString()
+ "$modifier${rawName[0].toUpperCase()}${rawName.drop(1)}"
+ }
+
+ descriptor.visibility
+ val parameters =
+ if (isGetter) {
+ emptyList()
+ } else {
+ listOf(propertyDescriptor.asParameter(dri))
+ }
+
+ return Function(
+ dri,
+ name,
+ descriptor.returnType?.let { KotlinTypeWrapper(it) },
+ false,
+ null,
+ parameters,
+ descriptor.takeIf { it.isExpect }?.resolveDescriptorData(),
+ listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()),
+ visibility = mapOf(platformData to descriptor.visibility)
+ )
+ }
+
+ private fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) =
+ Parameter(
+ dri = parent.dri.copy(target = index + 1),
+ name = descriptor.name.asString(),
+ type = KotlinTypeWrapper(descriptor.type),
+ expected = parent.expected,
+ actual = parent.actual
+ )
+
+ private fun MemberScope.functions(parent: DRIWithPlatformInfo): List<Function> =
+ getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) { true }
+ .filterIsInstance<FunctionDescriptor>()
+ .map { visitFunctionDescriptor(it, parent) }
+
+ private fun MemberScope.properties(parent: DRIWithPlatformInfo): List<Property> =
+ getContributedDescriptors(DescriptorKindFilter.VALUES) { true }
+ .filterIsInstance<PropertyDescriptor>()
+ .map { visitPropertyDescriptor(it, parent) }
+
+ private fun MemberScope.classlikes(parent: DRIWithPlatformInfo): List<Classlike> =
+ getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) { true }
+ .filterIsInstance<ClassDescriptor>()
+ .map { visitClassDescriptor(it, parent) }
+
+ private fun DeclarationDescriptor.resolveDescriptorData(): PlatformInfo {
+ val doc = findKDoc()
+ val parser = MarkdownParser(resolutionFacade, this)
+ val docHeader = parser.parseFromKDocTag(doc)
+
+ return BasePlatformInfo(docHeader, listOf(platformData))
+ }
+
+ private fun ClassDescriptor.resolveClassDescriptionData(): ClassPlatformInfo {
+ return ClassPlatformInfo(resolveDescriptorData(),
+ (getSuperInterfaces() + getAllSuperclassesWithoutAny()).map { DRI.from(it) })
+ }
+
+ private fun PlatformInfo?.filterTagWrappers(
+ types: List<KClass<out TagWrapper>>,
+ name: String? = null
+ ): PlatformInfo? {
+ if (this == null)
+ return null
+ return BasePlatformInfo(
+ DocumentationNode(
+ this.documentationNode.children.filter { it::class in types && (it as? NamedTagWrapper)?.name == name }
+ ),
+ this.platformData
+ )
+ }
+
+ private fun List<PlatformInfo>.filterTagWrappers(
+ types: List<KClass<out TagWrapper>>,
+ name: String? = null
+ ): List<PlatformInfo> =
+ this.map { it.filterTagWrappers(types, name)!! }
+}
+
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentablesToPageTranslator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentablesToPageTranslator.kt
new file mode 100644
index 00000000..ee3ae995
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentablesToPageTranslator.kt
@@ -0,0 +1,25 @@
+package org.jetbrains.dokka.base.translators.documentables
+
+import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
+import org.jetbrains.dokka.model.Module
+import org.jetbrains.dokka.pages.ModulePageNode
+import org.jetbrains.dokka.transformers.documentation.DocumentablesToPageTranslator
+import org.jetbrains.dokka.utilities.DokkaLogger
+
+
+class DefaultDocumentablesToPageTranslator(
+ private val commentsToContentConverter: CommentsToContentConverter,
+ private val logger: DokkaLogger
+) : DocumentablesToPageTranslator {
+ override fun invoke(module: Module): ModulePageNode =
+ DefaultPageBuilder { node, kind, operation ->
+ DefaultPageContentBuilder.group(
+ setOf(node.dri),
+ node.platformData,
+ kind,
+ commentsToContentConverter,
+ logger,
+ operation
+ )
+ }.pageForModule(module)
+} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageBuilder.kt
new file mode 100644
index 00000000..82419a77
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/documentables/PageBuilder.kt
@@ -0,0 +1,159 @@
+package org.jetbrains.dokka.base.translators.documentables
+
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.Enum
+import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.model.doc.TagWrapper
+import org.jetbrains.dokka.pages.*
+
+open class DefaultPageBuilder(
+ override val rootContentGroup: RootContentBuilder
+) : PageBuilder {
+
+ override fun pageForModule(m: Module): ModulePageNode =
+ ModulePageNode(m.name.ifEmpty { "root" }, contentForModule(m), m, m.packages.map { pageForPackage(it) })
+
+ override fun pageForPackage(p: Package) =
+ PackagePageNode(p.name, contentForPackage(p), setOf(p.dri), p,
+ p.classlikes.map { pageForClasslike(it) } +
+ p.functions.map { pageForMember(it) })
+
+ override fun pageForClasslike(c: Classlike): ClasslikePageNode {
+ val constructors = when (c) {
+ is Class -> c.constructors
+ is Enum -> c.constructors
+ else -> emptyList()
+ }
+
+ return ClasslikePageNode(c.name, contentForClasslike(c), setOf(c.dri), c,
+ constructors.map { pageForMember(it) } +
+ c.classlikes.map { pageForClasslike(it) } +
+ c.functions.map { pageForMember(it) })
+ }
+
+ override fun pageForMember(m: CallableNode): MemberPageNode =
+ when (m) {
+ is Function ->
+ MemberPageNode(m.name, contentForFunction(m), setOf(m.dri), m)
+ else -> throw IllegalStateException("$m should not be present here")
+ }
+
+ protected open fun group(node: Documentable, content: PageContentBuilderFunction) =
+ rootContentGroup(node, ContentKind.Main, content)
+
+ protected open fun contentForModule(m: Module) = group(m) {
+ header(1) { text("root") }
+ block("Packages", 2, ContentKind.Packages, m.packages, m.platformData) {
+ link(it.name, it.dri)
+ }
+ text("Index\n")
+ text("Link to allpage here")
+ }
+
+ protected open fun contentForPackage(p: Package) = group(p) {
+ header(1) { text("Package ${p.name}") }
+ block("Types", 2, ContentKind.Properties, p.classlikes, p.platformData) {
+ link(it.name, it.dri)
+ text(it.briefDocTagString)
+ }
+ block("Functions", 2, ContentKind.Functions, p.functions, p.platformData) {
+ link(it.name, it.dri)
+ signature(it)
+ text(it.briefDocTagString)
+ }
+ }
+
+ open fun contentForClasslike(c: Classlike): ContentGroup = when (c) {
+ is Class -> contentForClass(c)
+ is Enum -> contentForEnum(c)
+ else -> throw IllegalStateException("$c should not be present here")
+ }
+
+ protected fun contentForClass(c: Class) = group(c) {
+ header(1) { text(c.name) }
+
+ c.inherited.takeIf { it.isNotEmpty() }?.let {
+ header(2) { text("SuperInterfaces") }
+ linkTable(it)
+ }
+ contentForComments(c)
+ block("Constructors", 2, ContentKind.Functions, c.constructors, c.platformData) {
+ link(it.name, it.dri)
+ signature(it)
+ text(it.briefDocTagString)
+ }
+ block("Functions", 2, ContentKind.Functions, c.functions, c.platformData) {
+ link(it.name, it.dri)
+ signature(it)
+ text(it.briefDocTagString)
+ }
+ block("Properties", 2, ContentKind.Properties, c.properties, c.platformData) {
+ link(it.name, it.dri)
+ text(it.briefDocTagString)
+
+ }
+ }
+
+ fun contentForEnum(c: Enum): ContentGroup = group(c) {
+ header(1) { text("enum ${c.name}") }
+
+ block("Entries", 2, ContentKind.Properties, c.entries, c.platformData) { entry ->
+ link(entry.name, entry.dri)
+ contentForComments(entry)
+ }
+
+ c.inherited.takeIf { it.isNotEmpty() }?.let {
+ header(2) { text("SuperInterfaces") }
+ linkTable(it)
+ }
+ contentForComments(c)
+ block("Constructors", 2, ContentKind.Functions, c.constructors, c.platformData) {
+ link(it.name, it.dri)
+ signature(it)
+ text(it.briefDocTagString)
+ }
+ block("Functions", 2, ContentKind.Functions, c.functions, c.platformData) {
+ link(it.name, it.dri)
+ signature(it)
+ text(it.briefDocTagString)
+ }
+ block("Properties", 2, ContentKind.Properties, c.properties, c.platformData) {
+ link(it.name, it.dri)
+ text(it.briefDocTagString)
+ }
+ }
+
+ private fun PageContentBuilder.contentForComments(d: Documentable) =
+ d.platformInfo.forEach { platformInfo ->
+ platformInfo.documentationNode.children.forEach {
+ header(3) {
+ text(it.toHeaderString())
+ text(" [${platformInfo.platformData.joinToString(", ") { it.platformType.name }}]")
+ }
+ comment(it.root)
+ text("\n")
+ }
+ }
+
+ private fun contentForFunction(f: Function) = group(f) {
+ header(1) { text(f.name) }
+ signature(f)
+ contentForComments(f)
+ block("Parameters", 2, ContentKind.Parameters, f.children, f.platformData) {
+ text(it.name ?: "<receiver>")
+ it.platformInfo.forEach { it.documentationNode.children.forEach { comment(it.root) } }
+ }
+ }
+
+ private fun TagWrapper.toHeaderString() = this.javaClass.toGenericString().split('.').last()
+}
+
+typealias RootContentBuilder = (Documentable, Kind, PageContentBuilderFunction) -> ContentGroup
+
+interface PageBuilder {
+ val rootContentGroup: RootContentBuilder
+ fun pageForModule(m: Module): ModulePageNode
+ fun pageForPackage(p: Package): PackagePageNode
+ fun pageForMember(m: CallableNode): MemberPageNode
+ fun pageForClasslike(c: Classlike): ClasslikePageNode
+} \ 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
new file mode 100644
index 00000000..48456910
--- /dev/null
+++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
@@ -0,0 +1,218 @@
+package org.jetbrains.dokka.base.translators.documentables
+
+import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.Function
+import org.jetbrains.dokka.model.TypeWrapper
+import org.jetbrains.dokka.model.doc.DocTag
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.utilities.DokkaLogger
+
+open class DefaultPageContentBuilder(
+ protected val dri: Set<DRI>,
+ protected val platformData: Set<PlatformData>,
+ protected val kind: Kind,
+ protected val commentsConverter: CommentsToContentConverter,
+ val logger: DokkaLogger,
+ protected val styles: Set<Style> = emptySet(),
+ protected val extras: Set<Extra> = emptySet()
+) : PageContentBuilder {
+ protected val contents = mutableListOf<ContentNode>()
+
+ protected fun createText(text: String, kind: Kind = ContentKind.Symbol) =
+ ContentText(text, DCI(dri, kind), platformData, styles, extras)
+
+ protected fun build() = ContentGroup(
+ contents.toList(),
+ DCI(dri, kind),
+ platformData,
+ styles,
+ extras
+ )
+
+ override fun header(level: Int, block: PageContentBuilderFunction) {
+ contents += ContentHeader(level, group(ContentKind.Symbol, block))
+ }
+
+ override fun text(text: String, kind: Kind) {
+ contents += createText(text, kind)
+ }
+
+ protected fun signature(f: Function, block: PageContentBuilderFunction) {
+ contents += group(setOf(f.dri), f.platformData, ContentKind.Symbol, block)
+ }
+
+ override fun signature(f: Function) = signature(f) {
+ text("fun ")
+ f.receiver?.also {
+ type(it.type)
+ text(".")
+ }
+ link(f.name, f.dri)
+ text("(")
+ list(f.parameters) {
+ link(it.name!!, it.dri)
+ text(": ")
+ type(it.type)
+ }
+ text(")")
+ val returnType = f.returnType
+ if (!f.isConstructor && returnType != null &&
+ returnType.constructorFqName != Unit::class.qualifiedName
+ ) {
+ text(": ")
+ type(returnType)
+ }
+ }
+
+ override fun linkTable(elements: List<DRI>) {
+ contents += ContentTable(
+ emptyList(),
+ elements.map { group(dri, platformData, ContentKind.Classes) { link(it.classNames ?: "", it) } },
+ DCI(dri, kind),
+ platformData, styles, extras
+ )
+ }
+
+ override fun <T : Documentable> block(
+ name: String,
+ level: Int,
+ kind: Kind,
+ elements: Iterable<T>,
+ platformData: Set<PlatformData>,
+ operation: PageContentBuilder.(T) -> Unit
+ ) {
+ header(level) { text(name) }
+
+ contents += ContentTable(
+ emptyList(),
+ elements.map { group(setOf(it.dri), it.platformData, kind) { operation(it) } },
+ DCI(dri, kind),
+ platformData, styles, extras
+ )
+ }
+
+ override fun <T> list(
+ elements: List<T>,
+ prefix: String,
+ suffix: String,
+ separator: String,
+ operation: PageContentBuilder.(T) -> Unit
+ ) {
+ if (elements.isNotEmpty()) {
+ if (prefix.isNotEmpty()) text(prefix)
+ elements.dropLast(1).forEach {
+ operation(it)
+ text(separator)
+ }
+ operation(elements.last())
+ if (suffix.isNotEmpty()) text(suffix)
+ }
+ }
+
+ override fun link(text: String, address: DRI, kind: Kind) {
+ contents += ContentDRILink(
+ listOf(createText(text)),
+ address,
+ DCI(dri, kind),
+ platformData
+ )
+ }
+
+ override fun link(address: DRI, kind: Kind, block: PageContentBuilderFunction) {
+ contents += ContentDRILink(
+ group(ContentKind.Main, block).children,
+ address,
+ DCI(dri, kind),
+ platformData
+ )
+ }
+
+ override fun comment(docTag: DocTag) {
+ contents += group(ContentKind.Comment) {
+ with(this as DefaultPageContentBuilder) {
+ contents += commentsConverter.buildContent(
+ docTag,
+ DCI(dri, ContentKind.Comment),
+ platformData
+ )
+ }
+ }
+ }
+
+ fun group(kind: Kind, block: PageContentBuilderFunction): ContentGroup =
+ group(dri, platformData, kind, block)
+
+ override fun group(
+ dri: Set<DRI>,
+ platformData: Set<PlatformData>,
+ kind: Kind,
+ block: PageContentBuilderFunction
+ ): ContentGroup = group(dri, platformData, kind, commentsConverter, logger, block)
+
+ companion object {
+ fun group(
+ dri: Set<DRI>,
+ platformData: Set<PlatformData>,
+ kind: Kind,
+ commentsConverter: CommentsToContentConverter,
+ logger: DokkaLogger,
+ block: PageContentBuilderFunction
+ ): ContentGroup =
+ DefaultPageContentBuilder(dri, platformData, kind, commentsConverter, logger).apply(block).build()
+ }
+}
+
+
+fun PageContentBuilder.type(t: TypeWrapper) {
+ if (t.constructorNamePathSegments.isNotEmpty() && t.dri != null)
+ link(t.constructorNamePathSegments.last(), t.dri!!)
+ else if (t.constructorNamePathSegments.isNotEmpty() && t.dri == null)
+ text(t.toString())
+ else (this as? DefaultPageContentBuilder)?.let {
+ logger.error("type $t cannot be resolved")
+ text("???")
+ }
+ list(t.arguments, prefix = "<", suffix = ">") {
+ type(it)
+ }
+}
+
+typealias PageContentBuilderFunction = PageContentBuilder.() -> Unit
+
+@DslMarker
+annotation class ContentMarker
+
+@ContentMarker
+interface PageContentBuilder {
+ fun group(
+ dri: Set<DRI>,
+ platformData: Set<PlatformData>,
+ kind: Kind, block: PageContentBuilderFunction
+ ): ContentGroup
+
+ fun text(text: String, kind: Kind = ContentKind.Symbol)
+ fun signature(f: Function)
+ fun link(text: String, address: DRI, kind: Kind = ContentKind.Symbol)
+ fun link(address: DRI, kind: Kind = ContentKind.Symbol, block: PageContentBuilderFunction)
+ fun linkTable(elements: List<DRI>)
+ fun comment(docTag: DocTag)
+ fun header(level: Int, block: PageContentBuilderFunction)
+ fun <T> list(
+ elements: List<T>,
+ prefix: String = "",
+ suffix: String = "",
+ separator: String = ",",
+ operation: PageContentBuilder.(T) -> Unit
+ )
+
+ fun <T : Documentable> block(
+ name: String,
+ level: Int,
+ kind: Kind,
+ elements: Iterable<T>,
+ platformData: Set<PlatformData>,
+ operation: PageContentBuilder.(T) -> Unit
+ )
+} \ No newline at end of file