aboutsummaryrefslogtreecommitdiff
path: root/src/Kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'src/Kotlin')
-rw-r--r--src/Kotlin/DocumentationBuilder.kt101
-rw-r--r--src/Kotlin/KotlinLanguageService.kt54
2 files changed, 137 insertions, 18 deletions
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index aeea4a55..baa17b5c 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -6,9 +6,21 @@ import org.jetbrains.jet.lang.types.*
import org.jetbrains.jet.lang.types.lang.*
import org.jetbrains.jet.lang.resolve.name.*
import org.jetbrains.jet.lang.resolve.lazy.*
+import org.jetbrains.jet.lang.descriptors.annotations.Annotated
+import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor
+import org.jetbrains.jet.lang.resolve.DescriptorUtils
+import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant
+import com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.jet.lang.descriptors.impl.EnumEntrySyntheticClassDescriptor
public data class DocumentationOptions(val includeNonPublic: Boolean = false)
+private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
+ val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
+ val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
+ return package1 != null && package2 != null && package1.fqName == package2.fqName
+}
+
class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
@@ -73,11 +85,20 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
val superTypes = descriptor.getTypeConstructor().getSupertypes()
for (superType in superTypes) {
- if (superType.toString() != "Any")
+ if (!ignoreSupertype(superType))
appendType(superType, DocumentationNode.Kind.Supertype)
}
}
+ private fun ignoreSupertype(superType: JetType): Boolean {
+ val superClass = superType.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
+ if (superClass != null) {
+ val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
+ return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
+ }
+ return false
+ }
+
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
val prefix = when (projection.getProjectionKind()) {
Variance.IN_VARIANCE -> "in "
@@ -104,6 +125,16 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendProjection(typeArgument)
}
+ fun DocumentationNode.appendAnnotations(annotated: Annotated) {
+ annotated.getAnnotations().forEach {
+ val annotationNode = it.build()
+ if (annotationNode != null) {
+ append(annotationNode,
+ if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
+ }
+ }
+ }
+
fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
// do not include generated code
if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
@@ -120,15 +151,35 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
}
+ fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
+ externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
+ if (descriptor is CallableMemberDescriptor) {
+ val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
+ if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
+ val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
+ return externalClassNodes.getOrPut(fqName, {
+ val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
+ append(newNode, DocumentationReference.Kind.Member)
+ newNode
+ })
+ }
+ }
+ return this
+ }
+
fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
for ((name, parts) in fragments.groupBy { it.fqName }) {
descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
}
for ((packageName, declarations) in descriptors) {
- println(" package $packageName: ${declarations.count()} nodes")
+ println(" package $packageName: ${declarations.count()} declarations")
val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
- packageNode.appendChildren(declarations, DocumentationReference.Kind.Member)
+ val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
+ declarations.forEach { descriptor ->
+ val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
+ parent.appendChild(descriptor, DocumentationReference.Kind.Member)
+ }
append(packageNode, DocumentationReference.Kind.Member)
}
}
@@ -153,6 +204,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
ClassKind.CLASS_OBJECT -> Kind.Object
ClassKind.TRAIT -> Kind.Interface
ClassKind.ENUM_CLASS -> Kind.Enum
+ ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
ClassKind.ENUM_ENTRY -> Kind.EnumItem
else -> Kind.Class
}
@@ -168,6 +220,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
DocumentationReference.Kind.Member)
}
+ node.appendAnnotations(this)
register(this, node)
return node
}
@@ -182,6 +235,15 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
private fun DeclarationDescriptor.inClassObject() =
getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
+ fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
+ val extensionReceiver = getExtensionReceiverParameter()
+ if (extensionReceiver != null) {
+ val type = extensionReceiver.getType()
+ return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
+ }
+ return null
+ }
+
fun FunctionDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
@@ -189,6 +251,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
node.appendType(getReturnType())
+ node.appendAnnotations(this)
register(this, node)
return node
@@ -210,6 +273,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
node.appendType(getReturnType())
+ node.appendAnnotations(this)
getGetter()?.let {
if (!it.isDefault())
node.appendChild(it, DocumentationReference.Kind.Member)
@@ -226,6 +290,7 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
fun ValueParameterDescriptor.build(): DocumentationNode {
val node = DocumentationNode(this, Kind.Parameter)
node.appendType(getType())
+ node.appendAnnotations(this)
register(this, node)
return node
}
@@ -264,6 +329,36 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
return node
}
+ fun AnnotationDescriptor.build(): DocumentationNode? {
+ val annotationClass = getType().getConstructor().getDeclarationDescriptor()
+ if (ErrorUtils.isError(annotationClass)) {
+ return null
+ }
+ val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
+ val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
+ arguments.forEach {
+ val valueNode = it.second.build()
+ if (valueNode != null) {
+ val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
+ paramNode.append(valueNode, DocumentationReference.Kind.Detail)
+ node.append(paramNode, DocumentationReference.Kind.Detail)
+ }
+ }
+ return node
+ }
+
+ fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
+ val value = getValue()
+ val valueString = when(value) {
+ is String ->
+ "\"" + StringUtil.escapeStringCharacters(value) + "\""
+ is EnumEntrySyntheticClassDescriptor ->
+ value.getContainingDeclaration().getName().asString() + "." + value.getName()
+ else -> value?.toString()
+ }
+ return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
+ }
+
/**
* Generates cross-references for documentation such as extensions for a type, inheritors, etc
*
diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt
index a4016849..40f47639 100644
--- a/src/Kotlin/KotlinLanguageService.kt
+++ b/src/Kotlin/KotlinLanguageService.kt
@@ -1,28 +1,18 @@
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
-
/**
* Implements [LanguageService] and provides rendering of symbols in Kotlin language
*/
class KotlinLanguageService : LanguageService {
override fun render(node: DocumentationNode): ContentNode {
- return dokka.content {
+ return content {
when (node.kind) {
DocumentationNode.Kind.Package -> renderPackage(node)
DocumentationNode.Kind.Class,
DocumentationNode.Kind.Interface,
DocumentationNode.Kind.Enum,
DocumentationNode.Kind.EnumItem,
+ DocumentationNode.Kind.AnnotationClass,
DocumentationNode.Kind.Object -> renderClass(node)
DocumentationNode.Kind.TypeParameter -> renderTypeParameter(node)
@@ -135,6 +125,7 @@ class KotlinLanguageService : LanguageService {
}
private fun ContentNode.renderParameter(node: DocumentationNode) {
+ renderAnnotationsForNode(node)
identifier(node.name)
symbol(": ")
val parameterType = node.detail(DocumentationNode.Kind.Type)
@@ -171,24 +162,46 @@ class KotlinLanguageService : LanguageService {
}
}
+ private fun ContentNode.renderAnnotationsForNode(node: DocumentationNode) {
+ node.annotations.forEach {
+ renderAnnotation(it)
+ }
+ }
+
+ private fun ContentNode.renderAnnotation(node: DocumentationNode) {
+ identifier(node.name)
+ val parameters = node.details(DocumentationNode.Kind.Parameter)
+ if (!parameters.isEmpty()) {
+ symbol("(")
+ renderList(parameters) {
+ text(it.detail(DocumentationNode.Kind.Value).name)
+ }
+ symbol(")")
+ }
+ text(" ")
+ }
+
private fun ContentNode.renderClass(node: DocumentationNode) {
renderModifiersForNode(node)
+ renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Class -> keyword("class ")
DocumentationNode.Kind.Interface -> keyword("trait ")
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")
}
- identifier(node.name)
+ identifierOrDeprecated(node)
renderTypeParametersForNode(node)
renderSupertypesForNode(node)
}
private fun ContentNode.renderFunction(node: DocumentationNode) {
renderModifiersForNode(node)
+ renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name)
DocumentationNode.Kind.Function,
@@ -203,7 +216,7 @@ class KotlinLanguageService : LanguageService {
}
if (node.kind != org.jetbrains.dokka.DocumentationNode.Kind.Constructor)
- identifier(node.name)
+ identifierOrDeprecated(node)
symbol("(")
renderList(node.details(DocumentationNode.Kind.Parameter)) {
@@ -218,6 +231,7 @@ class KotlinLanguageService : LanguageService {
private fun ContentNode.renderProperty(node: DocumentationNode) {
renderModifiersForNode(node)
+ renderAnnotationsForNode(node)
when (node.kind) {
DocumentationNode.Kind.Property,
DocumentationNode.Kind.ClassObjectProperty -> keyword("val ")
@@ -230,8 +244,18 @@ class KotlinLanguageService : LanguageService {
symbol(".")
}
- identifier(node.name)
+ identifierOrDeprecated(node)
symbol(": ")
renderType(node.detail(DocumentationNode.Kind.Type))
}
+
+ fun ContentNode.identifierOrDeprecated(node: DocumentationNode) {
+ if (node.deprecation != null) {
+ val strike = ContentStrikethrough()
+ strike.identifier(node.name)
+ append(strike)
+ } else {
+ identifier(node.name)
+ }
+ }
} \ No newline at end of file