aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Formats/HtmlFormatService.kt2
-rw-r--r--src/Formats/KotlinWebsiteFormatService.kt1
-rw-r--r--src/Formats/StructuredFormatService.kt51
-rw-r--r--src/Kotlin/DocumentationBuilder.kt9
-rw-r--r--src/Kotlin/KotlinLanguageService.kt80
-rw-r--r--src/Languages/JavaLanguageService.kt2
-rw-r--r--src/Languages/LanguageService.kt12
-rw-r--r--src/Model/Content.kt1
-rw-r--r--src/Model/DocumentationNode.kt6
-rw-r--r--src/Model/DocumentationReference.kt1
-rw-r--r--test/data/format/summarizeSignatures.kt20
-rw-r--r--test/data/format/summarizeSignatures.md22
-rw-r--r--test/src/format/MarkdownFormatTest.kt6
13 files changed, 183 insertions, 30 deletions
diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt
index 74b10255..8e38a32c 100644
--- a/src/Formats/HtmlFormatService.kt
+++ b/src/Formats/HtmlFormatService.kt
@@ -152,7 +152,7 @@ fun formatPageTitle(node: DocumentationNode): String {
if (path.size == 1) {
return path.first().name
}
- val qualifiedName = path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
+ val qualifiedName = node.qualifiedName()
if (qualifiedName.length == 0 && path.size == 2) {
return path.first().name + " / root package"
}
diff --git a/src/Formats/KotlinWebsiteFormatService.kt b/src/Formats/KotlinWebsiteFormatService.kt
index 3a9fc6cd..97419c58 100644
--- a/src/Formats/KotlinWebsiteFormatService.kt
+++ b/src/Formats/KotlinWebsiteFormatService.kt
@@ -90,6 +90,7 @@ public class KotlinWebsiteFormatService(locationService: LocationService,
private fun identifierClassName(kind: IdentifierKind) = when(kind) {
IdentifierKind.ParameterName -> "parameterName"
+ IdentifierKind.SummarizedTypeName -> "summarizedTypeName"
else -> "identifier"
}
}
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index a9b058d1..405a8b10 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -221,7 +221,7 @@ public abstract class StructuredFormatService(locationService: LocationService,
}
}
- private fun StructuredFormatService.appendSection(location: Location, caption: String, nodes: List<DocumentationNode>, node: DocumentationNode, to: StringBuilder) {
+ private fun appendSection(location: Location, caption: String, nodes: List<DocumentationNode>, node: DocumentationNode, to: StringBuilder) {
if (nodes.any()) {
appendHeader(to, caption, 3)
@@ -238,24 +238,7 @@ public abstract class StructuredFormatService(locationService: LocationService,
appendTableCell(to) {
val breakdownBySummary = members.groupBy { formatText(location, it.summary) }
for ((summary, items) in breakdownBySummary) {
- val signatureTexts = items.map { signature ->
- val signatureText = languageService.render(signature, RenderMode.SUMMARY)
- if (signatureText is ContentBlock && signatureText.isEmpty()) {
- ""
- } else {
- val signatureAsCode = ContentCode()
- signatureAsCode.append(signatureText)
- formatText(location, signatureAsCode)
- }
- }
- signatureTexts.subList(0, signatureTexts.size -1).forEach {
- appendAsSignature(to) {
- appendLine(to, it)
- }
- }
- appendAsSignature(to) {
- to.append(signatureTexts.last())
- }
+ appendSummarySignatures(items, location, to)
if (!summary.isEmpty()) {
to.append(summary)
}
@@ -268,6 +251,36 @@ public abstract class StructuredFormatService(locationService: LocationService,
}
}
+ private fun appendSummarySignatures(items: List<DocumentationNode>, location: Location, to: StringBuilder) {
+ val summarySignature = languageService.summarizeSignatures(items)
+ if (summarySignature != null) {
+ appendAsSignature(to) {
+ val signatureAsCode = ContentCode()
+ signatureAsCode.append(summarySignature)
+ to.append(formatText(location, signatureAsCode))
+ }
+ return
+ }
+ val signatureTexts = items.map { signature ->
+ val signatureText = languageService.render(signature, RenderMode.SUMMARY)
+ if (signatureText is ContentBlock && signatureText.isEmpty()) {
+ ""
+ } else {
+ val signatureAsCode = ContentCode()
+ signatureAsCode.append(signatureText)
+ formatText(location, signatureAsCode)
+ }
+ }
+ signatureTexts.subList(0, signatureTexts.size - 1).forEach {
+ appendAsSignature(to) {
+ appendLine(to, it)
+ }
+ }
+ appendAsSignature(to) {
+ to.append(signatureTexts.last())
+ }
+ }
+
override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
val breakdownByLocation = nodes.groupBy { node ->
formatBreadcrumbs(node.path.filterNot { it.name.isEmpty() }.map { link(node, it) })
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index a9ac8551..b30d59d7 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -229,8 +229,8 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
}
}
- fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
- refGraph.link(node, descriptor.signature(), DocumentationReference.Kind.Link)
+ fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
+ refGraph.link(node, descriptor.signature(), kind)
}
fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: DocumentationReference.Kind) {
@@ -328,8 +328,9 @@ class DocumentationBuilder(val resolutionFacade: ResolutionFacade,
if (jetType.isMarkedNullable) {
node.appendTextNode("?", Kind.NullabilityModifier)
}
- if (classifierDescriptor != null && !classifierDescriptor.isBoringBuiltinClass()) {
- link(node, classifierDescriptor)
+ if (classifierDescriptor != null) {
+ link(node, classifierDescriptor,
+ if (classifierDescriptor.isBoringBuiltinClass()) DocumentationReference.Kind.HiddenLink else DocumentationReference.Kind.Link)
}
append(node, DocumentationReference.Kind.Detail)
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index 12a0bcda..1e7050ef 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -43,6 +43,74 @@ class KotlinLanguageService : LanguageService {
}
}
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
+ if (nodes.size < 2) return null
+ val receiverKind = nodes.getReceiverKind() ?: return null
+ val functionWithTypeParameter = nodes.firstOrNull { it.details(DocumentationNode.Kind.TypeParameter).any() } ?: return null
+ return content {
+ val typeParameter = functionWithTypeParameter.details(DocumentationNode.Kind.TypeParameter).first()
+ renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ }
+ }
+
+ private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? {
+ val qNames = map { it.getReceiverQName() }.filterNotNull()
+ if (qNames.size != size)
+ return null
+
+ return ReceiverKind.values.firstOrNull { kind -> qNames.all { it in kind.classes } }
+ }
+
+ private fun DocumentationNode.getReceiverQName(): String? {
+ if (kind != DocumentationNode.Kind.Function) return null
+ val receiver = details(DocumentationNode.Kind.Receiver).singleOrNull() ?: return null
+ val receiverType = receiver.detail(DocumentationNode.Kind.Type)
+ return (receiverType.links.firstOrNull() ?: receiverType.hiddenLinks.firstOrNull())?.qualifiedName()
+ }
+
+ companion object {
+ private val arrayClasses = setOf(
+ "kotlin.Array",
+ "kotlin.BooleanArray",
+ "kotlin.ByteArray",
+ "kotlin.CharArray",
+ "kotlin.ShortArray",
+ "kotlin.IntArray",
+ "kotlin.LongArray",
+ "kotlin.FloatArray",
+ "kotlin.DoubleArray"
+ )
+
+ private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses
+
+ private val iterableClasses = setOf(
+ "kotlin.Collection",
+ "kotlin.Sequence",
+ "kotlin.Iterable",
+ "kotlin.Map",
+ "kotlin.String",
+ "kotlin.CharSequence") + arrayOrListClasses
+ }
+
+ private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
+ ARRAY("any_array", arrayClasses),
+ ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses),
+ ITERABLE("any_iterable", iterableClasses),
+ }
+
+ interface SignatureMapper {
+ fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
+ }
+
+ private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String): SignatureMapper {
+ override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
+ val newReceiver = ContentEmphasis()
+ newReceiver.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
+ to.append(newReceiver)
+ to.text("<$typeParameterName>")
+ }
+ }
+
private fun ContentBlock.renderPackage(node: DocumentationNode) {
keyword("package")
text(" ")
@@ -238,7 +306,9 @@ class KotlinLanguageService : LanguageService {
renderSupertypesForNode(node)
}
- private fun ContentBlock.renderFunction(node: DocumentationNode, renderMode: RenderMode) {
+ private fun ContentBlock.renderFunction(node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
}
@@ -253,9 +323,15 @@ class KotlinLanguageService : LanguageService {
if (node.details(DocumentationNode.Kind.TypeParameter).any()) {
text(" ")
}
+
val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull()
if (receiver != null) {
- renderType(receiver.detail(DocumentationNode.Kind.Type))
+ if (signatureMapper != null) {
+ signatureMapper.renderReceiver(receiver, this)
+ }
+ else {
+ renderType(receiver.detail(DocumentationNode.Kind.Type))
+ }
symbol(".")
}
diff --git a/src/Languages/JavaLanguageService.kt b/src/Languages/JavaLanguageService.kt
index bcd058b5..cf0f127b 100644
--- a/src/Languages/JavaLanguageService.kt
+++ b/src/Languages/JavaLanguageService.kt
@@ -34,6 +34,8 @@ public class JavaLanguageService : LanguageService {
}
}
+ override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null
+
private fun renderPackage(node: DocumentationNode): String {
return "package ${node.name}"
}
diff --git a/src/Languages/LanguageService.kt b/src/Languages/LanguageService.kt
index c587335a..b0f4bbc9 100644
--- a/src/Languages/LanguageService.kt
+++ b/src/Languages/LanguageService.kt
@@ -12,13 +12,19 @@ interface LanguageService {
}
/**
- * Renders a [node](DocumentationNode) as a class, function, property or other signature in a target language.
- * $node: A [DocumentationNode] to render
- * $returns: [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService]
+ * Renders a [node] as a class, function, property or other signature in a target language.
+ * @param node A [DocumentationNode] to render
+ * @return [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService]
*/
fun render(node: DocumentationNode, renderMode: RenderMode = RenderMode.FULL): ContentNode
/**
+ * Tries to summarize the signatures of the specified documentation nodes in a compact representation.
+ * Returns the representation if successful, or null if the signatures could not be summarized.
+ */
+ fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode?
+
+ /**
* Renders [node] as a named representation in the target language
*
* For example:
diff --git a/src/Model/Content.kt b/src/Model/Content.kt
index 38a42afc..4dfd3606 100644
--- a/src/Model/Content.kt
+++ b/src/Model/Content.kt
@@ -24,6 +24,7 @@ enum class IdentifierKind {
TypeName,
ParameterName,
AnnotationName,
+ SummarizedTypeName,
Other
}
diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt
index 04285594..c082365f 100644
--- a/src/Model/DocumentationNode.kt
+++ b/src/Model/DocumentationNode.kt
@@ -1,6 +1,6 @@
package org.jetbrains.dokka
-import java.util.LinkedHashSet
+import java.util.*
public open class DocumentationNode(val name: String,
content: Content,
@@ -27,6 +27,8 @@ public open class DocumentationNode(val name: String,
get() = references(DocumentationReference.Kind.Override).map { it.to }
public val links: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Link).map { it.to }
+ public val hiddenLinks: List<DocumentationNode>
+ get() = references(DocumentationReference.Kind.HiddenLink).map { it.to }
public val annotations: List<DocumentationNode>
get() = references(DocumentationReference.Kind.Annotation).map { it.to }
public val deprecation: DocumentationNode?
@@ -147,3 +149,5 @@ fun DocumentationNode.appendTextNode(text: String,
refKind: DocumentationReference.Kind = DocumentationReference.Kind.Detail) {
append(DocumentationNode(text, Content.Empty, kind), refKind)
}
+
+fun DocumentationNode.qualifiedName() = path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
diff --git a/src/Model/DocumentationReference.kt b/src/Model/DocumentationReference.kt
index a61ac65f..bbdd026d 100644
--- a/src/Model/DocumentationReference.kt
+++ b/src/Model/DocumentationReference.kt
@@ -6,6 +6,7 @@ public data class DocumentationReference(val from: DocumentationNode, val to: Do
Member,
Detail,
Link,
+ HiddenLink,
Extension,
Inheritor,
Override,
diff --git a/test/data/format/summarizeSignatures.kt b/test/data/format/summarizeSignatures.kt
new file mode 100644
index 00000000..1d875a50
--- /dev/null
+++ b/test/data/format/summarizeSignatures.kt
@@ -0,0 +1,20 @@
+package kotlin
+
+class Array<T>
+class IntArray
+class CharArray
+
+/**
+ * Returns true if foo.
+ */
+fun IntArray.foo(predicate: (Int) -> Boolean): Boolean = false
+
+/**
+ * Returns true if foo.
+ */
+fun CharArray.foo(predicate: (Char) -> Boolean): Boolean = false
+
+/**
+ * Returns true if foo.
+ */
+fun <T> Array<T>.foo(predicate: (T) -> Boolean): Boolean = false
diff --git a/test/data/format/summarizeSignatures.md b/test/data/format/summarizeSignatures.md
new file mode 100644
index 00000000..2dcb31a2
--- /dev/null
+++ b/test/data/format/summarizeSignatures.md
@@ -0,0 +1,22 @@
+[test](test/index) / [kotlin](test/kotlin/index)
+
+
+## Package kotlin
+
+
+### Types
+
+
+| [Array](test/kotlin/-array/index) | `class Array&lt;T&gt;` |
+| [CharArray](test/kotlin/-char-array/index) | `class CharArray` |
+| [IntArray](test/kotlin/-int-array/index) | `class IntArray` |
+
+
+### Functions
+
+
+| [foo](test/kotlin/foo) | `fun &lt;T&gt; *any_array*&lt;T&gt;.foo(predicate:&nbsp;(T)&nbsp;-&gt;&nbsp;Boolean): Boolean`
+Returns true if foo.
+
+ |
+
diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt
index 76ead835..d981561f 100644
--- a/test/src/format/MarkdownFormatTest.kt
+++ b/test/src/format/MarkdownFormatTest.kt
@@ -179,4 +179,10 @@ public class MarkdownFormatTest {
markdownService.appendNodes(tempLocation, output, model.members.single().members)
}
}
+
+ @Test fun summarizeSignatures() {
+ verifyOutput("test/data/format/summarizeSignatures.kt", ".md") { model, output ->
+ markdownService.appendNodes(tempLocation, output, model.members)
+ }
+ }
}