From 193b8c0bcebdcdfd749090a408149cac06203614 Mon Sep 17 00:00:00 2001 From: Błażej Kardyś Date: Thu, 21 Nov 2019 02:10:26 +0100 Subject: Descriptor independent DocumentationNodes and seperate Page and PageContent builders --- core/src/main/kotlin/DokkaDescriptorVisitor.kt | 90 +++++--- core/src/main/kotlin/Model/DocumentationNode.kt | 101 +++++---- .../Model/transformers/DocumentationNodesMerger.kt | 38 ++-- core/src/main/kotlin/Utilities/nodeDebug.kt | 2 +- core/src/main/kotlin/links/DRI.kt | 72 ++++++- core/src/main/kotlin/pages/PageBuilder.kt | 95 +++++++++ core/src/main/kotlin/pages/PageContentBuilder.kt | 206 +++++++++++++++++++ core/src/main/kotlin/pages/PageNodes.kt | 20 +- .../DefaultDocumentationToPageTransformer.kt | 228 +-------------------- 9 files changed, 527 insertions(+), 325 deletions(-) create mode 100644 core/src/main/kotlin/pages/PageBuilder.kt create mode 100644 core/src/main/kotlin/pages/PageContentBuilder.kt (limited to 'core') diff --git a/core/src/main/kotlin/DokkaDescriptorVisitor.kt b/core/src/main/kotlin/DokkaDescriptorVisitor.kt index c24c43aa..bb914a27 100644 --- a/core/src/main/kotlin/DokkaDescriptorVisitor.kt +++ b/core/src/main/kotlin/DokkaDescriptorVisitor.kt @@ -1,26 +1,29 @@ package org.jetbrains.dokka import org.jetbrains.dokka.Model.* +import org.jetbrains.dokka.Model.ClassKind import org.jetbrains.dokka.Model.Function import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies import org.jetbrains.kotlin.idea.kdoc.findKDoc import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink import org.jetbrains.kotlin.kdoc.psi.impl.KDocName +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +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 org.jetbrains.kotlin.types.KotlinType class DokkaDescriptorVisitor( - val platformData: PlatformData, + private val platformData: PlatformData, private val resolutionFacade: DokkaResolutionFacade -) : DeclarationDescriptorVisitorEmptyBodies, DRI>() { +) : DeclarationDescriptorVisitorEmptyBodies() { override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor, parent: DRI): Nothing { throw IllegalStateException("${javaClass.simpleName} should never enter ${descriptor.javaClass.simpleName}") } @@ -42,17 +45,18 @@ class DokkaDescriptorVisitor( override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRI): Class { val dri = parent.withClass(descriptor.name.asString()) val scope = descriptor.getMemberScope(emptyList()) - + val descriptorData = descriptor.takeUnless { it.isExpect }?.resolveClassDescriptionData() return Class( dri, descriptor.name.asString(), + KotlinClassKindTypes.valueOf(descriptor.kind.toString()), descriptor.constructors.map { visitConstructorDescriptor(it, dri) }, scope.functions(dri), scope.properties(dri), scope.classes(dri), - descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), - listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()), - getXMLDRIs(listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData())).toMutableSet() + descriptor.takeIf { it.isExpect }?.resolveClassDescriptionData(), + listOfNotNull(descriptorData), + getXMLDRIs(descriptor, descriptorData).toMutableSet() ) } @@ -72,6 +76,8 @@ class DokkaDescriptorVisitor( return Function( dri, descriptor.name.asString(), + descriptor.returnType?.let { KotlinTypeWrapper(it) }, + false, descriptor.extensionReceiverParameter?.let { visitReceiverParameterDescriptor(it, dri) }, descriptor.valueParameters.mapIndexed { index, desc -> parameter(index, desc, dri) }, descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), @@ -84,6 +90,8 @@ class DokkaDescriptorVisitor( return Function( dri, "", + KotlinTypeWrapper(descriptor.returnType), + true, null, descriptor.valueParameters.mapIndexed { index, desc -> parameter(index, desc, dri) }, descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), @@ -97,6 +105,7 @@ class DokkaDescriptorVisitor( ) = Parameter( parent.copy(target = 0), null, + KotlinTypeWrapper(descriptor.type), listOf(descriptor.resolveDescriptorData()) ) @@ -104,16 +113,13 @@ class DokkaDescriptorVisitor( Parameter( parent.copy(target = index + 1), descriptor.name.asString(), + KotlinTypeWrapper(descriptor.type), listOf(descriptor.resolveDescriptorData()) ) - private val FunctionDescriptor.isSynthetic: Boolean - get() = (kind == FAKE_OVERRIDE || kind == SYNTHESIZED) && findKDoc() == null - private fun MemberScope.functions(parent: DRI): List = getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) { true } .filterIsInstance() - .filterNot { it.isSynthetic } .map { visitFunctionDescriptor(it, parent) } private fun MemberScope.properties(parent: DRI): List = @@ -126,7 +132,7 @@ class DokkaDescriptorVisitor( .filterIsInstance() .map { visitClassDescriptor(it, parent) } - private fun T.resolveDescriptorData(): Descriptor { + private fun DeclarationDescriptor.resolveDescriptorData(): PlatformInfo { val doc = findKDoc() val links = doc?.children?.filter { it is KDocLink }?.flatMap { link -> val destination = link.children.first { it is KDocName }.text @@ -138,26 +144,48 @@ class DokkaDescriptorVisitor( destination.split('.') ).map { Pair(destination, DRI.from(it)) } }?.toMap() ?: emptyMap() - return Descriptor(this, doc, links, listOf(platformData)) + return BasePlatformInfo(doc, links, listOf(platformData)) } - private fun getXMLDRIs(descriptors: List>) = - descriptors.flatMap { - it.docTag?.children - ?.filter { - it.text.contains("@attr") - }.orEmpty() - }.flatMap { ref -> - val matchResult = "@attr\\s+ref\\s+(.+)".toRegex().matchEntire(ref.text) - val toFind = matchResult?.groups?.last()?.value.orEmpty() - resolveKDocLink( - resolutionFacade.resolveSession.bindingContext, - resolutionFacade, - descriptors.first().descriptor, - null, - toFind.split('.') - ).map { DefaultExtra("@attr ref", DRI.from(it).toString()) } - } + private fun ClassDescriptor.resolveClassDescriptionData(): ClassPlatformInfo { + return ClassPlatformInfo(resolveDescriptorData(), + (getSuperInterfaces() + getAllSuperclassesWithoutAny()).map { DRI.from(it) }) + } + + private fun getXMLDRIs(descriptor: DeclarationDescriptor, platformInfo: PlatformInfo?) = + platformInfo?.docTag?.children + ?.filter { + it.text.contains("@attr") + }?.flatMap { ref -> + val matchResult = "@attr\\s+ref\\s+(.+)".toRegex().matchEntire(ref.text) + val toFind = matchResult?.groups?.last()?.value.orEmpty() + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + descriptor, + null, + toFind.split('.') + ).map { DefaultExtra("@attr ref", DRI.from(it).toString()) } + }.orEmpty() } data class DefaultExtra(val key: String, val value: String) : Extra + +enum class KotlinClassKindTypes : ClassKind { + CLASS, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS, + OBJECT; +} + +class KotlinTypeWrapper(private val kotlinType: KotlinType) : TypeWrapper { + private val declarationDescriptor = kotlinType.constructor.declarationDescriptor + private val fqNameSafe = declarationDescriptor?.fqNameSafe + override val constructorFqName = fqNameSafe?.asString() + override val constructorNamePathSegments: List = + fqNameSafe?.pathSegments()?.map { it.asString() } ?: emptyList() + override val arguments: List by lazy { kotlinType.arguments.map { KotlinTypeWrapper(it.type) } } + override val dri: DRI? by lazy { declarationDescriptor?.let { DRI.from(it) } } +} \ No newline at end of file diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index 55ae5902..623f2ea3 100644 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -1,11 +1,11 @@ package org.jetbrains.dokka.Model +import org.jetbrains.dokka.KotlinTypeWrapper import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.PlatformData -import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -class Module(val packages: List) : DocumentationNode() { +class Module(val packages: List) : DocumentationNode() { override val dri: DRI = DRI.topLevel override val children: List = packages override val extra: MutableSet = mutableSetOf() @@ -17,31 +17,36 @@ class Package( override val properties: List, override val classes: List, override val extra: MutableSet = mutableSetOf() -) : ScopeNode() { +) : ScopeNode() { val name = dri.packageName.orEmpty() } class Class( override val dri: DRI, val name: String, + val kind: ClassKind, val constructors: List, override val functions: List, override val properties: List, override val classes: List, - override val expectDescriptor: Descriptor?, - override val actualDescriptors: List>, + override val expected: ClassPlatformInfo?, + override val actual: List, override val extra: MutableSet = mutableSetOf() -) : ScopeNode() +) : ScopeNode() { + val inherited by lazy { platformInfo.mapNotNull { (it as? ClassPlatformInfo)?.inherited }.flatten() } +} class Function( override val dri: DRI, val name: String, + val returnType: TypeWrapper?, + val isConstructor: Boolean, override val receiver: Parameter?, val parameters: List, - override val expectDescriptor: Descriptor?, - override val actualDescriptors: List>, + override val expected: PlatformInfo?, + override val actual: List, override val extra: MutableSet = mutableSetOf() -) : CallableNode() { +) : CallableNode() { override val children: List get() = listOfNotNull(receiver) + parameters } @@ -50,10 +55,10 @@ class Property( override val dri: DRI, val name: String, override val receiver: Parameter?, - override val expectDescriptor: Descriptor?, - override val actualDescriptors: List>, + override val expected: PlatformInfo?, + override val actual: List, override val extra: MutableSet = mutableSetOf() -) : CallableNode() { +) : CallableNode() { override val children: List get() = listOfNotNull(receiver) } @@ -62,67 +67,75 @@ class Property( class Parameter( override val dri: DRI, val name: String?, - override val actualDescriptors: List>, + val type: TypeWrapper, + override val actual: List, override val extra: MutableSet = mutableSetOf() -) : DocumentationNode() { - override val children: List> +) : DocumentationNode() { + override val children: List get() = emptyList() } -class Descriptor( - val descriptor: T, - val docTag: KDocTag?, - val links: Map, +interface PlatformInfo { + val docTag: KDocTag? + val links: Map val platformData: List -) : DeclarationDescriptor by descriptor { +} + +class BasePlatformInfo( + override val docTag: KDocTag?, + override val links: Map, + override val platformData: List) : PlatformInfo { override fun equals(other: Any?): Boolean = - other is Descriptor<*> && ( - descriptor.toString() == other.descriptor.toString() && - docTag?.text == other.docTag?.text && + other is PlatformInfo && ( + docTag?.text == other.docTag?.text && links == other.links) override fun hashCode(): Int = - listOf(descriptor.toString(), docTag?.text, links).hashCode() + listOf(docTag?.text, links).hashCode() } -abstract class DocumentationNode { - open val expectDescriptor: Descriptor? = null - open val actualDescriptors: List> = emptyList() - val descriptors by lazy { listOfNotNull(expectDescriptor) + actualDescriptors } - val platformData by lazy { descriptors.flatMap { it.platformData }.toSet() } +class ClassPlatformInfo( + val info: PlatformInfo, + val inherited: List) : PlatformInfo by info + +abstract class DocumentationNode { + open val expected: PlatformInfo? = null + open val actual: List = emptyList() + val platformInfo by lazy { listOfNotNull(expected) + actual } + val platformData by lazy { platformInfo.flatMap { it.platformData }.toSet() } abstract val dri: DRI - abstract val children: List> + abstract val children: List override fun toString(): String { return "${javaClass.simpleName}($dri)" + briefDocstring.takeIf { it.isNotBlank() }?.let { " [$it]" }.orEmpty() } - override fun equals(other: Any?) = other is DocumentationNode<*> && this.dri == other.dri + override fun equals(other: Any?) = other is DocumentationNode && this.dri == other.dri override fun hashCode() = dri.hashCode() val commentsData: List>> - get() = descriptors.mapNotNull { it.docTag?.let { tag -> Pair(tag.getContent(), it.links) } } + get() = platformInfo.mapNotNull { it.docTag?.let { tag -> Pair(tag.getContent(), it.links) } } val briefDocstring: String - get() = descriptors.firstOrNull()?.docTag?.getContent().orEmpty().shorten(40) + get() = platformInfo.firstOrNull()?.docTag?.getContent().orEmpty().shorten(40) open val extra: MutableSet = mutableSetOf() } -abstract class ScopeNode : DocumentationNode() { +abstract class ScopeNode : DocumentationNode() { abstract val functions: List abstract val properties: List abstract val classes: List - override val children: List> + override val children: List get() = functions + properties + classes } -abstract class CallableNode : DocumentationNode() { +abstract class CallableNode : DocumentationNode() { abstract val receiver: Parameter? } @@ -130,20 +143,28 @@ private fun String.shorten(maxLength: Int) = lineSequence().first().let { if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it } -fun DocumentationNode<*>.walk(process: DocumentationNode<*>.() -> Unit) { +interface TypeWrapper { + val constructorFqName: String? + val constructorNamePathSegments: List + val arguments: List + val dri: DRI? +} +interface ClassKind + +fun DocumentationNode.walk(process: DocumentationNode.() -> Unit) { this.process() this.children.forEach { it.process() } } -fun DocumentationNode<*>.dfs(predicate: (DocumentationNode<*>) -> Boolean): DocumentationNode<*>? = +fun DocumentationNode.dfs(predicate: (DocumentationNode) -> Boolean): DocumentationNode? = if (predicate(this)) { this } else { this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } -fun DocumentationNode<*>.findAll(predicate: (DocumentationNode<*>) -> Boolean): Set> { - val found = mutableSetOf>() +fun DocumentationNode.findAll(predicate: (DocumentationNode) -> Boolean): Set { + val found = mutableSetOf() if (predicate(this)) { found.add(this) } else { diff --git a/core/src/main/kotlin/Model/transformers/DocumentationNodesMerger.kt b/core/src/main/kotlin/Model/transformers/DocumentationNodesMerger.kt index 2fb2f7c0..ae4f8d99 100644 --- a/core/src/main/kotlin/Model/transformers/DocumentationNodesMerger.kt +++ b/core/src/main/kotlin/Model/transformers/DocumentationNodesMerger.kt @@ -2,7 +2,6 @@ package org.jetbrains.dokka.Model.transformers import org.jetbrains.dokka.Model.* import org.jetbrains.dokka.Model.Function -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor internal object DocumentationNodesMerger : DocumentationNodeTransformer { override fun invoke(original: Module) = Module( @@ -19,55 +18,68 @@ private fun mergePackageContent(original: Package) = Package( merge(original.classes, Class::mergeWith) ) -private fun > merge(elements: List, reducer: (T, T) -> T): List = +private fun merge(elements: List, reducer: (T, T) -> T): List = elements.groupingBy { it.dri } .reduce { _, left, right -> reducer(left, right)} .values.toList() -fun Descriptor.mergeWith(other: Descriptor?) = Descriptor( - descriptor, +fun PlatformInfo.mergeWith(other: PlatformInfo?) = BasePlatformInfo( docTag, links, (platformData + (other?.platformData ?: emptyList())).distinct() ) -fun List>.merge() : List> = - groupingBy { it.descriptor }.reduce { +fun ClassPlatformInfo.mergeWith(other: ClassPlatformInfo?) = ClassPlatformInfo( + info.mergeWith(other?.info), + (inherited + (other?.inherited ?: emptyList())).distinct() +) + +fun List.mergeClassPlatformInfo() : List = + groupingBy { it.docTag.toString() + it.links + it.inherited}.reduce { + _, left, right -> left.mergeWith(right) + }.values.toList() + +fun List.merge() : List = + groupingBy { it.docTag.toString() + it.links }.reduce { _, left, right -> left.mergeWith(right) }.values.toList() fun Function.mergeWith(other: Function) = Function( dri, name, + returnType, + isConstructor, if (receiver != null && other.receiver != null) receiver.mergeWith(other.receiver) else null, merge(parameters + other.parameters, Parameter::mergeWith), - expectDescriptor?.mergeWith(other.expectDescriptor), - (actualDescriptors + other.actualDescriptors).merge() + expected?.mergeWith(other.expected), + (actual + other.actual).merge() ) fun Property.mergeWith(other: Property) = Property( dri, name, if (receiver != null && other.receiver != null) receiver.mergeWith(other.receiver) else null, - expectDescriptor?.mergeWith(other.expectDescriptor), - (actualDescriptors + other.actualDescriptors).merge() + expected?.mergeWith(other.expected), + (actual + other.actual).merge() ) fun Class.mergeWith(other: Class) = Class( dri, name, + kind, merge(constructors + other.constructors, Function::mergeWith), merge(functions + other.functions, Function::mergeWith), merge(properties + other.properties, Property::mergeWith), merge(classes + other.classes, Class::mergeWith), - expectDescriptor?.mergeWith(other.expectDescriptor), - (actualDescriptors + other.actualDescriptors).merge() + expected?.mergeWith(other.expected), + (actual + other.actual).mergeClassPlatformInfo() ) fun Parameter.mergeWith(other: Parameter) = Parameter( dri, name, - (actualDescriptors + other.actualDescriptors).merge() + type, + (actual + other.actual).merge() ) fun Package.mergeWith(other: Package) = Package( diff --git a/core/src/main/kotlin/Utilities/nodeDebug.kt b/core/src/main/kotlin/Utilities/nodeDebug.kt index 37655b0b..e89f88ec 100644 --- a/core/src/main/kotlin/Utilities/nodeDebug.kt +++ b/core/src/main/kotlin/Utilities/nodeDebug.kt @@ -8,7 +8,7 @@ const val DOWN = '\u2503' const val BRANCH = '\u2523' const val LAST = '\u2517' -fun DocumentationNode.pretty(prefix: String = "", isLast: Boolean = true): String { +fun DocumentationNode.pretty(prefix: String = "", isLast: Boolean = true): String { val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' return prefix + (if (isLast) LAST else BRANCH) + this.toString() + diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt index 605840a7..845388b5 100644 --- a/core/src/main/kotlin/links/DRI.kt +++ b/core/src/main/kotlin/links/DRI.kt @@ -1,8 +1,11 @@ package org.jetbrains.dokka.links import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjection import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import java.text.ParseException @@ -47,7 +50,7 @@ data class DRI( ) } } catch (e: Throwable) { - throw ParseException(s, 0) + throw ParseException("Can not create DRI from $s", 0) } fun from(descriptor: DeclarationDescriptor) = descriptor.parentsWithSelf.run { @@ -67,7 +70,7 @@ data class DRI( } } -fun DRI.withClass(name: String) = copy(classNames = if(classNames.isNullOrBlank()) name else "$classNames.$name") +fun DRI.withClass(name: String) = copy(classNames = if (classNames.isNullOrBlank()) name else "$classNames.$name") val DRI.parent: DRI get() = when { @@ -78,7 +81,12 @@ val DRI.parent: DRI else -> DRI.topLevel } -data class Callable(val name: String, val receiver: String, val returnType: String, val params: List) { +data class Callable( + val name: String, + val receiver: ClassReference? = null, + val returnType: String, + val params: List +) { fun signature() = "$receiver#$returnType#${params.joinToString("#")}" companion object { @@ -88,9 +96,9 @@ data class Callable(val name: String, val receiver: String, val returnType: Stri .let { (receiver, returnType, params) -> Callable( name.toString(), - receiver, + ClassReference.from(receiver), returnType, - params.split('#').filter { it.isNotBlank() } + params.split('#').mapNotNull { if (it.isNotBlank()) ClassReference.from(it) else null } ) } } catch (e: Throwable) { @@ -106,17 +114,61 @@ data class Callable(val name: String, val receiver: String, val returnType: Stri fun from(descriptor: CallableDescriptor) = with(descriptor) { Callable( name.asString(), - extensionReceiverParameter?.value?.type?.constructorName.orEmpty(), + extensionReceiverParameter?.let { ClassReference.from(it) }, returnType?.constructorName.orEmpty(), - valueParameters.map { it.type.constructorName.orEmpty() } + valueParameters.map { ClassReference.from(it.type.constructorName.orEmpty()) } ) } } } -data class ClassReference(val dri: DRI, val subs: MutableList = mutableListOf()) { - private val subsText = subs.takeIf { it.isNotEmpty() }?.toString().orEmpty() - override fun toString() = "$dri$subsText" +data class ClassReference(val classNames: String, val typeBounds: List = emptyList()) { + override fun toString() = classNames + if (typeBounds.isNotEmpty()) { + "[${typeBounds.joinToString(",")}]" + } else { + "" + } + + companion object { + + fun from(s: String?): ClassReference = + s?.let { + "((?:\\w+\\.?)+)(?:\\[((?:\\w+,?)+)])?".toRegex() // This regex matches class names with or without typebounds + .matchEntire(it) + ?.let { m -> + ClassReference(m.groupValues[1], typeBoundsFrom(m.groupValues[2])) + } + } ?: throw ParseException(s, 0) + + fun from(d: ReceiverParameterDescriptor): ClassReference = + when (val value = d.value) { + is ExtensionReceiver -> ClassReference( + classNames = value.type.constructorName.orEmpty(), + typeBounds = value.declarationDescriptor.typeParameters.map { + ClassReference( + it.fqNameSafe.toString(), + it.upperBounds.map { from(it) } + ) + } + ) + else -> ClassReference(d.value.type.constructorName.orEmpty()) + } + + private fun from(t: KotlinType): ClassReference = + ClassReference(t.constructorName.orEmpty(), t.arguments.map { from(it) }) + + private fun from(t: TypeProjection): ClassReference = + if (t.isStarProjection) { + starProjection + } else { + from(t.type) + } + + private fun typeBoundsFrom(s: String) = + s.split(",").filter { it.isNotBlank() }.map { ClassReference.from(it) } + + val starProjection = ClassReference("*") + } } private operator fun List.component6(): T = get(5) diff --git a/core/src/main/kotlin/pages/PageBuilder.kt b/core/src/main/kotlin/pages/PageBuilder.kt new file mode 100644 index 00000000..d20635a4 --- /dev/null +++ b/core/src/main/kotlin/pages/PageBuilder.kt @@ -0,0 +1,95 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.Model.* +import org.jetbrains.dokka.Model.Function + +class DefaultPageBuilder( + override val rootContentGroup: RootContentBuilder +) : PageBuilder { + + override fun pageForModule(m: Module): ModulePageNode = + ModulePageNode("root", contentForModule(m), m, m.packages.map { pageForPackage(it) }) + + override fun pageForPackage(p: Package) = + PackagePageNode(p.name, contentForPackage(p), p.dri, p, + p.classes.map { pageForClass(it) } + + p.functions.map { pageForMember(it) } + + p.properties.map { pageForMember(it) }) + + override fun pageForClass(c: Class): ClassPageNode = + ClassPageNode(c.name, contentForClass(c), c.dri, c, + c.constructors.map { pageForMember(it) } + + c.classes.map { pageForClass(it) } + + c.functions.map { pageForMember(it) }) + + override fun pageForMember(m: CallableNode): MemberPageNode = + when (m) { + is Function -> + MemberPageNode(m.name, contentForFunction(m), m.dri, m) + else -> throw IllegalStateException("$m should not be present here") + } + + private fun group(node: DocumentationNode, content: PageContentBuilderFunction) = + rootContentGroup(node, ContentKind.Main, content) + + private 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") + } + + private fun contentForPackage(p: Package) = group(p) { + header(1) { text("Package ${p.name}") } + block("Types", 2, ContentKind.Properties, p.classes, p.platformData) { + link(it.name, it.dri) + text(it.briefDocstring) + } + block("Functions", 2, ContentKind.Functions, p.functions, p.platformData) { + link(it.name, it.dri) + signature(it) + text(it.briefDocstring) + } + } + + private fun contentForClass(c: Class) = group(c) { + header(1) { text(c.name) } + c.inherited.takeIf { it.isNotEmpty() }?.let { + header(2) { text("SuperInterfaces") } + linkTable(it) + } + c.commentsData.forEach { (doc, links) -> comment(doc, links) } + block("Constructors", 2, ContentKind.Functions, c.constructors, c.platformData) { + link(it.name, it.dri) + signature(it) + text(it.briefDocstring) + } + block("Functions", 2, ContentKind.Functions, c.functions, c.platformData) { + link(it.name, it.dri) + signature(it) + text(it.briefDocstring) + } + } + + private fun contentForFunction(f: Function) = group(f) { + header(1) { text(f.name) } + signature(f) + f.commentsData.forEach { (doc, links) -> markdown(doc, links) } + block("Parameters", 2, ContentKind.Parameters, f.children, f.platformData) { + text(it.name ?: "") + it.commentsData.forEach { (doc, links) -> markdown(doc, links) } + } + } +} + +typealias RootContentBuilder = (DocumentationNode, Kind, PageContentBuilderFunction) -> ContentGroup + +interface PageBuilder { + val rootContentGroup: RootContentBuilder + fun pageForModule(m: Module): ModulePageNode + fun pageForPackage(p: Package): PackagePageNode + fun pageForMember(m: CallableNode): MemberPageNode + fun pageForClass(c: Class): ClassPageNode +} \ No newline at end of file diff --git a/core/src/main/kotlin/pages/PageContentBuilder.kt b/core/src/main/kotlin/pages/PageContentBuilder.kt new file mode 100644 index 00000000..9d07a098 --- /dev/null +++ b/core/src/main/kotlin/pages/PageContentBuilder.kt @@ -0,0 +1,206 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.DokkaLogger +import org.jetbrains.dokka.Model.DocumentationNode +import org.jetbrains.dokka.Model.Function +import org.jetbrains.dokka.Model.Parameter +import org.jetbrains.dokka.Model.TypeWrapper +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.parseMarkdown + +class DefaultPageContentBuilder( + private val node: DocumentationNode, + private val kind: Kind, + private val markdownConverter: MarkdownToContentConverter, + val logger: DokkaLogger, + private val styles: Set