diff options
Diffstat (limited to 'src/Kotlin/KotlinLanguageService.kt')
-rw-r--r-- | src/Kotlin/KotlinLanguageService.kt | 248 |
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() = |