From ef51f7e2466e28db0943d528b6b489aefd098c0d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 30 Dec 2014 16:34:08 +0100 Subject: annotations work in progress --- test/src/model/ClassTest.kt | 14 ++++++++++++++ test/src/model/FunctionTest.kt | 30 +++++++++++++++++++++++++++++- test/src/model/PropertyTest.kt | 13 +++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) (limited to 'test/src/model') diff --git a/test/src/model/ClassTest.kt b/test/src/model/ClassTest.kt index b95a31dc..b9be30bf 100644 --- a/test/src/model/ClassTest.kt +++ b/test/src/model/ClassTest.kt @@ -167,3 +167,17 @@ public class ClassTest { } } } + + Test fun annotatedClass() { + verifyModel("test/data/classes/annotatedClass.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("data", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + } + } + } + } +} diff --git a/test/src/model/FunctionTest.kt b/test/src/model/FunctionTest.kt index 2a4ad0a5..d3d7843a 100644 --- a/test/src/model/FunctionTest.kt +++ b/test/src/model/FunctionTest.kt @@ -135,4 +135,32 @@ Documentation""", content.description.toTestString()) } } } -} \ No newline at end of file + + Test fun annotatedFunction() { + verifyModel("test/data/functions/annotatedFunction.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("inline", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + } + } + } + } + + Test fun functionWithAnnotatedParam() { + verifyModel("test/data/functions/functionWithAnnotatedParam.kt") { model -> + with(model.members.single().members.single()) { + with(details.elementAt(2)) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("noinline", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + } + } + } + } + } +} diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt index 103da170..0bf9714d 100644 --- a/test/src/model/PropertyTest.kt +++ b/test/src/model/PropertyTest.kt @@ -99,4 +99,17 @@ public class PropertyTest { } } } + + Test fun annotatedProperty() { + verifyModel("test/data/properties/annotatedProperty.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("inline", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + } + } + } + } } -- cgit From 0e70fa4ca021bff09e7d9ce64269a4e698512af5 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 30 Dec 2014 16:59:34 +0100 Subject: render annotations --- src/Kotlin/KotlinLanguageService.kt | 24 +++++++++++++++--------- test/data/format/annotations.kt | 4 +++- test/data/format/annotations.md | 30 ++++++++++++++++++++++++++++++ test/src/format/MarkdownFormatTest.kt | 2 +- test/src/model/ClassTest.kt | 1 - 5 files changed, 49 insertions(+), 12 deletions(-) (limited to 'test/src/model') diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt index a4016849..1e5edf4d 100644 --- a/src/Kotlin/KotlinLanguageService.kt +++ b/src/Kotlin/KotlinLanguageService.kt @@ -1,15 +1,6 @@ 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 @@ -135,6 +126,7 @@ class KotlinLanguageService : LanguageService { } private fun ContentNode.renderParameter(node: DocumentationNode) { + renderAnnotationsForNode(node) identifier(node.name) symbol(": ") val parameterType = node.detail(DocumentationNode.Kind.Type) @@ -171,8 +163,20 @@ class KotlinLanguageService : LanguageService { } } + private fun ContentNode.renderAnnotationsForNode(node: DocumentationNode) { + node.annotations.forEach { + renderAnnotation(it) + } + } + + private fun ContentNode.renderAnnotation(node: DocumentationNode) { + identifier(node.name) + text(" ") + } + private fun ContentNode.renderClass(node: DocumentationNode) { renderModifiersForNode(node) + renderAnnotationsForNode(node) when (node.kind) { DocumentationNode.Kind.Class -> keyword("class ") DocumentationNode.Kind.Interface -> keyword("trait ") @@ -189,6 +193,7 @@ class KotlinLanguageService : LanguageService { private fun ContentNode.renderFunction(node: DocumentationNode) { renderModifiersForNode(node) + renderAnnotationsForNode(node) when (node.kind) { DocumentationNode.Kind.Constructor -> identifier(node.owner!!.name) DocumentationNode.Kind.Function, @@ -218,6 +223,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 ") diff --git a/test/data/format/annotations.kt b/test/data/format/annotations.kt index 445ec969..9356d4ca 100644 --- a/test/data/format/annotations.kt +++ b/test/data/format/annotations.kt @@ -1,4 +1,6 @@ data class Foo { - inline fun bar() { + inline fun bar([noinline] notInlined: () -> Unit) { } + + inline val x: Int } diff --git a/test/data/format/annotations.md b/test/data/format/annotations.md index e69de29b..83f79397 100644 --- a/test/data/format/annotations.md +++ b/test/data/format/annotations.md @@ -0,0 +1,30 @@ +[test](out.md) / [](out.md) / [Foo](out.md) + + +# Foo + + +``` +data class Foo +``` + + + + +### Constructors + + +| [<init>](out.md) | `public Foo()` | + + +### Properties + + +| [x](out.md) | `inline val x: Int` | + + +### Functions + + +| [bar](out.md) | `inline fun bar(noinline notInlined: () -> Unit): Unit` | + diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt index e9852964..531980de 100644 --- a/test/src/format/MarkdownFormatTest.kt +++ b/test/src/format/MarkdownFormatTest.kt @@ -21,7 +21,7 @@ public class MarkdownFormatTest { } Test fun annotations() { - verifyOutput("test/data/format/annotations.kt") { model, output -> + verifyOutput("test/data/format/annotations.kt", ".md") { model, output -> markdownService.appendNodes(tempLocation, output, model.members.single().members) } } diff --git a/test/src/model/ClassTest.kt b/test/src/model/ClassTest.kt index b9be30bf..257d73eb 100644 --- a/test/src/model/ClassTest.kt +++ b/test/src/model/ClassTest.kt @@ -166,7 +166,6 @@ public class ClassTest { } } } -} Test fun annotatedClass() { verifyModel("test/data/classes/annotatedClass.kt") { model -> -- cgit From 69dd2988ec98a9fa027fcc805f28efbe8758d476 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 30 Dec 2014 18:47:03 +0100 Subject: support and render annotation parameters --- src/Kotlin/DocumentationBuilder.kt | 25 +++++++++++- src/Kotlin/KotlinLanguageService.kt | 8 ++++ src/Model/DocumentationNode.kt | 2 + .../annotatedClassWithAnnotationParameters.kt | 1 + test/data/classes/javaAnnotationClass.kt | 5 +++ test/data/format/annotationParams.kt | 1 + test/data/format/annotationParams.md | 12 ++++++ .../annotatedFunctionWithAnnotationParameters.kt | 1 + test/src/TestAPI.kt | 3 ++ test/src/format/MarkdownFormatTest.kt | 6 +++ test/src/model/ClassTest.kt | 44 ++++++++++++++++++++++ test/src/model/FunctionTest.kt | 23 +++++++++++ 12 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 test/data/classes/annotatedClassWithAnnotationParameters.kt create mode 100644 test/data/classes/javaAnnotationClass.kt create mode 100644 test/data/format/annotationParams.kt create mode 100644 test/data/format/annotationParams.md create mode 100644 test/data/functions/annotatedFunctionWithAnnotationParameters.kt (limited to 'test/src/model') diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index 57f8572f..c00580c5 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -9,6 +9,9 @@ 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) @@ -290,10 +293,30 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati fun AnnotationDescriptor.build(): DocumentationNode { val annotationClass = getType().getConstructor().getDeclarationDescriptor() val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation) - // TODO handle parameters + 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.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 0538ba74..92d5bf1d 100644 --- a/src/Kotlin/KotlinLanguageService.kt +++ b/src/Kotlin/KotlinLanguageService.kt @@ -172,6 +172,14 @@ class KotlinLanguageService : LanguageService { 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(" ") } diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt index 0698a5d0..caae77a8 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -90,6 +90,8 @@ public open class DocumentationNode(val name: String, Module Annotation + + Value } } diff --git a/test/data/classes/annotatedClassWithAnnotationParameters.kt b/test/data/classes/annotatedClassWithAnnotationParameters.kt new file mode 100644 index 00000000..1af97e75 --- /dev/null +++ b/test/data/classes/annotatedClassWithAnnotationParameters.kt @@ -0,0 +1 @@ +deprecated("should no longer be used") class Foo() {} diff --git a/test/data/classes/javaAnnotationClass.kt b/test/data/classes/javaAnnotationClass.kt new file mode 100644 index 00000000..c5f5cac4 --- /dev/null +++ b/test/data/classes/javaAnnotationClass.kt @@ -0,0 +1,5 @@ +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy + +Retention(RetentionPolicy.SOURCE) +public annotation class throws() diff --git a/test/data/format/annotationParams.kt b/test/data/format/annotationParams.kt new file mode 100644 index 00000000..ee5b524a --- /dev/null +++ b/test/data/format/annotationParams.kt @@ -0,0 +1 @@ +inlineOptions(InlineOption.LOCAL_CONTINUE_AND_BREAK) fun f() {} diff --git a/test/data/format/annotationParams.md b/test/data/format/annotationParams.md new file mode 100644 index 00000000..6bbdc0e5 --- /dev/null +++ b/test/data/format/annotationParams.md @@ -0,0 +1,12 @@ +[test](out.md) / [](out.md) / [f](out.md) + + +# f + + +``` +inlineOptions([InlineOption.LOCAL_CONTINUE_AND_BREAK]) fun f(): Unit +``` + + + diff --git a/test/data/functions/annotatedFunctionWithAnnotationParameters.kt b/test/data/functions/annotatedFunctionWithAnnotationParameters.kt new file mode 100644 index 00000000..ee5b524a --- /dev/null +++ b/test/data/functions/annotatedFunctionWithAnnotationParameters.kt @@ -0,0 +1 @@ +inlineOptions(InlineOption.LOCAL_CONTINUE_AND_BREAK) fun f() {} diff --git a/test/src/TestAPI.kt b/test/src/TestAPI.kt index cc09f001..af1b8e52 100644 --- a/test/src/TestAPI.kt +++ b/test/src/TestAPI.kt @@ -7,6 +7,7 @@ import org.jetbrains.dokka.* import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor import java.io.File import kotlin.test.assertEquals +import com.intellij.openapi.application.PathManager public fun verifyModel(vararg files: String, verifier: (DocumentationModule) -> Unit) { val messageCollector = object : MessageCollector { @@ -27,6 +28,8 @@ public fun verifyModel(vararg files: String, verifier: (DocumentationModule) -> } val environment = AnalysisEnvironment(messageCollector) { + val stringRoot = PathManager.getResourceRoot(javaClass(), "/java/lang/String.class") + addClasspath(File(stringRoot)) addSources(files.toList()) } diff --git a/test/src/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt index a1fc7ac1..08267d32 100644 --- a/test/src/format/MarkdownFormatTest.kt +++ b/test/src/format/MarkdownFormatTest.kt @@ -31,4 +31,10 @@ public class MarkdownFormatTest { markdownService.appendNodes(tempLocation, output, model.members.single().members) } } + + Test fun annotationParams() { + verifyOutput("test/data/format/annotationParams.kt", ".md") { model, output -> + markdownService.appendNodes(tempLocation, output, model.members.single().members) + } + } } diff --git a/test/src/model/ClassTest.kt b/test/src/model/ClassTest.kt index 257d73eb..ae82a4f1 100644 --- a/test/src/model/ClassTest.kt +++ b/test/src/model/ClassTest.kt @@ -179,4 +179,48 @@ public class ClassTest { } } } + + Test fun annotatedClassWithAnnotationParameters() { + verifyModel("test/data/classes/annotatedClassWithAnnotationParameters.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("deprecated", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(DocumentationNode.Kind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(DocumentationNode.Kind.Value, kind) + assertEquals("\"should no longer be used\"", name) + } + } + } + } + } + } + + Test fun javaAnnotationClass() { + verifyModel("test/data/classes/javaAnnotationClass.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("Retention", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(DocumentationNode.Kind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(DocumentationNode.Kind.Value, kind) + assertEquals("RetentionPolicy.SOURCE", name) + } + } + } + } + } + } } diff --git a/test/src/model/FunctionTest.kt b/test/src/model/FunctionTest.kt index d3d7843a..bf7471ea 100644 --- a/test/src/model/FunctionTest.kt +++ b/test/src/model/FunctionTest.kt @@ -163,4 +163,27 @@ Documentation""", content.description.toTestString()) } } } + + Test fun annotatedFunctionWithAnnotationParameters() { + verifyModel("test/data/functions/annotatedFunctionWithAnnotationParameters.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(1, annotations.count()) + with(annotations[0]) { + assertEquals("inlineOptions", name) + assertEquals(Content.Empty, content) + assertEquals(DocumentationNode.Kind.Annotation, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(DocumentationNode.Kind.Parameter, kind) + assertEquals(1, details.count()) + with(details[0]) { + assertEquals(DocumentationNode.Kind.Value, kind) + assertEquals("[InlineOption.LOCAL_CONTINUE_AND_BREAK]", name) + } + } + } + } + } + } } + -- cgit From 4b0dcee83efbdb77ae5e389ee04c309c52446153 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 9 Jan 2015 19:48:44 +0100 Subject: generate ExternalClass nodes to hold extension functions and properties for classes from other packages --- src/Formats/StructuredFormatService.kt | 11 ++++++-- src/Kotlin/DocumentationBuilder.kt | 37 ++++++++++++++++++++++++++- src/Model/DocumentationNode.kt | 1 + test/data/format/extensions.class.md | 16 ++++++++++++ test/data/format/extensions.kt | 19 ++++++++++++++ test/data/format/extensions.package.md | 18 +++++++++++++ test/data/functions/functionWithReceiver.kt | 8 +++++- test/data/properties/propertyWithReceiver.kt | 2 ++ test/src/format/MarkdownFormatTest.kt | 9 +++++++ test/src/model/FunctionTest.kt | 38 +++++++++++++++++----------- test/src/model/PropertyTest.kt | 13 ++++++++++ 11 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 test/data/format/extensions.class.md create mode 100644 test/data/format/extensions.kt create mode 100644 test/data/format/extensions.package.md create mode 100644 test/data/properties/propertyWithReceiver.kt (limited to 'test/src/model') 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()) + val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass()) + 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() @@ -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): 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) { val descriptors = hashMapOf>() 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() + 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("", 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("", 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) + } + } + } + } } -- cgit From e17eaa5fbc296bab0f32e8169d50fea06a6de581 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 9 Jan 2015 20:59:58 +0100 Subject: nice rendering for deprecated members --- src/Formats/HtmlFormatService.kt | 4 +++ src/Formats/MarkdownFormatService.kt | 4 +++ src/Formats/StructuredFormatService.kt | 14 ++++++++++ src/Kotlin/DocumentationBuilder.kt | 6 ++++- src/Kotlin/KotlinLanguageService.kt | 16 ++++++++--- src/Model/Content.kt | 1 + src/Model/DocumentationNode.kt | 2 ++ src/Model/DocumentationReference.kt | 1 + test/data/format/deprecated.class.html | 46 ++++++++++++++++++++++++++++++++ test/data/format/deprecated.kt | 5 ++++ test/data/format/deprecated.package.html | 44 ++++++++++++++++++++++++++++++ test/src/format/HtmlFormatTest.kt | 9 +++++++ test/src/model/ClassTest.kt | 3 +-- 13 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 test/data/format/deprecated.class.html create mode 100644 test/data/format/deprecated.kt create mode 100644 test/data/format/deprecated.package.html (limited to 'test/src/model') diff --git a/src/Formats/HtmlFormatService.kt b/src/Formats/HtmlFormatService.kt index b23e4a45..f76693dc 100644 --- a/src/Formats/HtmlFormatService.kt +++ b/src/Formats/HtmlFormatService.kt @@ -93,6 +93,10 @@ public open class HtmlFormatService(locationService: LocationService, return "${text}" } + override fun formatStrikethrough(text: String): String { + return "${text}" + } + override fun formatCode(code: String): String { return "${code}" } diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt index 38fadf7a..a2e3ce55 100644 --- a/src/Formats/MarkdownFormatService.kt +++ b/src/Formats/MarkdownFormatService.kt @@ -46,6 +46,10 @@ public open class MarkdownFormatService(locationService: LocationService, return "*$text*" } + override fun formatStrikethrough(text: String): String { + return "~~$text~~" + } + override public fun formatLink(text: String, location: Location): String { return "[$text](${location.path})" } diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index cb510f80..51222ffb 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka import java.util.LinkedHashMap +import com.intellij.openapi.util.text.StringUtil public data class FormatLink(val text: String, val location: Location) @@ -28,6 +29,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi public abstract fun formatLink(text: String, href: String): String public open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.location) public abstract fun formatStrong(text: String): String + public abstract fun formatStrikethrough(text: String): String public abstract fun formatEmphasis(text: String): String public abstract fun formatCode(code: String): String public abstract fun formatList(text: String): String @@ -46,6 +48,7 @@ public abstract class StructuredFormatService(val locationService: LocationServi is ContentKeyword -> append(formatKeyword(content.text)) is ContentIdentifier -> append(formatIdentifier(content.text)) is ContentStrong -> append(formatStrong(formatText(location, content.children))) + is ContentStrikethrough -> append(formatStrikethrough(formatText(location, content.children))) is ContentCode -> append(formatCode(formatText(location, content.children))) is ContentEmphasis -> append(formatEmphasis(formatText(location, content.children))) is ContentList -> append(formatList(formatText(location, content.children))) @@ -116,6 +119,17 @@ public abstract class StructuredFormatService(val locationService: LocationServi for ((summary, items) in breakdownBySummary) { items.forEach { appendBlockCode(to, formatText(location, languageService.render(it))) + val deprecation = it.deprecation + if (deprecation != null) { + val deprecationParameter = deprecation.details(DocumentationNode.Kind.Parameter).firstOrNull() + val deprecationValue = deprecationParameter?.details(DocumentationNode.Kind.Value)?.firstOrNull() + if (deprecationValue != null) { + to.append(formatStrong("Deprecated: ")) + appendLine(to, formatText(StringUtil.unquoteString(deprecationValue.name))) + } else { + appendLine(to, formatStrong("Deprecated")) + } + } } appendLine(to, summary) appendLine(to) diff --git a/src/Kotlin/DocumentationBuilder.kt b/src/Kotlin/DocumentationBuilder.kt index 844d8290..121c44bf 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -127,7 +127,11 @@ class DocumentationBuilder(val session: ResolveSession, val options: Documentati fun DocumentationNode.appendAnnotations(annotated: Annotated) { annotated.getAnnotations().forEach { - it.build()?.let { append(it, DocumentationReference.Kind.Annotation) } + val annotationNode = it.build() + if (annotationNode != null) { + append(annotationNode, + if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation) + } } } diff --git a/src/Kotlin/KotlinLanguageService.kt b/src/Kotlin/KotlinLanguageService.kt index 92d5bf1d..2ed308f8 100644 --- a/src/Kotlin/KotlinLanguageService.kt +++ b/src/Kotlin/KotlinLanguageService.kt @@ -196,7 +196,7 @@ class KotlinLanguageService : LanguageService { else -> throw IllegalArgumentException("Node $node is not a class-like object") } - identifier(node.name) + identifierOrDeprecated(node) renderTypeParametersForNode(node) renderSupertypesForNode(node) } @@ -218,7 +218,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)) { @@ -246,8 +246,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 diff --git a/src/Model/Content.kt b/src/Model/Content.kt index e1c1ef78..8491fd88 100644 --- a/src/Model/Content.kt +++ b/src/Model/Content.kt @@ -27,6 +27,7 @@ public class ContentSymbol(val text: String) : ContentNode() public class ContentParagraph() : ContentBlock() public class ContentEmphasis() : ContentBlock() public class ContentStrong() : ContentBlock() +public class ContentStrikethrough() : ContentBlock() public class ContentCode() : ContentBlock() public class ContentBlockCode() : ContentBlock() public class ContentNodeLink(val node : DocumentationNode) : ContentBlock() diff --git a/src/Model/DocumentationNode.kt b/src/Model/DocumentationNode.kt index 5f9aabab..635d1db9 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -32,6 +32,8 @@ public open class DocumentationNode(val name: String, get() = references(DocumentationReference.Kind.Link).map { it.to } public val annotations: List get() = references(DocumentationReference.Kind.Annotation).map { it.to } + public val deprecation: DocumentationNode? + get() = references(DocumentationReference.Kind.Deprecation).singleOrNull()?.to // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice public fun addReferenceTo(to: DocumentationNode, kind: DocumentationReference.Kind) { diff --git a/src/Model/DocumentationReference.kt b/src/Model/DocumentationReference.kt index 9fb366cb..bd40f0f5 100644 --- a/src/Model/DocumentationReference.kt +++ b/src/Model/DocumentationReference.kt @@ -10,6 +10,7 @@ public data class DocumentationReference(val from: DocumentationNode, val to: Do Inheritor Override Annotation + Deprecation } } diff --git a/test/data/format/deprecated.class.html b/test/data/format/deprecated.class.html new file mode 100644 index 00000000..87599082 --- /dev/null +++ b/test/data/format/deprecated.class.html @@ -0,0 +1,46 @@ + + + + +test /  / C
+
+

C

+
class C
Deprecated: This class sucks
+
+
+test /  / f
+
+

f

+
fun f(): Unit
Deprecated: This function sucks
+
+
+test /  / p
+
+

p

+
val p: Int
Deprecated: This property sucks
+
+
+

Constructors

+ + + + + + + +
+<init> +public C()
+

Accessors

+ + + + + + + +
+get +
+ + diff --git a/test/data/format/deprecated.kt b/test/data/format/deprecated.kt new file mode 100644 index 00000000..9ee2c1d6 --- /dev/null +++ b/test/data/format/deprecated.kt @@ -0,0 +1,5 @@ +deprecated("This class sucks") class C() { } + +deprecated("This function sucks") fun f() { } + +deprecated("This property sucks") val p: Int get() = 0 diff --git a/test/data/format/deprecated.package.html b/test/data/format/deprecated.package.html new file mode 100644 index 00000000..cf563a8b --- /dev/null +++ b/test/data/format/deprecated.package.html @@ -0,0 +1,44 @@ + + + + +test / 
+
+

+
package 

+
+

Types

+ + + + + + + +
+C +class C
+

Properties

+ + + + + + + +
+p +val p: Int
+

Functions

+ + + + + + + +
+f +fun f(): Unit
+ + diff --git a/test/src/format/HtmlFormatTest.kt b/test/src/format/HtmlFormatTest.kt index 881a7828..47fe9c4e 100644 --- a/test/src/format/HtmlFormatTest.kt +++ b/test/src/format/HtmlFormatTest.kt @@ -24,4 +24,13 @@ public class HtmlFormatTest { htmlService.appendNodes(tempLocation, output, model.members) } } + + Test fun deprecated() { + verifyOutput("test/data/format/deprecated.kt", ".package.html") { model, output -> + htmlService.appendNodes(tempLocation, output, model.members) + } + verifyOutput("test/data/format/deprecated.kt", ".class.html") { model, output -> + htmlService.appendNodes(tempLocation, output, model.members.single().members) + } + } } diff --git a/test/src/model/ClassTest.kt b/test/src/model/ClassTest.kt index ae82a4f1..f21c5c57 100644 --- a/test/src/model/ClassTest.kt +++ b/test/src/model/ClassTest.kt @@ -183,8 +183,7 @@ public class ClassTest { Test fun annotatedClassWithAnnotationParameters() { verifyModel("test/data/classes/annotatedClassWithAnnotationParameters.kt") { model -> with(model.members.single().members.single()) { - assertEquals(1, annotations.count()) - with(annotations[0]) { + with(deprecation!!) { assertEquals("deprecated", name) assertEquals(Content.Empty, content) assertEquals(DocumentationNode.Kind.Annotation, kind) -- cgit