aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Jemerov <yole@jetbrains.com>2015-01-09 19:48:44 +0100
committerDmitry Jemerov <yole@jetbrains.com>2015-01-09 19:48:44 +0100
commit4b0dcee83efbdb77ae5e389ee04c309c52446153 (patch)
treeed2251d21b2b79985ce958a30e4c238e90f606f9
parentc1a1cf14edfcf3b1b0cd166d60cee30e109ffe1a (diff)
downloaddokka-4b0dcee83efbdb77ae5e389ee04c309c52446153.tar.gz
dokka-4b0dcee83efbdb77ae5e389ee04c309c52446153.tar.bz2
dokka-4b0dcee83efbdb77ae5e389ee04c309c52446153.zip
generate ExternalClass nodes to hold extension functions and properties for classes from other packages
-rw-r--r--src/Formats/StructuredFormatService.kt11
-rw-r--r--src/Kotlin/DocumentationBuilder.kt37
-rw-r--r--src/Model/DocumentationNode.kt1
-rw-r--r--test/data/format/extensions.class.md16
-rw-r--r--test/data/format/extensions.kt19
-rw-r--r--test/data/format/extensions.package.md18
-rw-r--r--test/data/functions/functionWithReceiver.kt8
-rw-r--r--test/data/properties/propertyWithReceiver.kt2
-rw-r--r--test/src/format/MarkdownFormatTest.kt9
-rw-r--r--test/src/model/FunctionTest.kt38
-rw-r--r--test/src/model/PropertyTest.kt13
11 files changed, 153 insertions, 19 deletions
diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt
index b75f39d1..cb510f80 100644
--- a/src/Formats/StructuredFormatService.kt
+++ b/src/Formats/StructuredFormatService.kt
@@ -178,10 +178,15 @@ public abstract class StructuredFormatService(val locationService: LocationServi
for ((breadcrumbs, items) in breakdownByLocation) {
appendLine(to, breadcrumbs)
appendLine(to)
- appendLocation(location, to, items)
+ appendLocation(location, to, items.filter { it.kind != DocumentationNode.Kind.ExternalClass })
}
for (node in nodes) {
+ if (node.kind == DocumentationNode.Kind.ExternalClass) {
+ appendSection(location, "Extensions for ${node.name}", node.members, node, to)
+ continue
+ }
+
appendSection(location, "Packages", node.members(DocumentationNode.Kind.Package), node, to)
appendSection(location, "Types", node.members.filter {
it.kind in setOf(
@@ -191,6 +196,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi
DocumentationNode.Kind.Object,
DocumentationNode.Kind.AnnotationClass)
}, node, to)
+ appendSection(location, "Extensions for External Classes", node.members(DocumentationNode.Kind.ExternalClass), node, to)
appendSection(location, "Constructors", node.members(DocumentationNode.Kind.Constructor), node, to)
appendSection(location, "Properties", node.members(DocumentationNode.Kind.Property), node, to)
appendSection(location, "Functions", node.members(DocumentationNode.Kind.Function), node, to)
@@ -210,7 +216,8 @@ public abstract class StructuredFormatService(val locationService: LocationServi
DocumentationNode.Kind.Function,
DocumentationNode.Kind.PropertyAccessor,
DocumentationNode.Kind.ClassObjectProperty,
- DocumentationNode.Kind.ClassObjectFunction
+ DocumentationNode.Kind.ClassObjectFunction,
+ DocumentationNode.Kind.ExternalClass
)
}, node, to)
appendSection(location, "Extensions", node.extensions, node, to)
diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt
index c00580c5..99c81760 100644
--- a/src/Kotlin/DocumentationBuilder.kt
+++ b/src/Kotlin/DocumentationBuilder.kt
@@ -15,6 +15,12 @@ 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>()
@@ -141,6 +147,22 @@ 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 }) {
@@ -149,7 +171,11 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati
for ((packageName, declarations) in descriptors) {
println(" package $packageName: ${declarations.count()} nodes")
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)
}
}
@@ -205,6 +231,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)
diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt
index caae77a8..5f9aabab 100644
--- a/src/Model/DocumentationNode.kt
+++ b/src/Model/DocumentationNode.kt
@@ -89,6 +89,7 @@ public open class DocumentationNode(val name: String,
Module
+ ExternalClass
Annotation
Value
diff --git a/test/data/format/extensions.class.md b/test/data/format/extensions.class.md
new file mode 100644
index 00000000..a9747756
--- /dev/null
+++ b/test/data/format/extensions.class.md
@@ -0,0 +1,16 @@
+[test](out.md) / [foo](out.md) / [String](out.md)
+
+
+### Extensions for String
+
+
+| [fn](out.md) | `fun String.fn(): Unit`
+`fun String.fn(x: Int): Unit`
+Function with receiver
+
+ |
+| [foobar](out.md) | `val String.foobar: Int`
+Property with receiver.
+
+ |
+
diff --git a/test/data/format/extensions.kt b/test/data/format/extensions.kt
new file mode 100644
index 00000000..6f2eff9d
--- /dev/null
+++ b/test/data/format/extensions.kt
@@ -0,0 +1,19 @@
+package foo
+
+/**
+ * Function with receiver
+ */
+fun String.fn() {
+}
+
+/**
+ * Function with receiver
+ */
+fun String.fn(x: Int) {
+}
+
+/**
+ * Property with receiver.
+ */
+val String.foobar: Int
+ get() = size() * 2
diff --git a/test/data/format/extensions.package.md b/test/data/format/extensions.package.md
new file mode 100644
index 00000000..13f40457
--- /dev/null
+++ b/test/data/format/extensions.package.md
@@ -0,0 +1,18 @@
+[test](out.md) / [foo](out.md)
+
+
+# foo
+
+
+```
+package foo
+```
+
+
+
+
+### Extensions for External Classes
+
+
+| [String](out.md) | `` |
+
diff --git a/test/data/functions/functionWithReceiver.kt b/test/data/functions/functionWithReceiver.kt
index 663c3e56..c8473251 100644
--- a/test/data/functions/functionWithReceiver.kt
+++ b/test/data/functions/functionWithReceiver.kt
@@ -2,4 +2,10 @@
* Function with receiver
*/
fun String.fn() {
-} \ No newline at end of file
+}
+
+/**
+ * Function with receiver
+ */
+fun String.fn(x: Int) {
+}
diff --git a/test/data/properties/propertyWithReceiver.kt b/test/data/properties/propertyWithReceiver.kt
new file mode 100644
index 00000000..e282f6bd
--- /dev/null
+++ b/test/data/properties/propertyWithReceiver.kt
@@ -0,0 +1,2 @@
+val String.foobar: Int
+ get() = size() * 2
diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt
index 08267d32..3d32743f 100644
--- a/test/src/format/MarkdownFormatTest.kt
+++ b/test/src/format/MarkdownFormatTest.kt
@@ -37,4 +37,13 @@ public class MarkdownFormatTest {
markdownService.appendNodes(tempLocation, output, model.members.single().members)
}
}
+
+ Test fun extensions() {
+ verifyOutput("test/data/format/extensions.kt", ".package.md") { model, output ->
+ markdownService.appendNodes(tempLocation, output, model.members)
+ }
+ verifyOutput("test/data/format/extensions.kt", ".class.md") { model, output ->
+ markdownService.appendNodes(tempLocation, output, model.members.single().members)
+ }
+ }
}
diff --git a/test/src/model/FunctionTest.kt b/test/src/model/FunctionTest.kt
index bf7471ea..299f33a8 100644
--- a/test/src/model/FunctionTest.kt
+++ b/test/src/model/FunctionTest.kt
@@ -21,23 +21,32 @@ public class FunctionTest {
Test fun functionWithReceiver() {
verifyModel("test/data/functions/functionWithReceiver.kt") { model ->
with(model.members.single().members.single()) {
- assertEquals("fn", name)
- assertEquals(DocumentationNode.Kind.Function, kind)
- assertEquals("Function with receiver", content.summary.toTestString())
- assertEquals(4, details.count())
- assertEquals("internal", details.elementAt(0).name)
- assertEquals("final", details.elementAt(1).name)
- with(details.elementAt(2)) {
- assertEquals("<this>", name)
- assertEquals(DocumentationNode.Kind.Receiver, kind)
- assertEquals(Content.Empty, content)
- assertEquals("String", details.single().name)
+ assertEquals("String", name)
+ assertEquals(DocumentationNode.Kind.ExternalClass, kind)
+ assertEquals(2, members.count())
+ with(members[0]) {
+ assertEquals("fn", name)
+ assertEquals(DocumentationNode.Kind.Function, kind)
+ assertEquals("Function with receiver", content.summary.toTestString())
+ assertEquals(4, details.count())
+ assertEquals("internal", details.elementAt(0).name)
+ assertEquals("final", details.elementAt(1).name)
+ with(details.elementAt(2)) {
+ assertEquals("<this>", name)
+ assertEquals(DocumentationNode.Kind.Receiver, kind)
+ assertEquals(Content.Empty, content)
+ assertEquals("String", details.single().name)
+ assertTrue(members.none())
+ assertTrue(links.none())
+ }
+ assertEquals("Unit", details.elementAt(3).name)
assertTrue(members.none())
assertTrue(links.none())
}
- assertEquals("Unit", details.elementAt(3).name)
- assertTrue(members.none())
- assertTrue(links.none())
+ with(members[1]) {
+ assertEquals("fn", name)
+ assertEquals(DocumentationNode.Kind.Function, kind)
+ }
}
}
}
@@ -186,4 +195,3 @@ Documentation""", content.description.toTestString())
}
}
}
-
diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt
index 0bf9714d..14c43f78 100644
--- a/test/src/model/PropertyTest.kt
+++ b/test/src/model/PropertyTest.kt
@@ -112,4 +112,17 @@ public class PropertyTest {
}
}
}
+
+ Test fun propertyWithReceiver() {
+ verifyModel("test/data/properties/propertyWithReceiver.kt") { model ->
+ with(model.members.single().members.single()) {
+ assertEquals("String", name)
+ assertEquals(DocumentationNode.Kind.ExternalClass, kind)
+ with(members.single()) {
+ assertEquals("foobar", name)
+ assertEquals(DocumentationNode.Kind.Property, kind)
+ }
+ }
+ }
+ }
}