diff options
author | Simon Ogorodnik <Simon.Ogorodnik@jetbrains.com> | 2017-03-01 22:26:28 +0300 |
---|---|---|
committer | Simon Ogorodnik <Simon.Ogorodnik@jetbrains.com> | 2017-03-01 22:26:28 +0300 |
commit | abe161a118d3d7df40ad0e1df8b8adf07773103b (patch) | |
tree | 4176768eaaac9eec7240fdcbb954238d790edae8 /core/src | |
parent | 1934463c0a2a708acfde371daf0692811f479a7a (diff) | |
download | dokka-abe161a118d3d7df40ad0e1df8b8adf07773103b.tar.gz dokka-abe161a118d3d7df40ad0e1df8b8adf07773103b.tar.bz2 dokka-abe161a118d3d7df40ad0e1df8b8adf07773103b.zip |
Correct merging of different type namesake declarations between platforms
Diffstat (limited to 'core/src')
6 files changed, 139 insertions, 43 deletions
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt index a8544cca..eac053aa 100644 --- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt +++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt @@ -34,8 +34,8 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) - protected fun div(to: StringBuilder, cssClass: String, markdown: Boolean = false, block: () -> Unit) { - to.append("<div class=\"$cssClass\"") + protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) { + to.append("<div class=\"$cssClass\"$otherAttributes") if (markdown) to.append(" markdown=\"1\"") to.append(">") if (!markdown) insideDiv++ @@ -57,8 +57,8 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, } } - override fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) { - div(to, "overload-group", true) { + override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { + div(to, "overload-group", calculateDataAttributes(platforms), true) { ensureParagraph() block() ensureParagraph() @@ -147,20 +147,23 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, else -> "identifier" } + fun calculateDataAttributes(platforms: Set<String>): String { + fun String.isKotlinVersion() = this.startsWith("Kotlin") + fun String.isJREVersion() = this.startsWith("JRE") + val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion) + val jreVersion = platforms.singleOrNull(String::isJREVersion) + val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() } + + val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: "" + val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: "" + val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: "" + return "$platformsAttr$kotlinVersionAttr$jreVersionAttr" + } + override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) { - if (platforms.isNotEmpty()) { - fun String.isKotlinVersion() = this.startsWith("Kotlin") - fun String.isJREVersion() = this.startsWith("JRE") - val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion) - val jreVersion = platforms.singleOrNull(String::isJREVersion) - val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() } - - val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: "" - val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: "" - val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: "" - - wrap("<tr$platformsAttr$kotlinVersionAttr$jreVersionAttr>", "</tr>", block) - } else + if (platforms.isNotEmpty()) + wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) + else appendTableRow(block) } @@ -188,7 +191,7 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { - div(to, "sample", true) { + div(to, "sample", markdown = true) { appendBlockCode(language) { imports() wrap("\n\nfun main(args: Array<String>) {", "}") { diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt index 419e5eeb..b4bb660e 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -185,6 +185,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, to: DocumentationNode, extension: String, name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink { + if (to.owner?.kind == NodeKind.GroupNode) + return link(from, to.owner!!, extension, name) + return FormatLink(name(to), locationService.relativePathToLocation(from, to)) } @@ -204,7 +207,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, block() } - protected open fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) { + protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { block() } @@ -242,16 +245,18 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendContent(signatureAsCode) } - open inner class PageBuilder(val nodes: Iterable<DocumentationNode>) { + open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) { open fun build() { val breakdownByLocation = nodes.groupBy { node -> node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } } for ((path, nodes) in breakdownByLocation) { - appendBreadcrumbs(path) - appendLine() - appendLine() + if (!noHeader) { + appendBreadcrumbs(path) + appendLine() + appendLine() + } appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass }) } } @@ -268,7 +273,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } else { val breakdownByName = nodes.groupBy { node -> node.name } for ((name, items) in breakdownByName) { - appendHeader { appendText(name) } + if (!noHeader) + appendHeader { appendText(name) } appendDocumentation(items) } } @@ -280,9 +286,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, if (breakdownBySummary.size == 1) { formatOverloadGroup(breakdownBySummary.values.single()) } else { - for ((summary, items) in breakdownBySummary) { + for ((_, items) in breakdownBySummary) { ensureParagraph() - appendAsOverloadGroup(to) { + appendAsOverloadGroup(to, platformsOfItems(items)) { formatOverloadGroup(items) } } @@ -371,7 +377,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { val platforms = items.asSequence().map { when (it.kind) { - NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module -> platformsOfItems(it.members) + NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members) else -> it.platformsToShow.toSet() } } @@ -418,8 +424,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } - inner class SingleNodePageBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { + inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) { + + override fun build() { + + val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } + + appendBreadcrumbs(breakdownByLocation) + appendLine() + appendLine() + appendHeader { appendText(node.name) } + + fun DocumentationNode.priority(): Int = when (kind) { + NodeKind.TypeAlias -> 1 + NodeKind.Class -> 2 + else -> 3 + } + + for (member in node.members.sortedBy(DocumentationNode::priority)) { + ensureParagraph() + appendAsOverloadGroup(to, platformsOfItems(listOf(member))) { + formatSubNodeOfGroup(member) + } + } + } + + fun formatSubNodeOfGroup(member: DocumentationNode) { + SingleNodePageBuilder(member, true).build() + } + } + + inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) + : PageBuilder(listOf(node), noHeader) { override fun build() { super.build() @@ -429,11 +465,19 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, return } + fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> { + return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) } + } + + fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { + return membersOrGroupMembers { it.kind == kind } + } + appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true) - appendSection("Types", node.members.filter { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) - appendSection("Annotations", node.members(NodeKind.AnnotationClass)) - appendSection("Exceptions", node.members(NodeKind.Exception)) - appendSection("Type Aliases", node.members(NodeKind.TypeAlias)) + appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) + appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass)) + appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception)) + appendSection("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias)) appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true) @@ -462,7 +506,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, NodeKind.CompanionObjectFunction, NodeKind.ExternalClass, NodeKind.EnumItem, - NodeKind.AllTypes + NodeKind.AllTypes, + NodeKind.GroupNode ) }) @@ -586,14 +631,11 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, override fun appendNodes(nodes: Iterable<DocumentationNode>) { val singleNode = nodes.singleOrNull() - if (singleNode != null) { - if (singleNode.kind == NodeKind.AllTypes) { - AllTypesNodeBuilder(singleNode).build() - } else { - SingleNodePageBuilder(singleNode).build() - } - } else { - PageBuilder(nodes).build() + when (singleNode?.kind) { + NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build() + NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build() + null -> PageBuilder(nodes).build() + else -> SingleNodePageBuilder(singleNode).build() } } } diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt index fcb86d53..299ad477 100644 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt @@ -278,11 +278,36 @@ class DocumentationBuilder return null } + fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?: + DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply { + appendTextNode(signature, NodeKind.Signature, RefKind.Detail) + }) + .also { groupNode -> + nodes.forEach { node -> + if (node != groupNode) { + node.owner?.let { owner -> + node.dropReferences { it.to == owner && it.kind == RefKind.Owner } + owner.dropReferences { it.to == node && it.kind == RefKind.Member } + owner.append(groupNode, RefKind.Member) + } + groupNode.append(node, RefKind.Member) + } + } + } + + fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return val existingNode = refGraph.lookup(descriptor.signature()) if (existingNode != null) { + if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor + || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) { + val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build())) + register(descriptor, node) + return + } + existingNode.updatePlatforms(descriptor) if (descriptor is ClassDescriptor) { diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index caacac14..c38a6a9f 100644 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -56,7 +56,9 @@ enum class NodeKind { * A note which is rendered once on a page documenting a group of overloaded functions. * Needs to be generated equally on all overloads. */ - OverloadGroupNote; + OverloadGroupNote, + + GroupNode; companion object { val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias) @@ -106,6 +108,10 @@ open class DocumentationNode(val name: String, references.add(DocumentationReference(this, to, kind)) } + fun dropReferences(predicate: (DocumentationReference) -> Boolean) { + references.removeAll(predicate) + } + fun addAllReferencesFrom(other: DocumentationNode) { references.addAll(other.references) } diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt index 2d86ba5b..9565263f 100644 --- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt +++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.tests import org.jetbrains.dokka.* +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import org.junit.Test class KotlinWebSiteFormatTest { @@ -23,6 +24,15 @@ class KotlinWebSiteFormatTest { verifyMultiplatformPackage(module, "dataTags") } + @Test fun dataTagsInGroupNode() { + val path = "dataTagsInGroupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/website/$path/multiplatform.kt") { model, output -> + kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.find { it.kind == NodeKind.GroupNode }.singletonOrEmptyList()) + } + verifyMultiplatformPackage(module, path) + } + private fun verifyKWSNodeByName(fileName: String, name: String) { verifyOutput("testdata/format/website/$fileName.kt", ".md", format = "kotlin-website") { model, output -> kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name }) diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt index 04c2dc62..2753e18d 100644 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.tests import org.jetbrains.dokka.* +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import org.junit.Test class MarkdownFormatTest { @@ -304,6 +305,15 @@ class MarkdownFormatTest { verifyMultiplatformPackage(module, path) } + @Test fun multiplePlatformsGroupNode() { + val path = "multiplatform/groupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.find { it.kind == NodeKind.GroupNode }.singletonOrEmptyList()) + } + verifyMultiplatformPackage(module, path) + } + private fun buildMultiplePlatforms(path: String): DocumentationModule { val module = DocumentationModule("test") val options = DocumentationOptions("", "html", generateIndexPages = false) |