diff options
37 files changed, 626 insertions, 59 deletions
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 "<emph>${text}</emph>" } + override fun formatStrikethrough(text: String): String { + return "<s>${text}</s>" + } + override fun formatCode(code: String): String { return "<code>${code}</code>" } diff --git a/src/Formats/MarkdownFormatService.kt b/src/Formats/MarkdownFormatService.kt index 96f64eec..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})" } @@ -106,14 +110,15 @@ public open class MarkdownFormatService(locationService: LocationService, } override fun appendTableRow(to: StringBuilder, body: () -> Unit) { - to.append("| ") + to.append("|") body() to.appendln() } override fun appendTableCell(to: StringBuilder, body: () -> Unit) { + to.append(" ") body() - to.append(" | ") + to.append(" |") } var outlineLevel = 0 diff --git a/src/Formats/StructuredFormatService.kt b/src/Formats/StructuredFormatService.kt index 2d326854..977d81d0 100644 --- a/src/Formats/StructuredFormatService.kt +++ b/src/Formats/StructuredFormatService.kt @@ -28,6 +28,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 +47,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 +118,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(deprecationValue.name.trim("\""))) + } else { + appendLine(to, formatStrong("Deprecated")) + } + } } appendLine(to, summary) appendLine(to) @@ -178,18 +191,25 @@ 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( DocumentationNode.Kind.Class, DocumentationNode.Kind.Interface, DocumentationNode.Kind.Enum, - DocumentationNode.Kind.Object) + 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) @@ -200,14 +220,17 @@ public abstract class StructuredFormatService(val locationService: LocationServi it.kind !in setOf( DocumentationNode.Kind.Class, DocumentationNode.Kind.Interface, + DocumentationNode.Kind.Enum, DocumentationNode.Kind.Object, + DocumentationNode.Kind.AnnotationClass, DocumentationNode.Kind.Constructor, DocumentationNode.Kind.Property, DocumentationNode.Kind.Package, 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 af8d0ad6..8b6b19d3 100644 --- a/src/Kotlin/DocumentationBuilder.kt +++ b/src/Kotlin/DocumentationBuilder.kt @@ -6,9 +6,21 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.builtins.* import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.resolve.lazy.* +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.kotlin.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 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 86d2ee04..635d1db9 100644 --- a/src/Model/DocumentationNode.kt +++ b/src/Model/DocumentationNode.kt @@ -30,6 +30,10 @@ public open class DocumentationNode(val name: String, get() = references(DocumentationReference.Kind.Inheritor).map { it.to } public val links: List<DocumentationNode> get() = references(DocumentationReference.Kind.Link).map { it.to } + public val annotations: List<DocumentationNode> + 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) { @@ -62,6 +66,7 @@ public open class DocumentationNode(val name: String, Class Interface Enum + AnnotationClass EnumItem Object @@ -85,6 +90,11 @@ public open class DocumentationNode(val name: String, Modifier Module + + ExternalClass + Annotation + + Value } } diff --git a/src/Model/DocumentationReference.kt b/src/Model/DocumentationReference.kt index 1849fe08..bd40f0f5 100644 --- a/src/Model/DocumentationReference.kt +++ b/src/Model/DocumentationReference.kt @@ -9,6 +9,8 @@ public data class DocumentationReference(val from: DocumentationNode, val to: Do Extension Inheritor Override + Annotation + Deprecation } } diff --git a/src/main.kt b/src/main.kt index f540e3dc..24853be5 100644 --- a/src/main.kt +++ b/src/main.kt @@ -98,17 +98,7 @@ public fun main(args: Array<String>) { } val documentationModule = DocumentationModule(arguments.moduleName, moduleContent) - - 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") - val packageNode = DocumentationNode(packageName, Content.Empty, DocumentationNode.Kind.Package) - packageNode.appendChildren(declarations, DocumentationReference.Kind.Member) - documentationModule.append(packageNode, DocumentationReference.Kind.Member) - } + documentationModule.appendFragments(fragments) documentationBuilder.resolveReferences(documentationModule) documentationModule } diff --git a/test/data/classes/annotatedClass.kt b/test/data/classes/annotatedClass.kt new file mode 100644 index 00000000..62c6f0ec --- /dev/null +++ b/test/data/classes/annotatedClass.kt @@ -0,0 +1 @@ +data class Foo() {} 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/annotationClass.kt b/test/data/format/annotationClass.kt new file mode 100644 index 00000000..89d494fb --- /dev/null +++ b/test/data/format/annotationClass.kt @@ -0,0 +1 @@ +annotation class fancy diff --git a/test/data/format/annotationClass.md b/test/data/format/annotationClass.md new file mode 100644 index 00000000..301eff08 --- /dev/null +++ b/test/data/format/annotationClass.md @@ -0,0 +1,18 @@ +[test](out.md) / [](out.md) / [fancy](out.md) + + +# fancy + + +``` +annotation class fancy +``` + + + + +### Constructors + + +| [<init>](out.md) | `public fancy()` | + 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/format/annotations.kt b/test/data/format/annotations.kt new file mode 100644 index 00000000..9356d4ca --- /dev/null +++ b/test/data/format/annotations.kt @@ -0,0 +1,6 @@ +data class Foo { + inline fun bar([noinline] notInlined: () -> Unit) { + } + + inline val x: Int +} diff --git a/test/data/format/annotations.md b/test/data/format/annotations.md new file mode 100644 index 00000000..e745213e --- /dev/null +++ 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/data/format/classWithClassObject.md b/test/data/format/classWithClassObject.md index f694a76f..796ecce2 100644 --- a/test/data/format/classWithClassObject.md +++ b/test/data/format/classWithClassObject.md @@ -14,17 +14,17 @@ class Klass ### Constructors -| [<init>](out.md) | `public Klass()` | +| [<init>](out.md) | `public Klass()` | ### Class Object Properties -| [x](out.md) | `val x: Int` | +| [x](out.md) | `val x: Int` | ### Class Object Functions -| [foo](out.md) | `fun foo(): Unit` | +| [foo](out.md) | `fun foo(): Unit` | 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 @@ +<HTML> +<HEAD> +</HEAD> +<BODY> +<a href="out.html">test</a> / <a href="out.html"></a> / <a href="out.html">C</a><br/> +<br/> +<h1>C</h1> +<pre><code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></pre><strong>Deprecated: </strong>This class sucks<br/> +<br/> +<br/> +<a href="out.html">test</a> / <a href="out.html"></a> / <a href="out.html">f</a><br/> +<br/> +<h1>f</h1> +<pre><code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></pre><strong>Deprecated: </strong>This function sucks<br/> +<br/> +<br/> +<a href="out.html">test</a> / <a href="out.html"></a> / <a href="out.html">p</a><br/> +<br/> +<h1>p</h1> +<pre><code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code></pre><strong>Deprecated: </strong>This property sucks<br/> +<br/> +<br/> +<h3>Constructors</h3> +<table> +<tbody> +<tr> +<td> +<a href="out.html"><init></a></td> +<td> +<code><span class="keyword">public</span> <span class="identifier">C</span><span class="symbol">(</span><span class="symbol">)</span></code></td> +</tr> +</tbody> +</table> +<h3>Accessors</h3> +<table> +<tbody> +<tr> +<td> +<a href="out.html">get</a></td> +<td> +<code></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> 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 @@ +<HTML> +<HEAD> +</HEAD> +<BODY> +<a href="out.html">test</a> / <a href="out.html"></a><br/> +<br/> +<h1></h1> +<pre><code><span class="keyword">package</span> <span class="identifier"></span></code></pre><br/> +<br/> +<h3>Types</h3> +<table> +<tbody> +<tr> +<td> +<a href="out.html">C</a></td> +<td> +<code><span class="keyword">class </span><s><span class="identifier">C</span></s></code></td> +</tr> +</tbody> +</table> +<h3>Properties</h3> +<table> +<tbody> +<tr> +<td> +<a href="out.html">p</a></td> +<td> +<code><span class="keyword">val </span><s><span class="identifier">p</span></s><span class="symbol">: </span><span class="identifier">Int</span></code></td> +</tr> +</tbody> +</table> +<h3>Functions</h3> +<table> +<tbody> +<tr> +<td> +<a href="out.html">f</a></td> +<td> +<code><span class="keyword">fun </span><s><span class="identifier">f</span></s><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></td> +</tr> +</tbody> +</table> +</BODY> +</HTML> 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/annotatedFunction.kt b/test/data/functions/annotatedFunction.kt new file mode 100644 index 00000000..11c19672 --- /dev/null +++ b/test/data/functions/annotatedFunction.kt @@ -0,0 +1,2 @@ +inline fun f() { +} 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/data/functions/functionWithAnnotatedParam.kt b/test/data/functions/functionWithAnnotatedParam.kt new file mode 100644 index 00000000..8922f765 --- /dev/null +++ b/test/data/functions/functionWithAnnotatedParam.kt @@ -0,0 +1,2 @@ +fun function([noinline] notInlined: () -> Unit) { +} 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/annotatedProperty.kt b/test/data/properties/annotatedProperty.kt new file mode 100644 index 00000000..f70c28b4 --- /dev/null +++ b/test/data/properties/annotatedProperty.kt @@ -0,0 +1 @@ +inline val property = "test"
\ No newline at end of file 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/TestAPI.kt b/test/src/TestAPI.kt index d67254a2..901e78fc 100644 --- a/test/src/TestAPI.kt +++ b/test/src/TestAPI.kt @@ -6,7 +6,8 @@ import kotlin.test.fail import org.jetbrains.dokka.* import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import java.io.File -import kotlin.test.assertEquals +import com.intellij.openapi.application.PathManager +import org.junit.Assert 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<String>(), "/java/lang/String.class") + addClasspath(File(stringRoot)) addSources(files.toList()) } @@ -34,10 +37,6 @@ public fun verifyModel(vararg files: String, verifier: (DocumentationModule) -> val documentation = environment.withContext { environment, session -> val fragments = environment.getSourceFiles().map { session.getPackageFragment(it.getPackageFqName()) }.filterNotNull().distinct() - val descriptors = hashMapOf<String, List<DeclarationDescriptor>>() - for ((name, parts) in fragments.groupBy { it.fqName }) { - descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() }) - } val documentationModule = DocumentationModule("test") val documentationBuilder = DocumentationBuilder(session, options) @@ -56,7 +55,7 @@ public fun verifyOutput(path: String, outputExtension: String, outputGenerator: val output = StringBuilder() outputGenerator(it, output) val expectedOutput = File(path.replace(".kt", outputExtension)).readText() - assertEquals(expectedOutput, output.toString()) + Assert.assertEquals(expectedOutput, output.toString()) } } 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/format/MarkdownFormatTest.kt b/test/src/format/MarkdownFormatTest.kt index 5cdfb8b5..3d32743f 100644 --- a/test/src/format/MarkdownFormatTest.kt +++ b/test/src/format/MarkdownFormatTest.kt @@ -19,4 +19,31 @@ public class MarkdownFormatTest { markdownService.appendNodes(tempLocation, output, model.members.single().members) } } + + Test fun annotations() { + verifyOutput("test/data/format/annotations.kt", ".md") { model, output -> + markdownService.appendNodes(tempLocation, output, model.members.single().members) + } + } + + Test fun annotationClass() { + verifyOutput("test/data/format/annotationClass.kt", ".md") { model, output -> + 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) + } + } + + 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/ClassTest.kt b/test/src/model/ClassTest.kt index b95a31dc..f21c5c57 100644 --- a/test/src/model/ClassTest.kt +++ b/test/src/model/ClassTest.kt @@ -166,4 +166,60 @@ 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) + } + } + } + } + + Test fun annotatedClassWithAnnotationParameters() { + verifyModel("test/data/classes/annotatedClassWithAnnotationParameters.kt") { model -> + with(model.members.single().members.single()) { + with(deprecation!!) { + 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 2a4ad0a5..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) + } } } } @@ -135,4 +144,54 @@ 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) + } + } + } + } + } + + 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) + } + } + } + } + } + } +} diff --git a/test/src/model/PropertyTest.kt b/test/src/model/PropertyTest.kt index 103da170..14c43f78 100644 --- a/test/src/model/PropertyTest.kt +++ b/test/src/model/PropertyTest.kt @@ -99,4 +99,30 @@ 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) + } + } + } + } + + 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) + } + } + } + } } |