diff options
Diffstat (limited to 'src/Kotlin/KotlinLanguageService.kt')
-rw-r--r-- | src/Kotlin/KotlinLanguageService.kt | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt new file mode 100644 index 00000000..80467914 --- /dev/null +++ b/src/Kotlin/KotlinLanguageService.kt @@ -0,0 +1,229 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.symbol +import org.jetbrains.dokka.text +import org.jetbrains.dokka.identifier +import org.jetbrains.dokka.link +import org.jetbrains.dokka.keyword +import org.jetbrains.dokka.LanguageService +import org.jetbrains.dokka.DocumentationNode +import org.jetbrains.dokka.ContentNode +import org.jetbrains.dokka +import org.jetbrains.dokka.ContentText + +class KotlinLanguageService : LanguageService { + override fun render(node: DocumentationNode): ContentNode { + return dokka.content { + when (node.kind) { + DocumentationNode.Kind.Package -> renderPackage(node) + DocumentationNode.Kind.Class, + DocumentationNode.Kind.Interface, + DocumentationNode.Kind.Enum, + DocumentationNode.Kind.EnumItem, + DocumentationNode.Kind.Object -> renderClass(node) + + DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node) + DocumentationNode.Kind.Type, + DocumentationNode.Kind.UpperBound -> renderType(node) + + DocumentationNode.Kind.Modifier -> renderModifier(node) + DocumentationNode.Kind.Constructor, + DocumentationNode.Kind.Function -> renderFunction(node) + DocumentationNode.Kind.Property -> renderProperty(node) + else -> ContentText("${node.kind}: ${node.name}") + } + } + } + + override fun renderName(node: DocumentationNode): String { + return when (node.kind) { + DocumentationNode.Kind.Constructor -> node.owner!!.name + else -> node.name + } + } + + private fun ContentNode.renderPackage(node: DocumentationNode) { + keyword("package") + text(" ") + identifier(node.name) + } + + private fun ContentNode.renderList(nodes: List<DocumentationNode>, separator: String = ", ", renderItem: (DocumentationNode) -> Unit) { + if (nodes.none()) + return + renderItem(nodes.first()) + nodes.drop(1).forEach { + symbol(separator) + renderItem(it) + } + } + + private fun ContentNode.renderLinked(node: DocumentationNode, body: ContentNode.(DocumentationNode)->Unit) { + val to = node.links.firstOrNull() + if (to == null) + body(node) + else + link(to) { + body(node) + } + } + + private fun ContentNode.renderType(node: DocumentationNode) { + val typeArguments = node.details(DocumentationNode.Kind.Type) + if (node.name == "Function${typeArguments.count() - 1}") { + // lambda + symbol("(") + renderList(typeArguments.take(typeArguments.size - 1)) { + renderType(it) + } + symbol(")") + text(" ") + symbol("->") + text(" ") + 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)) { + renderType(it) + } + symbol(")") + text(" ") + symbol("->") + text(" ") + renderType(typeArguments.last()) + return + } + renderLinked(node) { identifier(it.name) } + if (typeArguments.any()) { + symbol("<") + renderList(typeArguments) { + renderType(it) + } + symbol(">") + } + } + + private fun ContentNode.renderModifier(node: DocumentationNode) { + when (node.name) { + "final", "internal" -> { + } + else -> keyword(node.name) + } + } + + private fun ContentNode.renderTypeParameter(node: DocumentationNode) { + val constraints = node.details(DocumentationNode.Kind.UpperBound) + identifier(node.name) + if (constraints.any()) { + symbol(" : ") + renderList(constraints) { + renderType(it) + } + } + } + + private fun ContentNode.renderParameter(node: DocumentationNode) { + identifier(node.name) + symbol(": ") + val parameterType = node.detail(DocumentationNode.Kind.Type) + renderType(parameterType) + } + + private fun ContentNode.renderTypeParametersForNode(node: DocumentationNode) { + val typeParameters = node.details(DocumentationNode.Kind.TypeParameter) + if (typeParameters.any()) { + symbol("<") + renderList(typeParameters) { + renderType(it) + } + symbol("> ") + } + } + + private fun ContentNode.renderSupertypesForNode(node: DocumentationNode) { + val supertypes = node.details(DocumentationNode.Kind.Supertype) + if (supertypes.any()) { + symbol(" : ") + renderList(supertypes) { + renderType(it) + } + } + } + + private fun ContentNode.renderModifiersForNode(node: DocumentationNode) { + val modifiers = node.details(DocumentationNode.Kind.Modifier) + for (it in modifiers) { + if (node.kind == org.jetbrains.dokka.DocumentationNode.Kind.Interface && it.name == "abstract") + continue + renderModifier(it) + text(" ") + } + } + + private fun ContentNode.renderClass(node: DocumentationNode) { + renderModifiersForNode(node) + when (node.kind) { + DocumentationNode.Kind.Class -> keyword("class ") + DocumentationNode.Kind.Interface -> keyword("trait ") + DocumentationNode.Kind.Enum -> keyword("enum class ") + DocumentationNode.Kind.EnumItem -> keyword("enum val ") + DocumentationNode.Kind.Object -> keyword("object ") + else -> throw IllegalArgumentException("Node $node is not a class-like object") + } + + identifier(node.name) + renderTypeParametersForNode(node) + renderSupertypesForNode(node) + } + + private fun ContentNode.renderFunction(node: DocumentationNode) { + renderModifiersForNode(node) + when (node.kind) { + DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name) + DocumentationNode.Kind.Function -> keyword("fun ") + else -> throw IllegalArgumentException("Node $node is not a function-like object") + } + renderTypeParametersForNode(node) + val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull() + if (receiver != null) { + renderType(receiver.detail(DocumentationNode.Kind.Type)) + symbol(".") + } + + if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor) + identifier(node.name) + + symbol("(") + renderList(node.details(DocumentationNode.Kind.Parameter)) { + renderParameter(it) + } + symbol(")") + if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor) { + symbol(": ") + renderType(node.detail(DocumentationNode.Kind.Type)) + } + } + + private fun ContentNode.renderProperty(node: DocumentationNode) { + renderModifiersForNode(node) + when (node.kind) { + DocumentationNode.Kind.Property -> keyword("val ") + else -> throw IllegalArgumentException("Node $node is not a property") + } + renderTypeParametersForNode(node) + val receiver = node.details(DocumentationNode.Kind.Receiver).singleOrNull() + if (receiver != null) { + renderType(receiver.detail(DocumentationNode.Kind.Type)) + symbol(".") + } + + identifier(node.name) + symbol(": ") + renderType(node.detail(DocumentationNode.Kind.Type)) + } +}
\ No newline at end of file |