aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/Formats
diff options
context:
space:
mode:
authorKrystianUjma <kujma@virtuslab.com>2019-03-19 11:34:38 +0100
committerKrystianUjma <kujma@virtuslab.com>2019-03-19 15:56:01 +0100
commit2e94ac4098d9e2582223485b981114fa462e5757 (patch)
treeed1b772bce9d790c473b346bcce435e893b29be7 /core/src/main/kotlin/Formats
parentb566f8852e94f9a17be86bf845aeff6c36bd8378 (diff)
parenta9c91cb7dc54c1554be5cf8de90516c929c0c1cb (diff)
downloaddokka-2e94ac4098d9e2582223485b981114fa462e5757.tar.gz
dokka-2e94ac4098d9e2582223485b981114fa462e5757.tar.bz2
dokka-2e94ac4098d9e2582223485b981114fa462e5757.zip
Merge branch 'kotlin-website-jonnyzzz' into multiplatform-support
Diffstat (limited to 'core/src/main/kotlin/Formats')
-rw-r--r--core/src/main/kotlin/Formats/FormatDescriptor.kt1
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt10
-rw-r--r--core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt130
-rw-r--r--core/src/main/kotlin/Formats/PackageListService.kt11
-rw-r--r--core/src/main/kotlin/Formats/StructuredFormatService.kt691
-rw-r--r--core/src/main/kotlin/Formats/YamlOutlineService.kt2
6 files changed, 617 insertions, 228 deletions
diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt
index b497fb0f..4bac8aa0 100644
--- a/core/src/main/kotlin/Formats/FormatDescriptor.kt
+++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt
@@ -25,6 +25,7 @@ abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor {
override fun configureOutput(binder: Binder): Unit = with(binder) {
bind<Generator>() toType NodeLocationAwareGenerator::class
bind<NodeLocationAwareGenerator>() toType generatorServiceClass
+ bind(generatorServiceClass.java) // https://github.com/google/guice/issues/847
bind<LanguageService>() toType languageServiceClass
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
index a98002d4..51ceb47e 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt
@@ -57,8 +57,8 @@ open class KotlinWebsiteOutputBuilder(
}
}
- override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
- div(to, "overload-group", calculateDataAttributes(platforms), true) {
+ override fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) {
+ div(to, "overload-group", calculateDataAttributes(platforms.keys), true) {
ensureParagraph()
block()
ensureParagraph()
@@ -159,14 +159,14 @@ open class KotlinWebsiteOutputBuilder(
return "$platformsAttr$kotlinVersionAttr$jreVersionAttr"
}
- override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+ override fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) {
if (platforms.isNotEmpty())
- wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
+ wrap("<tr${calculateDataAttributes(platforms.keys)}>", "</tr>", block)
else
appendTableRow(block)
}
- override fun appendPlatforms(platforms: Set<String>) {
+ override fun appendPlatforms(platforms: PlatformsData) {
}
}
diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
index 6ced75b5..86bc9df9 100644
--- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
+++ b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt
@@ -3,7 +3,6 @@ package org.jetbrains.dokka
import com.google.inject.Inject
import com.google.inject.name.Named
import org.jetbrains.dokka.Utilities.impliedPlatformsName
-import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File
@@ -20,7 +19,7 @@ open class KotlinWebsiteHtmlOutputBuilder(
generator: NodeLocationAwareGenerator,
languageService: LanguageService,
extension: String,
- impliedPlatforms: List<String>,
+ val impliedPlatforms: List<String>,
templateService: HtmlTemplateService
) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) {
private var needHardLineBreaks = false
@@ -60,7 +59,7 @@ open class KotlinWebsiteHtmlOutputBuilder(
}
}
- override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ override fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) {
div(to, "overload-group", calculateDataAttributes(platforms)) {
block()
}
@@ -69,27 +68,29 @@ open class KotlinWebsiteHtmlOutputBuilder(
override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body)
override fun appendTable(vararg columns: String, body: () -> Unit) {
- to.appendln("<table class=\"api-docs-table\">")
- body()
- to.appendln("</table>")
+ //to.appendln("<table class=\"api-docs-table\">")
+ div(to, "api-declarations-list") {
+ body()
+ }
+ //to.appendln("</table>")
}
override fun appendTableBody(body: () -> Unit) {
- to.appendln("<tbody>")
+ //to.appendln("<tbody>")
body()
- to.appendln("</tbody>")
+ //to.appendln("</tbody>")
}
override fun appendTableRow(body: () -> Unit) {
- to.appendln("<tr>")
+ //to.appendln("<tr>")
body()
- to.appendln("</tr>")
+ //to.appendln("</tr>")
}
override fun appendTableCell(body: () -> Unit) {
- to.appendln("<td>")
+// to.appendln("<td>")
body()
- to.appendln("\n</td>")
+// to.appendln("\n</td>")
}
override fun appendSymbol(text: String) {
@@ -122,34 +123,79 @@ open class KotlinWebsiteHtmlOutputBuilder(
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() }
+ private data class PlatformsForElement(
+ val platformToVersion: Map<String, String>
+ )
+
+ private fun calculatePlatforms(platforms: PlatformsData): PlatformsForElement {
+ //val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)?.removePrefix("Kotlin ")
+ val jreVersion = platforms.keys.filter(String::isJREVersion).min()?.takeUnless { it.endsWith("6") }
+ val targetPlatforms = platforms.filterNot { it.key.isJREVersion() } +
+ listOfNotNull(jreVersion?.let { it to platforms[it]!! })
+
+ return PlatformsForElement(
+ targetPlatforms.mapValues { (_, nodes) -> effectiveSinceKotlinForNodes(nodes) }
+ )
+ }
- 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"
+ private fun calculateDataAttributes(platforms: PlatformsData): String {
+ val platformToVersion = calculatePlatforms(platforms).platformToVersion
+ val (platformNames, versions) = platformToVersion.toList().unzip()
+ return "data-platform=\"${platformNames.joinToString()}\" "+
+ "data-kotlin-version=\"${versions.joinToString()}\""
}
- override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
- if (platforms.isNotEmpty())
- wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
- else
- appendTableRow(block)
+ override fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) {
+// if (platforms.isNotEmpty())
+// wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block)
+// else
+// appendTableRow(block)
+ div(to, "declarations", otherAttributes = " ${calculateDataAttributes(platforms)}") {
+ block()
+ }
}
- override fun appendPlatforms(platforms: Set<String>) {}
+ override fun appendPlatforms(platforms: PlatformsData) {
+ val platformToVersion = calculatePlatforms(platforms).platformToVersion
+ div(to, "tags") {
+ div(to, "spacer") {}
+ platformToVersion.entries.sortedBy {
+ platformSortWeight(it.key)
+ }.forEach { (platform, version) ->
+ div(to, "tags__tag platform tag-value-$platform",
+ otherAttributes = " data-tag-version=\"$version\"") {
+ to.append(platform)
+ }
+ }
+ div(to, "tags__tag kotlin-version") {
+ to.append(mergeVersions(platformToVersion.values.toList()))
+ }
+ }
+ }
+
+ override fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) {
+ div(to, "node-page-main", otherAttributes = " ${calculateDataAttributes(platforms)}") {
+ block()
+ }
+
+ }
override fun appendBreadcrumbSeparator() {
to.append(" / ")
}
+ override fun appendPlatformsAsText(platforms: PlatformsData) {
+ appendHeader(5) {
+ val filtered = platforms.keys.filterNot { it.isJREVersion() }.sortedBy { platformSortWeight(it) }
+ if (filtered.isNotEmpty()) {
+ to.append("For ")
+ filtered.joinTo(to)
+ }
+ }
+ }
+
override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) {
- div(to, "sample") {
+ div(to, "sample", otherAttributes = " data-min-compiler-version=\"1.3\"") {
appendBlockCode(language) {
imports()
wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") {
@@ -169,6 +215,29 @@ open class KotlinWebsiteHtmlOutputBuilder(
appendContent(section)
}
}
+
+ override fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ if (platforms.isNotEmpty())
+ wrap("<div ${calculateDataAttributes(platforms)}>", "</div>") {
+ block(platforms)
+ }
+ else
+ block(platforms)
+ }
+
+ override fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ div(to, "summary-group", otherAttributes = " ${calculateDataAttributes(platforms)}") {
+ block(platforms)
+ }
+ }
+
+ fun platformSortWeight(name: String) = when(name.toLowerCase()) {
+ "common" -> 0
+ "jvm" -> 1
+ "js" -> 3
+ "native" -> 4
+ else -> 2 // This is hack to support JRE/JUnit and so on
+ }
}
class KotlinWebsiteHtmlFormatService @Inject constructor(
@@ -184,3 +253,6 @@ class KotlinWebsiteHtmlFormatService @Inject constructor(
KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService)
}
+
+private fun String.isKotlinVersion() = this.startsWith("Kotlin")
+private fun String.isJREVersion() = this.startsWith("JRE", ignoreCase=true) \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt
index 7b68098e..e675d927 100644
--- a/core/src/main/kotlin/Formats/PackageListService.kt
+++ b/core/src/main/kotlin/Formats/PackageListService.kt
@@ -32,10 +32,13 @@ class DefaultPackageListService @Inject constructor(
node.members.forEach { visit(it, relocated = true) }
}
NodeKind.GroupNode -> {
- //only children of top-level GN records interesting for us, since link to top-level ones should point to GN
- node.members.forEach { it.members.forEach { visit(it, relocated = true) } }
- //record signature of GN as signature of type alias and class merged to GN, so link to it should point to GN
- node.detailOrNull(NodeKind.Signature)?.let { visit(it, relocated = true) }
+ if (node.members.isNotEmpty()) {
+ // Only nodes only has single file is need to be relocated
+ // TypeAliases for example
+ node.origins
+ .filter { it.members.isEmpty() }
+ .forEach { visit(it, relocated = true) }
+ }
}
else -> {
if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) {
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt
index 410de281..04810498 100644
--- a/core/src/main/kotlin/Formats/StructuredFormatService.kt
+++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt
@@ -1,18 +1,59 @@
package org.jetbrains.dokka
import org.jetbrains.dokka.LanguageService.RenderMode
+import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
data class FormatLink(val text: String, val href: String)
+private data class Summarized(
+ val data: List<SummarizedBySummary>
+) {
+
+ constructor(data: Map<ContentNode, Map<ContentNode, List<DocumentationNode>>>) : this(
+ data.entries.map { (summary, signatureToMember) ->
+ SummarizedBySummary(
+ summary,
+ signatureToMember.map { (signature, nodes) ->
+ SummarizedNodes(signature, nodes)
+ }
+ )
+ }
+ )
+
+ data class SummarizedNodes(val content: ContentNode, val nodes: List<DocumentationNode>) {
+ val platforms = effectivePlatformsForMembers(nodes)
+ }
+ data class SummarizedBySummary(val content: ContentNode, val signatures: List<SummarizedNodes>) {
+ val platforms = effectivePlatformsForMembers(signatures.flatMap { it.nodes })
+ val platformsOnSignature = !samePlatforms(signatures.map { it.platforms })
+ }
+
+
+ fun computePlatformLevel(): PlatformPlacement {
+ if (data.any { it.platformsOnSignature }) {
+ return PlatformPlacement.Signature
+ }
+ if (samePlatforms(data.map { it.platforms })) {
+ return PlatformPlacement.Row
+ }
+ return PlatformPlacement.Summary
+ }
+ val platformPlacement: PlatformPlacement = computePlatformLevel()
+ val platforms = effectivePlatformsForMembers(data.flatMap { it.signatures.flatMap { it.nodes } })
+
+
+ enum class PlatformPlacement {
+ Row, Summary, Signature
+ }
+}
+
abstract class StructuredOutputBuilder(val to: StringBuilder,
val location: Location,
val generator: NodeLocationAwareGenerator,
val languageService: LanguageService,
val extension: String,
- val impliedPlatforms: List<String>) : FormattedOutputBuilder {
-
- protected fun DocumentationNode.location() = generator.location(this)
+ impliedPlatforms: List<String>) : FormattedOutputBuilder {
protected fun wrap(prefix: String, suffix: String, body: () -> Unit) {
to.append(prefix)
@@ -83,6 +124,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
+ open fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ block(platforms)
+ }
+
+ open fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) {
+ appendAsPlatformDependentBlock(platforms, block)
+ }
+
open fun appendSymbol(text: String) {
appendText(text)
}
@@ -95,6 +144,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendText(text)
}
+ open fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) {
+ block()
+ }
+
fun appendEntity(text: String) {
to.append(text)
}
@@ -158,7 +211,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
is ContentNodeLink -> {
val node = content.node
- val linkTo = if (node != null) locationHref(location, node) else "#"
+ val linkTo = if (node != null) locationHref(location, node, generator) else "#"
appendLinkIfNotThisPage(linkTo, content)
}
is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content)
@@ -196,31 +249,19 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
- open fun link(from: DocumentationNode,
- to: DocumentationNode,
- name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink = link(from, to, extension, name)
-
- open fun link(from: DocumentationNode,
- to: DocumentationNode,
- extension: String,
- name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink {
- if (to.owner?.kind == NodeKind.GroupNode)
- return link(from, to.owner!!, extension, name)
-
- if (from.owner?.kind == NodeKind.GroupNode)
- return link(from.owner!!, to, extension, name)
+ open fun link(
+ from: DocumentationNode,
+ to: DocumentationNode,
+ name: (DocumentationNode) -> String = DocumentationNode::name
+ ): FormatLink = link(from, to, extension, name)
- return FormatLink(name(to), from.location().relativePathTo(to.location()))
- }
-
- fun locationHref(from: Location, to: DocumentationNode): String {
- val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to
- if (topLevelPage != null) {
- val signature = to.detailOrNull(NodeKind.Signature)
- return from.relativePathTo(topLevelPage.location(), signature?.name ?: to.name)
- }
- return from.relativePathTo(to.location())
- }
+ open fun link(
+ from: DocumentationNode,
+ to: DocumentationNode,
+ extension: String,
+ name: (DocumentationNode) -> String = DocumentationNode::name
+ ): FormatLink =
+ FormatLink(name(to), generator.relativePathToLocation(from, to))
private fun DocumentationNode.isModuleOrPackage(): Boolean =
kind == NodeKind.Module || kind == NodeKind.Package
@@ -229,18 +270,25 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
block()
}
- protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
+ protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) {
block()
}
- protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
+ protected open fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) {
appendTableRow(block)
}
- protected open fun appendPlatforms(platforms: Set<String>) {
+ protected open fun appendPlatformsAsText(platforms: PlatformsData) {
+ if (platforms.isNotEmpty()) {
+ appendLine()
+ appendText(platforms.keys.joinToString(prefix = "(", postfix = ")"))
+ }
+ }
+
+ protected open fun appendPlatforms(platforms: PlatformsData) {
if (platforms.isNotEmpty()) {
appendLine()
- appendText(platforms.joinToString(prefix = "(", postfix = ")"))
+ appendText(platforms.keys.joinToString(prefix = "(", postfix = ")"))
}
}
@@ -289,7 +337,6 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name
appendHeader(2) { appendText("Package $packageName") }
}
- singleNode.appendPlatforms()
appendContent(singleNode.content)
} else {
val breakdownByName = nodes.groupBy { node -> node.name }
@@ -302,45 +349,136 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) {
- val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content }
+ val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node ->
+ when (node.kind) {
+ NodeKind.GroupNode -> node.origins.map { it.content }
+ else -> node.content
+ }
+ }
if (breakdownBySummary.size == 1) {
- formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode)
+ val node = breakdownBySummary.values.single()
+ appendAsNodeDescription(effectivePlatformsForMembers(node)) {
+ formatOverloadGroup(node, isSingleNode)
+ }
} else {
for ((_, items) in breakdownBySummary) {
-
- appendAsOverloadGroup(to, platformsOfItems(items)) {
+ appendAsOverloadGroup(to, effectivePlatformsForMembers(items)) {
formatOverloadGroup(items)
}
-
}
}
}
private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) {
+
+ val platformsPerGroup = samePlatforms(
+ items.flatMap {
+ if (it.kind == NodeKind.GroupNode) {
+ it.origins.groupBy { origin ->
+ languageService.render(origin)
+ }.values.map { origins -> effectivePlatformsForMembers(origins) }
+ } else {
+ listOf(effectivePlatformsForNode(it))
+ }
+ }
+ )
+
+ if (platformsPerGroup) {
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(items)) { platforms ->
+ appendPlatforms(platforms)
+ }
+ }
for ((index, item) in items.withIndex()) {
if (index > 0) appendLine()
+
+ if (item.kind == NodeKind.GroupNode) {
+ renderGroupNode(item, isSingleNode, !platformsPerGroup)
+ } else {
+ renderSimpleNode(item, isSingleNode, !platformsPerGroup)
+ }
+
+ }
+ // All items have exactly the same documentation, so we can use any item to render it
+ val item = items.first()
+ // TODO: remove this block cause there is no one node with OverloadGroupNote detail
+ item.details(NodeKind.OverloadGroupNote).forEach {
+ appendContent(it.content)
+ }
+
+ if (item.kind == NodeKind.GroupNode) {
+ val groupByContent = item.origins.groupBy { it.content }
+ if (groupByContent.count { !it.key.isEmpty() } > 1) {
+ if (groupByContent.size > 1) println("[mult] Found ov diff: ${generator.location(item).path}")
+ }
+ for ((content, origins) in groupByContent) {
+ if (content.isEmpty()) continue
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(origins)) { platforms ->
+ if (groupByContent.count { !it.key.isEmpty() } > 1) {
+ appendPlatformsAsText(platforms)
+ }
+ appendContent(content.summary)
+ content.appendDescription()
+ }
+ }
+ } else {
+ if (!item.content.isEmpty()) {
+ val platforms = effectivePlatformsForNode(item)
+ appendAsPlatformDependentBlock(platforms) {
+ appendContent(item.summary)
+ item.content.appendDescription()
+ }
+ }
+ }
+ }
+
+
+ fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) {
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(listOf(item))) { platforms ->
+ // TODO: use summarizesignatures
val rendered = languageService.render(item)
item.detailOrNull(NodeKind.Signature)?.let {
if (item.kind !in NodeKind.classLike || !isSingleNode)
appendAnchor(it.name)
}
+ if (withPlatforms) {
+ appendPlatforms(platforms)
+ }
appendAsSignature(rendered) {
appendCode { appendContent(rendered) }
item.appendSourceLink()
}
item.appendOverrides()
item.appendDeprecation()
- item.appendPlatforms()
}
- // All items have exactly the same documentation, so we can use any item to render it
- val item = items.first()
- item.details(NodeKind.OverloadGroupNote).forEach {
- appendContent(it.content)
+ }
+
+ fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) {
+ // TODO: use summarizesignatures
+ val groupBySignature = item.origins.groupBy {
+ languageService.render(it)
}
- appendContent(item.content.summary)
- item.appendDescription()
+ for ((sign, nodes) in groupBySignature) {
+ appendAsPlatformDependentBlock(effectivePlatformsForMembers(nodes)) { platforms ->
+ val first = nodes.first()
+ first.detailOrNull(NodeKind.Signature)?.let {
+ if (item.kind !in NodeKind.classLike || !isSingleNode)
+ appendAnchor(it.name)
+ }
+
+ if (withPlatforms) {
+ appendPlatforms(platforms)
+ }
+
+ appendAsSignature(sign) {
+ appendCode { appendContent(sign) }
+ }
+ first.appendOverrides()
+ first.appendDeprecation()
+ }
+
+ }
}
private fun DocumentationNode.appendSourceLink() {
@@ -355,7 +493,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
overrides.forEach {
appendParagraph {
to.append("Overrides ")
- val location = location().relativePathTo(it.location())
+ val location = generator.relativePathToLocation(this, it)
appendLink(FormatLink(it.owner!!.name + "." + it.name, location))
}
@@ -367,82 +505,95 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull()
val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull()
appendLine()
- if (deprecationValue != null) {
- appendStrong { to.append("Deprecated:") }
- appendText(" " + deprecationValue.name.removeSurrounding("\""))
- appendLine()
- appendLine()
- } else if (deprecation?.content != Content.Empty) {
- appendStrong { to.append("Deprecated:") }
- to.append(" ")
- appendContent(deprecation!!.content)
- } else {
- appendStrong { to.append("Deprecated") }
- appendLine()
- appendLine()
- }
- }
- }
-
- private fun DocumentationNode.appendPlatforms() {
- val platforms = if (isModuleOrPackage())
- platformsToShow.toSet() + platformsOfItems(members)
- else
- platformsToShow
-
- if (platforms.isEmpty()) return
-
- appendParagraph {
- appendStrong { to.append("Platform and version requirements:") }
- to.append(" " + platforms.joinToString())
- }
- }
-
- protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> {
- val platforms = items.asSequence().map {
- when (it.kind) {
- NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members)
- else -> it.platformsToShow.toSet()
- }
- }
-
- fun String.isKotlinVersion() = this.startsWith("Kotlin")
-
- // Calculating common platforms for items
- return platforms.reduce { result, platformsOfItem ->
- val otherKotlinVersion = result.find { it.isKotlinVersion() }
- val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
-
- // When no Kotlin version specified, it means that version is 1.0
- if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
- val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct()
-
- val minVersion = allKotlinVersions.min()!!
- val resultVersion = when {
- allKotlinVersions.size == 1 -> allKotlinVersions.single()
- minVersion.endsWith("+") -> minVersion
- else -> minVersion + "+"
+ when {
+ deprecationValue != null -> {
+ appendStrong { to.append("Deprecated:") }
+ appendText(" " + deprecationValue.name.removeSurrounding("\""))
+ appendLine()
+ appendLine()
+ }
+ deprecation?.content != Content.Empty -> {
+ appendStrong { to.append("Deprecated:") }
+ to.append(" ")
+ appendContent(deprecation!!.content)
+ }
+ else -> {
+ appendStrong { to.append("Deprecated") }
+ appendLine()
+ appendLine()
}
-
- result.intersect(otherPlatforms) + resultVersion
- } else {
- result.intersect(platformsOfItem)
}
}
}
- val DocumentationNode.platformsToShow: List<String>
- get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it }
- private fun DocumentationNode.appendDescription() {
- if (content.description != ContentEmpty) {
- appendContent(content.description)
- }
- content.getSectionsWithSubjects().forEach {
+// 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.GroupNode -> platformsOfItems(it.origins)
+// else -> it.platformsToShow.toSet()
+// }
+// }
+//
+// fun String.isKotlinVersion() = this.startsWith("Kotlin")
+//
+// if (platforms.count() == 0) return emptySet()
+//
+// // Calculating common platforms for items
+// return platforms.reduce { result, platformsOfItem ->
+// val otherKotlinVersion = result.find { it.isKotlinVersion() }
+// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
+//
+// // When no Kotlin version specified, it means that version is 1.0
+// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
+// result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions)
+// } else {
+// result.intersect(platformsOfItem)
+// }
+// }
+// }
+//
+// protected fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> {
+// val platforms = items.asSequence().map {
+// when (it.kind) {
+// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins)
+// else -> it.platformsToShow.toSet()
+// }
+// }
+//
+// fun String.isKotlinVersion() = this.startsWith("Kotlin")
+//
+// if (platforms.count() == 0) return emptySet()
+//
+// // Calculating common platforms for items
+// return platforms.reduce { result, platformsOfItem ->
+// val otherKotlinVersion = result.find { it.isKotlinVersion() }
+// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
+//
+// // When no Kotlin version specified, it means that version is 1.0
+// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
+// result.union(otherPlatforms) + mergeVersions(otherKotlinVersion, kotlinVersions)
+// } else {
+// result.union(otherPlatforms)
+// }
+// }
+// }
+
+// val DocumentationNode.platformsToShow: List<String>
+// get() = platforms
+
+ private fun Content.appendDescription() {
+ if (description != ContentEmpty) {
+ appendContent(description)
+ }
+
+
+ getSectionsWithSubjects().forEach {
appendSectionWithSubject(it.key, it.value)
}
- for (section in content.sections.filter { it.subjectName == null }) {
+ for (section in sections.filter { it.subjectName == null }) {
appendSectionWithTag(section)
}
}
@@ -461,6 +612,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
}
+
+ fun appendOriginsGroupByContent(node: DocumentationNode) {
+ require(node.kind == NodeKind.GroupNode)
+ val groupByContent =
+ node.origins.groupBy { it.content }
+ .mapValues { (_, origins) ->
+ effectivePlatformsForMembers(origins)
+ }
+ .filterNot { it.key.isEmpty() }
+ .toList()
+ .sortedByDescending { it.second.size }
+
+ if (groupByContent.size > 1) println("[mult] Found diff: ${generator.location(node).path}")
+ for ((content, platforms) in groupByContent) {
+ appendAsPlatformDependentBlock(platforms) {
+ if (groupByContent.size > 1) {
+ appendPlatformsAsText(platforms)
+ }
+ appendContent(content.summary)
+ content.appendDescription()
+ }
+ }
+ }
+ }
+
+ inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) :
+ PageBuilder(listOf(node), noHeader) {
+
+ override fun build() {
+ super.build()
+ SectionsBuilder(node).build()
+ }
}
inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) {
@@ -473,39 +656,37 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
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)) {
-
- appendAsOverloadGroup(to, platformsOfItems(listOf(member))) {
- formatSubNodeOfGroup(member)
- }
+ appendAsNodeDescription(effectivePlatformsForNode(node)) {
+ renderGroupNode(node, true)
+ appendOriginsGroupByContent(node)
}
- }
- fun formatSubNodeOfGroup(member: DocumentationNode) {
- SingleNodePageBuilder(member, true).build()
+ SectionsBuilder(node).build()
}
}
-
- inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false)
- : PageBuilder(listOf(node), noHeader) {
-
+//
+// private fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> {
+// val platforms = items.flatMapTo(mutableSetOf<String>()) {
+// when (it.kind) {
+// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins)
+// else -> it.platforms
+// }
+// }
+//
+// return platforms
+// }
+
+
+ inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) {
override fun build() {
- super.build()
-
if (node.kind == NodeKind.ExternalClass) {
appendSection("Extensions for ${node.name}", node.members)
return
}
fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> {
- return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) }
+ return members.filter(predicate) + members(NodeKind.GroupNode).filter{ it.origins.isNotEmpty() && predicate(it.origins.first()) }
}
fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> {
@@ -513,20 +694,19 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true)
- appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception })
+ 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)
- appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true)
+ appendSection("Enum Values", node.membersOrGroupMembers(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true)
+ appendSection("Constructors", node.membersOrGroupMembers(NodeKind.Constructor), omitSamePlatforms = true)
+ appendSection("Properties", node.membersOrGroupMembers(NodeKind.Property), omitSamePlatforms = true)
appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property))
- appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true)
+ appendSection("Functions", node.membersOrGroupMembers(NodeKind.Function), omitSamePlatforms = true)
appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function))
- appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
+ appendSection("Companion Object Properties", node.membersOrGroupMembers(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property))
- appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
+ appendSection("Companion Object Functions", node.membersOrGroupMembers(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function))
appendSection("Other members", node.members.filter {
it.kind !in setOf(
@@ -561,7 +741,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
if (node.kind == NodeKind.Module) {
appendHeader(3) { to.append("Index") }
node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes ->
- appendLink(link(node, allTypes, { "All Types" }))
+ appendLink(link(node, allTypes) { "All Types" })
}
}
}
@@ -582,26 +762,27 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendTable("Name", "Summary") {
appendTableBody {
for ((memberLocation, members) in membersMap) {
- val elementPlatforms = platformsOfItems(members, omitSamePlatforms)
- val platforms = if (platformsBasedOnMembers)
- members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
- else
- elementPlatforms
+ val platforms = effectivePlatformsForMembers(members)
+// val platforms = if (platformsBasedOnMembers)
+// members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+// else
+// elementPlatforms
+
+ val summarized = computeSummarySignatures(members)
+
+
appendIndexRow(platforms) {
appendTableCell {
- appendParagraph {
- appendLink(memberLocation)
- if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
- appendPlatforms(platforms)
- }
+ if (summarized.platformPlacement == Summarized.PlatformPlacement.Row) {
+ appendPlatforms(platforms)
}
- }
- appendTableCell {
- val breakdownBySummary = members.groupBy { it.summary }
- for ((summary, items) in breakdownBySummary) {
- appendSummarySignatures(items)
- appendContent(summary)
+ appendHeader(level = 4) {
+ appendLink(memberLocation)
+// if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
+// appendPlatforms(platforms)
+// }
}
+ appendSummarySignatures(summarized)
}
}
}
@@ -609,35 +790,90 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
}
}
- private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
- val platforms = platformsOfItems(items)
- if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
- return platforms
+//
+// private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
+// if (items.all { it.kind != NodeKind.Package && it.kind != NodeKind.Module && it.kind != NodeKind.ExternalClass }) {
+// return unionPlatformsOfItems(items)
+// }
+//
+// val platforms = platformsOfItems(items)
+// if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
+// return platforms
+// }
+// return emptySet()
+// }
+
+
+
+ private fun computeSummarySignatures(items: List<DocumentationNode>): Summarized =
+ Summarized(items.groupBy { it.summary }.mapValues { (_, nodes) ->
+ val nodesToAppend = nodes.flatMap { if(it.kind == NodeKind.GroupNode) it.origins else listOf(it) }
+
+ val summarySignature = languageService.summarizeSignatures(nodesToAppend)
+ if (summarySignature != null) {
+ mapOf(summarySignature to nodesToAppend)
+ } else {
+ nodesToAppend.groupBy {
+ languageService.render(it, RenderMode.SUMMARY)
+ }
+ }
+ })
+
+
+ private fun appendSummarySignatures(
+ summarized: Summarized
+ ) {
+ for(summary in summarized.data) {
+
+ appendAsSummaryGroup(summary.platforms) {
+ if (summarized.platformPlacement == Summarized.PlatformPlacement.Summary) {
+ appendPlatforms(summary.platforms)
+ }
+ appendContent(summary.content)
+ appendSoftLineBreak()
+ for (signature in summary.signatures) {
+ appendSignatures(
+ signature,
+ summarized.platformPlacement == Summarized.PlatformPlacement.Signature
+ )
+ }
+ }
+
}
- return emptySet()
}
- private fun appendSummarySignatures(items: List<DocumentationNode>) {
- val summarySignature = languageService.summarizeSignatures(items)
- if (summarySignature != null) {
- appendAsSignature(summarySignature) {
- summarySignature.appendSignature()
+ private fun appendSignatures(
+ signature: Summarized.SummarizedNodes,
+ withPlatforms: Boolean
+ ) {
+
+// val platforms = if (platformsBasedOnMembers)
+// items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
+// else
+// elementPlatforms
+
+
+ appendAsPlatformDependentBlock(signature.platforms) {
+ if (withPlatforms) {
+ appendPlatforms(signature.platforms)
}
- return
- }
- val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) }
- renderedSignatures.subList(0, renderedSignatures.size - 1).forEach {
- appendAsSignature(it) {
- it.appendSignature()
+ appendAsSignature(signature.content) {
+ signature.content.appendSignature()
}
- appendLine()
- }
- appendAsSignature(renderedSignatures.last()) {
- renderedSignatures.last().appendSignature()
+ appendSoftLineBreak()
}
}
}
+ private fun DocumentationNode.isClassLikeGroupNode(): Boolean {
+ if (kind != NodeKind.GroupNode) {
+ return false
+ }
+
+ return origins.all { it.kind in NodeKind.classLike }
+ }
+
+
inner class AllTypesNodeBuilder(val node: DocumentationNode)
: PageBuilder(listOf(node)) {
@@ -648,21 +884,23 @@ abstract class StructuredOutputBuilder(val to: StringBuilder,
appendTable("Name", "Summary") {
appendTableBody {
for (type in node.members) {
- appendTableRow {
- appendTableCell {
+ val platforms = effectivePlatformsForNode(type)
+ appendIndexRow(platforms) {
+ appendPlatforms(platforms)
+ appendHeader(level = 5) {
appendLink(link(node, type) {
if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName()
})
- if (type.kind == NodeKind.ExternalClass) {
- val packageName = type.owner?.name
- if (packageName != null) {
- appendText(" (extensions in package $packageName)")
- }
- }
}
- appendTableCell {
- appendContent(type.summary)
+
+ if (type.kind == NodeKind.ExternalClass) {
+ val packageName = type.owner?.name
+ if (packageName != null) {
+ appendText(" (extensions in package $packageName)")
+ }
}
+
+ appendContent(type.summary)
}
}
}
@@ -687,3 +925,78 @@ abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator
override final val linkExtension: String = extension) : FormatService {
}
+
+typealias PlatformsData = Map<String, Set<DocumentationNode>>
+
+fun memberPlatforms(node: DocumentationNode): PlatformsData {
+ val members = when {
+ node.kind == NodeKind.GroupNode -> node.origins
+ node.kind in NodeKind.classLike -> emptyList()
+ node.kind in NodeKind.memberLike -> emptyList()
+ else -> node.members
+ }
+
+ return members.map(::effectivePlatformsForNode).fold(mapOf(), ::mergePlatforms)
+}
+
+fun mergePlatforms(a: PlatformsData, b: PlatformsData): PlatformsData {
+ val mutable = a.toMutableMap()
+ b.forEach { (name, declarations) ->
+ mutable.merge(name, declarations) { a, b -> a.union(b) }
+ }
+ return mutable
+}
+
+fun effectivePlatformsForNode(node: DocumentationNode): PlatformsData {
+ val platforms = node.platforms + memberPlatforms(node).keys
+ return platforms.keysToMap { setOf(node) }
+}
+
+fun effectivePlatformsForMembers(nodes: Collection<DocumentationNode>): PlatformsData {
+ return nodes.map { effectivePlatformsForNode(it) }.reduce(::mergePlatforms)
+}
+
+fun mergeVersions(kotlinVersions: List<String>): String {
+ return kotlinVersions.distinct().min()!!
+}
+
+fun effectiveSinceKotlinForNode(node: DocumentationNode, baseVersion: String = "1.0"): String {
+ val members = when {
+ node.kind == NodeKind.GroupNode -> node.origins
+ node.kind in NodeKind.classLike -> emptyList()
+ node.kind in NodeKind.memberLike -> emptyList()
+ else -> node.members
+ }
+ val newBase = node.sinceKotlin ?: baseVersion
+ val memberVersion = if (members.isNotEmpty()) effectiveSinceKotlinForNodes(members, newBase) else newBase
+
+ return node.sinceKotlin ?: memberVersion
+}
+
+fun effectiveSinceKotlinForNodes(nodes: Collection<DocumentationNode>, baseVersion: String = "1.0"): String {
+ val map = nodes.map { effectiveSinceKotlinForNode(it, baseVersion) }
+ return mergeVersions(map)
+}
+
+fun samePlatforms(platformsPerNode: Collection<PlatformsData>): Boolean {
+
+ val first = platformsPerNode.firstOrNull()?.keys ?: return true
+ return platformsPerNode.all { it.keys == first }
+}
+
+fun locationHref(
+ from: Location,
+ to: DocumentationNode,
+ generator: NodeLocationAwareGenerator,
+ pathOnly: Boolean = false
+): String {
+ val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to
+ if (topLevelPage != null) {
+ val signature = to.detailOrNull(NodeKind.Signature)
+ return from.relativePathTo(
+ generator.location(topLevelPage),
+ (signature?.name ?: to.name).takeUnless { pathOnly }
+ )
+ }
+ return from.relativePathTo(generator.location(to))
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt
index c36f98eb..3c92d8ff 100644
--- a/core/src/main/kotlin/Formats/YamlOutlineService.kt
+++ b/core/src/main/kotlin/Formats/YamlOutlineService.kt
@@ -13,7 +13,7 @@ class YamlOutlineService @Inject constructor(
override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) {
val indent = " ".repeat(outlineLevel)
to.appendln("$indent- title: ${languageService.renderName(node)}")
- to.appendln("$indent url: ${generator.location(node).path}")
+ to.appendln("$indent url: ${generator.relativePathToLocation(node.path.first(), node)}")
}
override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) {