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.kdoc.psi.impl.KDocTag class Module(val packages: List) : DocumentationNode() { override val dri: DRI = DRI.topLevel override val children: List = packages override val extra: MutableSet = mutableSetOf() } class Package( override val dri: DRI, override val functions: List, override val properties: List, override val classes: List, override val extra: MutableSet = mutableSetOf() ) : 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 expected: ClassPlatformInfo?, override val actual: List, override val extra: MutableSet = mutableSetOf() ) : 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 expected: PlatformInfo?, override val actual: List, override val extra: MutableSet = mutableSetOf() ) : CallableNode() { override val children: List get() = listOfNotNull(receiver) + parameters } class Property( override val dri: DRI, val name: String, override val receiver: Parameter?, override val expected: PlatformInfo?, override val actual: List, override val extra: MutableSet = mutableSetOf() ) : CallableNode() { override val children: List get() = listOfNotNull(receiver) } // TODO: treat named Parameters and receivers differently class Parameter( override val dri: DRI, val name: String?, val type: TypeWrapper, override val actual: List, override val extra: MutableSet = mutableSetOf() ) : DocumentationNode() { override val children: List get() = emptyList() } interface PlatformInfo { val docTag: KDocTag? val links: Map val platformData: List } class BasePlatformInfo( override val docTag: KDocTag?, override val links: Map, override val platformData: List) : PlatformInfo { override fun equals(other: Any?): Boolean = other is PlatformInfo && ( docTag?.text == other.docTag?.text && links == other.links) override fun hashCode(): Int = listOf(docTag?.text, links).hashCode() } 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 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 hashCode() = dri.hashCode() val commentsData: List>> get() = platformInfo.mapNotNull { it.docTag?.let { tag -> Pair(tag.getContent(), it.links) } } val briefDocstring: String get() = platformInfo.firstOrNull()?.docTag?.getContent().orEmpty().shorten(40) open val extra: MutableSet = mutableSetOf() } abstract class ScopeNode : DocumentationNode() { abstract val functions: List abstract val properties: List abstract val classes: List override val children: List get() = functions + properties + classes } abstract class CallableNode : DocumentationNode() { abstract val receiver: Parameter? } private fun String.shorten(maxLength: Int) = lineSequence().first().let { if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it } 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? = if (predicate(this)) { this } else { this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } fun DocumentationNode.findAll(predicate: (DocumentationNode) -> Boolean): Set { val found = mutableSetOf() if (predicate(this)) { found.add(this) } else { this.children.asSequence().mapNotNull { it.findAll(predicate) }.forEach { found.addAll(it) } } return found } interface Extra