aboutsummaryrefslogtreecommitdiff
path: root/src/Kotlin/KotlinLanguageService.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/Kotlin/KotlinLanguageService.kt')
-rw-r--r--src/Kotlin/KotlinLanguageService.kt248
1 files changed, 171 insertions, 77 deletions
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index 75675c6f..3c4b974f 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -6,7 +6,7 @@ import org.jetbrains.dokka.LanguageService.RenderMode
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
class KotlinLanguageService : LanguageService {
- private val visibilityModifiers = setOf("public", "protected", "private")
+ private val fullOnlyModifiers = setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
return content {
@@ -21,9 +21,9 @@ class KotlinLanguageService : LanguageService {
DocumentationNode.Kind.EnumItem,
DocumentationNode.Kind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name)
- DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node)
+ DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node, renderMode)
DocumentationNode.Kind.Type,
- DocumentationNode.Kind.UpperBound -> renderType(node)
+ DocumentationNode.Kind.UpperBound -> renderType(node, renderMode)
DocumentationNode.Kind.Modifier -> renderModifier(node)
DocumentationNode.Kind.Constructor,
@@ -43,6 +43,77 @@ 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()
+ if (functionWithTypeParameter.kind == DocumentationNode.Kind.Function) {
+ renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
+ }
+ else {
+ renderProperty(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 && kind != DocumentationNode.Kind.Property) 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) {
+ to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
+ to.text("<$typeParameterName>")
+ }
+ }
+
private fun ContentBlock.renderPackage(node: DocumentationNode) {
keyword("package")
text(" ")
@@ -75,42 +146,36 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderType(node: DocumentationNode) {
- val typeArguments = node.details(DocumentationNode.Kind.Type)
+ private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
+ var typeArguments = node.details(DocumentationNode.Kind.Type)
if (node.name == "Function${typeArguments.count() - 1}") {
// lambda
- symbol("(")
- renderList(typeArguments.take(typeArguments.size() - 1), noWrap = true) {
- renderType(it)
+ val isExtension = node.annotations.any { it.name == "Extension" }
+ if (isExtension) {
+ renderType(typeArguments.first(), renderMode)
+ symbol(".")
+ typeArguments = typeArguments.drop(1)
}
- symbol(")")
- nbsp()
- symbol("->")
- nbsp()
- renderType(typeArguments.last())
- return
- }
- if (node.name == "ExtensionFunction${typeArguments.count() - 2}") {
- // extension lambda
- renderType(typeArguments.first())
- symbol(".")
symbol("(")
- renderList(typeArguments.drop(1).take(typeArguments.size() - 2), noWrap = true) {
- renderType(it)
+ renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
+ renderType(it, renderMode)
}
symbol(")")
nbsp()
symbol("->")
nbsp()
- renderType(typeArguments.last())
+ renderType(typeArguments.last(), renderMode)
return
}
- renderSingleModifier(node)
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
+ renderModifiersForNode(node, renderMode, true)
renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) }
if (typeArguments.any()) {
symbol("<")
renderList(typeArguments, noWrap = true) {
- renderType(it)
+ renderType(it, renderMode)
}
symbol(">")
}
@@ -120,18 +185,23 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderModifier(node: DocumentationNode) {
+ private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) {
when (node.name) {
- "final", "internal", "var" -> {}
+ "final", "public", "var" -> {}
else -> {
keyword(node.name)
- text(" ")
+ if (nowrap) {
+ nbsp()
+ }
+ else {
+ text(" ")
+ }
}
}
}
- private fun ContentBlock.renderTypeParameter(node: DocumentationNode) {
- renderSingleModifier(node)
+ private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) {
+ renderModifiersForNode(node, renderMode, true)
identifier(node.name)
@@ -141,26 +211,20 @@ class KotlinLanguageService : LanguageService {
symbol(":")
nbsp()
renderList(constraints, noWrap=true) {
- renderType(it)
+ renderType(it, renderMode)
}
}
}
-
- private fun ContentBlock.renderSingleModifier(node: DocumentationNode) {
- val modifier = node.details(DocumentationNode.Kind.Modifier).singleOrNull()
- if (modifier != null) {
- keyword(modifier.name)
- nbsp()
+ private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
}
- }
-
- private fun ContentBlock.renderParameter(node: DocumentationNode) {
- renderAnnotationsForNode(node)
+ renderModifiersForNode(node, renderMode)
identifier(node.name, IdentifierKind.ParameterName)
symbol(":")
nbsp()
val parameterType = node.detail(DocumentationNode.Kind.Type)
- renderType(parameterType)
+ renderType(parameterType, renderMode)
val valueNode = node.details(DocumentationNode.Kind.Value).firstOrNull()
if (valueNode != null) {
nbsp()
@@ -170,38 +234,41 @@ class KotlinLanguageService : LanguageService {
}
}
- private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) {
+ private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) {
val typeParameters = node.details(DocumentationNode.Kind.TypeParameter)
if (typeParameters.any()) {
symbol("<")
renderList(typeParameters) {
- renderTypeParameter(it)
+ renderTypeParameter(it, renderMode)
}
symbol(">")
}
}
- private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode) {
+ private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
val supertypes = node.details(DocumentationNode.Kind.Supertype)
if (supertypes.any()) {
nbsp()
symbol(":")
nbsp()
renderList(supertypes) {
- renderType(it)
+ indentedSoftLineBreak()
+ renderType(it, renderMode)
}
}
}
- private fun ContentBlock.renderModifiersForNode(node: DocumentationNode, renderMode: RenderMode) {
+ private fun ContentBlock.renderModifiersForNode(node: DocumentationNode,
+ renderMode: RenderMode,
+ nowrap: Boolean = false) {
val modifiers = node.details(DocumentationNode.Kind.Modifier)
for (it in modifiers) {
if (node.kind == org.jetbrains.dokka.DocumentationNode.Kind.Interface && it.name == "abstract")
continue
- if (renderMode == RenderMode.SUMMARY && it.name in visibilityModifiers) {
+ if (renderMode == RenderMode.SUMMARY && it.name in fullOnlyModifiers) {
continue
}
- renderModifier(it)
+ renderModifier(it, nowrap)
}
}
@@ -212,7 +279,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderAnnotation(node: DocumentationNode) {
- identifier(node.name, IdentifierKind.AnnotationName)
+ identifier("@" + node.name, IdentifierKind.AnnotationName)
val parameters = node.details(DocumentationNode.Kind.Parameter)
if (!parameters.isEmpty()) {
symbol("(")
@@ -225,82 +292,109 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
renderModifiersForNode(node, renderMode)
- renderAnnotationsForNode(node)
when (node.kind) {
- DocumentationNode.Kind.Class -> keyword("class ")
+ DocumentationNode.Kind.Class,
+ DocumentationNode.Kind.AnnotationClass,
+ DocumentationNode.Kind.Enum -> keyword("class ")
DocumentationNode.Kind.Interface -> keyword("interface ")
- DocumentationNode.Kind.Enum -> keyword("enum class ")
- DocumentationNode.Kind.AnnotationClass -> keyword("annotation class ")
DocumentationNode.Kind.EnumItem -> keyword("enum val ")
DocumentationNode.Kind.Object -> keyword("object ")
else -> throw IllegalArgumentException("Node $node is not a class-like object")
}
identifierOrDeprecated(node)
- renderTypeParametersForNode(node)
- renderSupertypesForNode(node)
+ renderTypeParametersForNode(node, renderMode)
+ renderSupertypesForNode(node, renderMode)
}
- 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)
+ }
renderModifiersForNode(node, renderMode)
- renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name)
DocumentationNode.Kind.Function,
DocumentationNode.Kind.CompanionObjectFunction -> keyword("fun ")
else -> throw IllegalArgumentException("Node $node is not a function-like object")
}
- renderTypeParametersForNode(node)
+ renderTypeParametersForNode(node, renderMode)
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))
- symbol(".")
- }
+
+ renderReceiver(node, renderMode, signatureMapper)
if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor)
identifierOrDeprecated(node)
symbol("(")
- renderList(node.details(DocumentationNode.Kind.Parameter)) {
- renderParameter(it)
+ val parameters = node.details(DocumentationNode.Kind.Parameter)
+ renderList(parameters) {
+ indentedSoftLineBreak()
+ renderParameter(it, renderMode)
}
- symbol(")")
if (needReturnType(node)) {
+ if (parameters.isNotEmpty()) {
+ softLineBreak()
+ }
+ symbol(")")
symbol(": ")
- renderType(node.detail(DocumentationNode.Kind.Type))
+ renderType(node.detail(DocumentationNode.Kind.Type), renderMode)
+ }
+ else {
+ symbol(")")
+ }
+ }
+
+ private fun ContentBlock.renderReceiver(node: DocumentationNode, renderMode: RenderMode, signatureMapper: SignatureMapper?) {
+ val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull()
+ if (receiver != null) {
+ if (signatureMapper != null) {
+ signatureMapper.renderReceiver(receiver, this)
+ } else {
+ renderType(receiver.detail(DocumentationNode.Kind.Type), renderMode)
+ }
+ symbol(".")
}
}
private fun needReturnType(node: DocumentationNode) = when(node.kind) {
DocumentationNode.Kind.Constructor -> false
- else -> true
+ else -> !node.isUnitReturnType()
}
- private fun ContentBlock.renderProperty(node: DocumentationNode, renderMode: RenderMode) {
+ fun DocumentationNode.isUnitReturnType(): Boolean =
+ detail(DocumentationNode.Kind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
+
+ private fun ContentBlock.renderProperty(node: DocumentationNode,
+ renderMode: RenderMode,
+ signatureMapper: SignatureMapper? = null) {
+ if (renderMode == RenderMode.FULL) {
+ renderAnnotationsForNode(node)
+ }
renderModifiersForNode(node, renderMode)
- renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Property,
DocumentationNode.Kind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ")
else -> throw IllegalArgumentException("Node $node is not a property")
}
- renderTypeParametersForNode(node)
+ renderTypeParametersForNode(node, renderMode)
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))
- symbol(".")
- }
+
+ renderReceiver(node, renderMode, signatureMapper)
identifierOrDeprecated(node)
symbol(": ")
- renderType(node.detail(DocumentationNode.Kind.Type))
+ renderType(node.detail(DocumentationNode.Kind.Type), renderMode)
}
fun DocumentationNode.getPropertyKeyword() =