aboutsummaryrefslogtreecommitdiff
path: root/src/Kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'src/Kotlin')
-rw-r--r--src/Kotlin/Diagnostics.kt40
-rw-r--r--src/Kotlin/DocumentationBuilder.kt326
-rw-r--r--src/Kotlin/DocumentationContext.kt18
-rw-r--r--src/Kotlin/DocumentationNodeBuilder.kt15
4 files changed, 342 insertions, 57 deletions
diff --git a/src/Kotlin/Diagnostics.kt b/src/Kotlin/Diagnostics.kt
deleted file mode 100644
index 5548bc01..00000000
--- a/src/Kotlin/Diagnostics.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.jetbrains.dokka
-
-import org.jetbrains.jet.lang.descriptors.*
-import org.jetbrains.jet.lang.resolve.name.*
-import org.jetbrains.jet.lang.resolve.BindingContext
-
-fun DocumentationContext.checkResolveChildren(node: DocumentationNode) {
- if (node.kind != DocumentationNode.Kind.Module && node.kind != DocumentationNode.Kind.Package) {
- // TODO: we don't resolve packages and modules for now
-
- val parentScope = getResolutionScope(node)
- for (item in node.details + node.members) {
- val symbolName = item.name
- val symbol: DeclarationDescriptor? = when (item.kind) {
- DocumentationNode.Kind.Modifier -> continue // do not resolve modifiers, they are not names
- DocumentationNode.Kind.Receiver -> continue // what is receiver's name in platform?
- DocumentationNode.Kind.Parameter -> parentScope.getLocalVariable(Name.guess(symbolName))
- DocumentationNode.Kind.Function -> parentScope.getFunctions(Name.guess(symbolName)).firstOrNull()
- DocumentationNode.Kind.Property -> parentScope.getProperties(Name.guess(symbolName)).firstOrNull()
- DocumentationNode.Kind.Constructor -> parentScope.getFunctions(Name.guess(symbolName)).firstOrNull()
- else -> parentScope.getClassifier(Name.guess(symbolName))
- }
-
- if (symbol == null)
- println("WARNING: Cannot resolve $item in ${path(node)}")
- }
- }
-
- for (reference in node.allReferences().filterNot { it.kind == DocumentationReference.Kind.Owner }) {
- checkResolveChildren(reference.to)
- }
-}
-
-fun path(node: DocumentationNode): String {
- val owner = node.owner
- if (owner != null)
- return "$node in ${path(owner)}"
- else
- return "$node"
-} \ No newline at end of file
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
new file mode 100644
index 00000000..211697cd
--- /dev/null
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -0,0 +1,326 @@
+package org.jetbrains.dokka
+
+import org.jetbrains.jet.lang.descriptors.*
+import org.jetbrains.dokka.DocumentationNode.Kind
+import org.jetbrains.jet.lang.types.TypeProjection
+import org.jetbrains.jet.lang.types.Variance
+import org.jetbrains.jet.lang.types.JetType
+import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
+import org.jetbrains.jet.lang.resolve.BindingContext
+import org.jetbrains.jet.lang.resolve.name.Name
+import org.jetbrains.jet.lang.resolve.scopes.JetScope
+import org.jetbrains.jet.lang.psi.JetFile
+import org.jetbrains.jet.lang.resolve.name.FqName
+
+class DocumentationBuilder(val context: BindingContext, val options: DocumentationOptions) {
+ val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
+ val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
+ val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
+ val packages = hashMapOf<FqName, DocumentationNode>()
+
+ fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
+ val docText = context.getDocumentationElements(descriptor).map { it.extractText() }.join("\n")
+ val tree = MarkdownProcessor.parse(docText)
+ //println(tree.toTestString())
+ val content = tree.toContent()
+ return content
+ }
+
+ fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
+ links.put(node, descriptor)
+ }
+
+ fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
+ descriptorToNode.put(descriptor, node)
+ nodeToDescriptor.put(node, descriptor)
+ }
+
+ fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
+ val doc = parseDocumentation(descriptor)
+ val node = DocumentationNode(descriptor.getName().asString(), doc, kind)
+ if (descriptor is MemberDescriptor) {
+ if (descriptor !is ConstructorDescriptor) {
+ node.appendModality(descriptor)
+ }
+ node.appendVisibility(descriptor)
+ }
+ return node
+ }
+
+ fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
+ addReferenceTo(child, kind)
+ when (kind) {
+ DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
+ DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
+ DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
+ }
+ }
+
+ fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
+ val modifier = descriptor.getModality().name().toLowerCase()
+ val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
+ append(node, DocumentationReference.Kind.Detail)
+ }
+
+ fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
+ val modifier = descriptor.getVisibility().toString()
+ val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
+ append(node, DocumentationReference.Kind.Detail)
+ }
+
+ fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
+ val superTypes = descriptor.getTypeConstructor().getSupertypes()
+ for (superType in superTypes) {
+ if (superType.toString() != "Any")
+ appendType(superType, DocumentationNode.Kind.Supertype)
+ }
+ }
+
+ fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
+ val prefix = when (projection.getProjectionKind()) {
+ Variance.IN_VARIANCE -> "in "
+ Variance.OUT_VARIANCE -> "out "
+ else -> ""
+ }
+ appendType(projection.getType(), kind, prefix)
+ }
+
+ fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
+ if (jetType == null)
+ return
+ val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
+ val name = when (classifierDescriptor) {
+ is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isNullable()) "?" else ""
+ else -> "<anonymous>"
+ }
+ val node = DocumentationNode(name, Content.Empty, kind)
+ if (classifierDescriptor != null)
+ link(node, classifierDescriptor)
+
+ append(node, DocumentationReference.Kind.Detail)
+ for (typeArgument in jetType.getArguments())
+ node.appendProjection(typeArgument)
+ }
+
+ val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
+
+ fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
+ // do not include generated code
+ if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
+ return
+
+ if (options.includeNonPublic
+ || descriptor !is MemberDescriptor
+ || descriptor.getVisibility() in visibleToDocumentation) {
+ append(descriptor.build(), kind)
+ }
+ }
+
+ fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
+ descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
+ }
+
+ fun DocumentationNode.appendFiles(sourceFiles : List<JetFile>) {
+ for (sourceFile in sourceFiles) {
+ val fragment = context.getPackageFragment(sourceFile)!!
+ val packageNode = packages.getOrPut(fragment.fqName) {
+ val packageNode = DocumentationNode(fragment.fqName.asString(), Content.Empty, Kind.Package)
+ append(packageNode, DocumentationReference.Kind.Member)
+ packageNode
+ }
+ packageNode.appendChildren(fragment.getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
+ }
+ }
+
+ fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
+ is ClassDescriptor -> build()
+ is ConstructorDescriptor -> build()
+ is ScriptDescriptor -> build()
+ is FunctionDescriptor -> build()
+ is PropertyDescriptor -> build()
+ is PropertyGetterDescriptor -> build()
+ is PropertySetterDescriptor -> build()
+ is TypeParameterDescriptor -> build()
+ is ValueParameterDescriptor -> build()
+ is ReceiverParameterDescriptor -> build()
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+ fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
+ fun ClassDescriptor.build(): DocumentationNode {
+ val kind = when (getKind()) {
+ ClassKind.OBJECT -> Kind.Object
+ ClassKind.CLASS_OBJECT -> Kind.Object
+ ClassKind.TRAIT -> Kind.Interface
+ ClassKind.ENUM_CLASS -> Kind.Enum
+ ClassKind.ENUM_ENTRY -> Kind.EnumItem
+ else -> Kind.Class
+ }
+ val node = DocumentationNode(this, kind)
+ node.appendSupertypes(this)
+ if (getKind() != ClassKind.OBJECT) {
+ node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
+ node.appendChildren(getConstructors(), DocumentationReference.Kind.Member)
+ val classObjectDescriptor = getClassObjectDescriptor()
+ if (classObjectDescriptor != null)
+ node.appendChild(classObjectDescriptor, DocumentationReference.Kind.Member)
+ }
+ node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
+ register(this, node)
+ return node
+ }
+
+
+ fun ConstructorDescriptor.build(): DocumentationNode {
+ val node = DocumentationNode(this, Kind.Constructor)
+ node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
+ register(this, node)
+ return node
+ }
+
+ fun FunctionDescriptor.build(): DocumentationNode {
+ val node = DocumentationNode(this, Kind.Function)
+
+ node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
+ getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
+ node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
+ node.appendType(getReturnType())
+ register(this, node)
+ return node
+
+ }
+
+ fun PropertyDescriptor.build(): DocumentationNode {
+ val node = DocumentationNode(this, Kind.Property)
+ node.appendType(getReturnType())
+ node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
+ getGetter()?.let {
+ if (!it.isDefault())
+ node.appendChild(it, DocumentationReference.Kind.Member)
+ }
+ getSetter()?.let {
+ if (!it.isDefault())
+ node.appendChild(it, DocumentationReference.Kind.Member)
+ }
+
+ register(this, node)
+ return node
+ }
+
+ fun ValueParameterDescriptor.build(): DocumentationNode {
+ val node = DocumentationNode(this, Kind.Parameter)
+ node.appendType(getType())
+ return node
+ }
+
+ fun TypeParameterDescriptor.build(): DocumentationNode {
+ val doc = parseDocumentation(this)
+ val name = getName().asString()
+ val prefix = when (getVariance()) {
+ Variance.IN_VARIANCE -> "in "
+ Variance.OUT_VARIANCE -> "out "
+ else -> ""
+ }
+
+ val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
+
+ val builtIns = KotlinBuiltIns.getInstance()
+ for (constraint in getUpperBounds()) {
+ if (constraint == builtIns.getDefaultBound())
+ continue
+ val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
+ node.append(constraintNode, DocumentationReference.Kind.Detail)
+ }
+
+ for (constraint in getLowerBounds()) {
+ if (builtIns.isNothing(constraint))
+ continue
+ val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
+ node.append(constraintNode, DocumentationReference.Kind.Detail)
+ }
+ return node
+ }
+
+ fun ReceiverParameterDescriptor.build(): DocumentationNode {
+ val node = DocumentationNode(this, Kind.Receiver)
+ node.appendType(getType())
+ return node
+ }
+
+ /**
+ * Generates cross-references for documentation such as extensions for a type, inheritors, etc
+ *
+ * $receiver: [DocumentationContext] for node/descriptor resolutions
+ * $node: [DocumentationNode] to visit
+ */
+ public fun resolveReferences(node: DocumentationNode) {
+ node.details(DocumentationNode.Kind.Receiver).forEach { detail ->
+ val receiverType = detail.detail(DocumentationNode.Kind.Type)
+ val descriptor = links[receiverType]
+ if (descriptor != null) {
+ val typeNode = descriptorToNode[descriptor]
+ // if typeNode is null, extension is to external type like in a library
+ // should we create dummy node here?
+ typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
+ }
+ }
+ node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
+ val descriptor = links[detail]
+ if (descriptor != null) {
+ val typeNode = descriptorToNode[descriptor]
+ typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
+ }
+ }
+ node.details.forEach { detail ->
+ val descriptor = links[detail]
+ if (descriptor != null) {
+ val typeNode = descriptorToNode[descriptor]
+ if (typeNode != null) {
+ detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
+ }
+ }
+ }
+
+ resolveContentLinks(node, node.doc)
+
+ for (child in node.members) {
+ resolveReferences(child)
+ }
+ for (child in node.details) {
+ resolveReferences(child)
+ }
+ }
+
+ fun getResolutionScope(node: DocumentationNode): JetScope {
+ val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
+ return context.getResolutionScope(descriptor)
+ }
+
+ fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
+ val snapshot = content.children.toList()
+ for (child in snapshot) {
+ if (child is ContentExternalLink) {
+ val referenceText = child.href
+ if (Name.isValidIdentifier(referenceText)) {
+ val scope = getResolutionScope(node)
+ val symbolName = Name.guess(referenceText)
+ val symbol = scope.getLocalVariable(symbolName) ?:
+ scope.getProperties(symbolName).firstOrNull() ?:
+ scope.getFunctions(symbolName).firstOrNull() ?:
+ scope.getClassifier(symbolName)
+
+ if (symbol != null) {
+ val targetNode = descriptorToNode[symbol]
+ val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
+
+ val index = content.children.indexOf(child)
+ content.children.remove(index)
+ contentLink.children.addAll(child.children)
+ content.children.add(index, contentLink)
+ }
+ }
+ }
+ resolveContentLinks(node, child)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Kotlin/DocumentationContext.kt b/src/Kotlin/DocumentationContext.kt
index 95c3ded1..b13f08ea 100644
--- a/src/Kotlin/DocumentationContext.kt
+++ b/src/Kotlin/DocumentationContext.kt
@@ -47,16 +47,14 @@ fun BindingContext.createDocumentationModule(name: String,
packages: Set<FqName>,
options: DocumentationOptions = DocumentationOptions()): DocumentationModule {
val documentationModule = DocumentationModule(name)
- val context = DocumentationContext(this)
- val visitor = DocumentationNodeBuilder(context)
- for (packageName in packages) {
- val pkg = module.getPackage(packageName)
- pkg!!.accept(DocumentationBuildingVisitor(this, options, visitor), documentationModule)
+ val builder = DocumentationBuilder(this, options)
+ with(builder) {
+ for (packageName in packages) {
+ val pkg = module.getPackage(packageName)
+ if (pkg != null)
+ documentationModule.appendChild(pkg, DocumentationReference.Kind.Member)
+ }
}
-
- context.resolveReferences(documentationModule)
-
- // TODO: Uncomment for resolve verification
- // checkResolveChildren(documentationModule)
+ builder.resolveReferences(documentationModule)
return documentationModule
}
diff --git a/src/Kotlin/DocumentationNodeBuilder.kt b/src/Kotlin/DocumentationNodeBuilder.kt
index 8f77a3d6..a2686b00 100644
--- a/src/Kotlin/DocumentationNodeBuilder.kt
+++ b/src/Kotlin/DocumentationNodeBuilder.kt
@@ -18,6 +18,7 @@ import org.jetbrains.jet.lang.descriptors.ClassKind
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.types.TypeProjection
import org.jetbrains.jet.lang.types.Variance
+import org.jetbrains.dokka.DocumentationNode.Kind
class DocumentationNodeBuilder(val context: DocumentationContext) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DocumentationNode>() {
@@ -110,12 +111,12 @@ class DocumentationNodeBuilder(val context: DocumentationContext) : DeclarationD
descriptor!!
val doc = context.parseDocumentation(descriptor)
val node = DocumentationNode(descriptor.getName().asString(), doc, when (descriptor.getKind()) {
- ClassKind.OBJECT -> org.jetbrains.dokka.DocumentationNode.Kind.Object
- ClassKind.CLASS_OBJECT -> org.jetbrains.dokka.DocumentationNode.Kind.Object
- ClassKind.TRAIT -> org.jetbrains.dokka.DocumentationNode.Kind.Interface
- ClassKind.ENUM_CLASS -> org.jetbrains.dokka.DocumentationNode.Kind.Enum
- ClassKind.ENUM_ENTRY -> org.jetbrains.dokka.DocumentationNode.Kind.EnumItem
- else -> DocumentationNode.Kind.Class
+ ClassKind.OBJECT -> Kind.Object
+ ClassKind.CLASS_OBJECT -> Kind.Object
+ ClassKind.TRAIT -> Kind.Interface
+ ClassKind.ENUM_CLASS -> Kind.Enum
+ ClassKind.ENUM_ENTRY -> Kind.EnumItem
+ else -> Kind.Class
})
reference(data!!, node, DocumentationReference.Kind.Member)
addModality(descriptor, node)
@@ -182,7 +183,7 @@ class DocumentationNodeBuilder(val context: DocumentationContext) : DeclarationD
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor?, data: DocumentationNode?): DocumentationNode? {
descriptor!!
val doc = context.parseDocumentation(descriptor)
- val node = DocumentationNode("<constructor>", doc, DocumentationNode.Kind.Constructor)
+ val node = DocumentationNode("<init>", doc, DocumentationNode.Kind.Constructor)
reference(data!!, node, DocumentationReference.Kind.Member)
addVisibility(descriptor, node)