diff options
author | Simon Ogorodnik <Simon.Ogorodnik@jetbrains.com> | 2016-11-16 16:51:08 +0300 |
---|---|---|
committer | Simon Ogorodnik <Simon.Ogorodnik@jetbrains.com> | 2016-11-24 20:15:49 +0300 |
commit | 76c7e32f74b8982303a86f81b2ffed2b2a7e7c70 (patch) | |
tree | f9068cdf80d044fbd3d1c2f9be894de8884a1b07 | |
parent | 17a9a1774ec10690a031ae7afba5f5cfc59a9f24 (diff) | |
download | dokka-76c7e32f74b8982303a86f81b2ffed2b2a7e7c70.tar.gz dokka-76c7e32f74b8982303a86f81b2ffed2b2a7e7c70.tar.bz2 dokka-76c7e32f74b8982303a86f81b2ffed2b2a7e7c70.zip |
Type alias support
-rw-r--r-- | core/src/main/kotlin/Formats/StructuredFormatService.kt | 25 | ||||
-rw-r--r-- | core/src/main/kotlin/Kotlin/DocumentationBuilder.kt | 97 | ||||
-rw-r--r-- | core/src/main/kotlin/Kotlin/KotlinLanguageService.kt | 8 | ||||
-rw-r--r-- | core/src/main/kotlin/Model/DocumentationNode.kt | 5 | ||||
-rw-r--r-- | core/src/test/kotlin/format/MarkdownFormatTest.kt | 2 | ||||
-rw-r--r-- | core/src/test/kotlin/model/TypeAliasTest.kt | 105 | ||||
-rw-r--r-- | core/testdata/typealias/chain.kt | 8 | ||||
-rw-r--r-- | core/testdata/typealias/deprecated.kt | 7 | ||||
-rw-r--r-- | core/testdata/typealias/documented.kt | 9 | ||||
-rw-r--r-- | core/testdata/typealias/functional.kt | 10 | ||||
-rw-r--r-- | core/testdata/typealias/generic.kt | 7 | ||||
-rw-r--r-- | core/testdata/typealias/inheritanceFromTypeAlias.kt | 7 | ||||
-rw-r--r-- | core/testdata/typealias/simple.kt | 5 |
13 files changed, 239 insertions, 56 deletions
diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt index f24c9c2d..b7306486 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -23,11 +23,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, body() if (checkEndsWith && to.endsWith(suffix)) { to.setLength(to.length - suffix.length) - } - else if (to.length > startLength + prefix.length) { + } else if (to.length > startLength + prefix.length) { to.append(suffix) - } - else { + } else { to.setLength(startLength) } } @@ -124,8 +122,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val child = content.children.singleOrNull() if (child is ContentParagraph) { appendContent(child.children) - } - else { + } else { appendContent(content.children) } } @@ -361,7 +358,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } inner class SingleNodePageBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { + : PageBuilder(listOf(node)) { override fun build() { super.build() @@ -372,14 +369,15 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } appendSection("Packages", node.members(NodeKind.Package)) - appendSection("Types", node.members.filter { it.kind in NodeKind.classLike && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) + appendSection("Types", node.members.filter { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) appendSection("Annotations", node.members(NodeKind.AnnotationClass)) appendSection("Exceptions", node.members(NodeKind.Exception)) + appendSection("Type aliases", node.members(NodeKind.TypeAlias)) appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false) appendSection("Constructors", node.members(NodeKind.Constructor)) appendSection("Properties", node.members(NodeKind.Property)) - appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) + appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) appendSection("Functions", node.members(NodeKind.Function)) appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty)) @@ -394,6 +392,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, NodeKind.Object, NodeKind.AnnotationClass, NodeKind.Exception, + NodeKind.TypeAlias, NodeKind.Constructor, NodeKind.Property, NodeKind.Package, @@ -472,7 +471,7 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } inner class AllTypesNodeBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { + : PageBuilder(listOf(node)) { override fun build() { appendContent(node.owner!!.summary) @@ -508,12 +507,10 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, if (singleNode != null) { if (singleNode.kind == NodeKind.AllTypes) { AllTypesNodeBuilder(singleNode).build() - } - else { + } else { SingleNodePageBuilder(singleNode).build() } - } - else { + } else { PageBuilder(nodes).build() } } diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt index ecb6edf2..5f2d08f9 100644 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt @@ -25,10 +25,7 @@ import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.resolve.source.getPsi -import org.jetbrains.kotlin.types.ErrorUtils -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeProjection -import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.supertypes @@ -61,13 +58,12 @@ interface PackageDocumentationBuilder { } class DocumentationBuilder - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val descriptorDocumentationParser: DescriptorDocumentationParser, - val options: DocumentationOptions, - val refGraph: NodeReferenceGraph, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver) -{ +@Inject constructor(val resolutionFacade: DokkaResolutionFacade, + val descriptorDocumentationParser: DescriptorDocumentationParser, + val options: DocumentationOptions, + val refGraph: NodeReferenceGraph, + val logger: DokkaLogger, + val linkResolver: DeclarationLinkResolver) { val boringBuiltinClasses = setOf( "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean", "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any") @@ -97,7 +93,7 @@ class DocumentationBuilder return node } - private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor) : DocumentationNode{ + private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor): DocumentationNode { if (descriptor is MemberDescriptor) { appendVisibility(descriptor) if (descriptor !is ConstructorDescriptor) { @@ -124,15 +120,15 @@ class DocumentationBuilder appendTextNode(modifier, NodeKind.Modifier) } - fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) { - val superTypes = descriptor.typeConstructor.supertypes - for (superType in superTypes) { - if (!ignoreSupertype(superType)) { - appendType(superType, NodeKind.Supertype) - val superclass = superType?.constructor?.declarationDescriptor - link(superclass, descriptor, RefKind.Inheritor) - link(descriptor, superclass, RefKind.Superclass) - } + fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType) { + val unwrappedType = superType.unwrap() + if (unwrappedType is AbbreviatedType) { + appendSupertype(descriptor, unwrappedType.abbreviation) + } else if (!ignoreSupertype(unwrappedType)) { + appendType(unwrappedType, NodeKind.Supertype) + val superclass = unwrappedType.constructor.declarationDescriptor + link(superclass, descriptor, RefKind.Inheritor) + link(descriptor, superclass, RefKind.Superclass) } } @@ -148,8 +144,7 @@ class DocumentationBuilder fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) { if (projection.isStarProjection) { appendTextNode("*", NodeKind.Type) - } - else { + } else { appendType(projection.type, kind, projection.projectionKind.label) } } @@ -157,14 +152,18 @@ class DocumentationBuilder fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: NodeKind = NodeKind.Type, prefix: String = "") { if (kotlinType == null) return + if (kotlinType is WrappedType) + return appendType(kotlinType.unwrap()) + if (kotlinType is AbbreviatedType) + return appendType(kotlinType.abbreviation) + val classifierDescriptor = kotlinType.constructor.declarationDescriptor val name = when (classifierDescriptor) { is ClassDescriptor -> { if (classifierDescriptor.isCompanionObject) { classifierDescriptor.containingDeclaration.name.asString() + "." + classifierDescriptor.name.asString() - } - else { + } else { classifierDescriptor.name.asString() } } @@ -182,8 +181,7 @@ class DocumentationBuilder val jdkLink = linkResolver.buildJdkLink(classifierDescriptor) if (jdkLink != null) { node.append(DocumentationNode(jdkLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) - } - else { + } else { link(node, classifierDescriptor, if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link) } @@ -197,7 +195,7 @@ class DocumentationBuilder } fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean = - DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses + DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses fun DocumentationNode.appendAnnotations(annotated: Annotated) { annotated.annotations.filter { it.isDocumented() }.forEach { @@ -250,8 +248,7 @@ class DocumentationBuilder link(this, baseDescriptor, inheritedLinkKind) } null - } - else { + } else { val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original appendChild(descriptorToUse, RefKind.Member) } @@ -378,9 +375,25 @@ class DocumentationBuilder is TypeParameterDescriptor -> build() is ValueParameterDescriptor -> build() is ReceiverParameterDescriptor -> build() + is TypeAliasDescriptor -> build() else -> throw IllegalStateException("Descriptor $this is not known") } + fun TypeAliasDescriptor.build(): DocumentationNode { + val node = nodeForDescriptor(this, NodeKind.TypeAlias) + + node.appendAnnotations(this) + node.appendModifiers(this) + node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) + + node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) + + node.appendSourceLink(source) + + register(this, node) + return node + } + fun ClassDescriptor.build(): DocumentationNode { val kind = when { kind == ClassKind.OBJECT -> NodeKind.Object @@ -392,7 +405,9 @@ class DocumentationBuilder else -> NodeKind.Class } val node = nodeForDescriptor(this, kind) - node.appendSupertypes(this) + typeConstructor.supertypes.forEach { + node.appendSupertype(this, it) + } if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS) @@ -572,8 +587,7 @@ class DocumentationBuilder var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) { receiverClass = receiverClass.containingDeclaration!! - } - else if (receiverClass is TypeParameterDescriptor) { + } else if (receiverClass is TypeParameterDescriptor) { val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor if (upperBoundClass != null) { receiverClass = upperBoundClass @@ -648,11 +662,10 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { } class KotlinJavaDocumentationBuilder - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val documentationBuilder: DocumentationBuilder, - val options: DocumentationOptions, - val logger: DokkaLogger) : JavaDocumentationBuilder -{ +@Inject constructor(val resolutionFacade: DokkaResolutionFacade, + val documentationBuilder: DocumentationBuilder, + val options: DocumentationOptions, + val logger: DokkaLogger) : JavaDocumentationBuilder { override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { val classDescriptors = file.classes.map { val javaDescriptorResolver = resolutionFacade.getFrontendService(JavaDescriptorResolver::class.java) @@ -731,8 +744,12 @@ fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor return null } -fun DeclarationDescriptor.signature(): String = when(this) { - is ClassDescriptor, is PackageFragmentDescriptor, is PackageViewDescriptor -> DescriptorUtils.getFqName(this).asString() +fun DeclarationDescriptor.signature(): String = when (this) { + is ClassDescriptor, + is PackageFragmentDescriptor, + is PackageViewDescriptor, + is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString() + is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature() is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature() is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt index a0d8f95d..6fcf3772 100644 --- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt +++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt @@ -315,6 +315,7 @@ class KotlinLanguageService : LanguageService { NodeKind.Interface -> keyword("interface ") NodeKind.EnumItem -> keyword("enum val ") NodeKind.Object -> keyword("object ") + NodeKind.TypeAlias -> keyword("typealias ") else -> throw IllegalArgumentException("Node $node is not a class-like object") } @@ -322,6 +323,13 @@ class KotlinLanguageService : LanguageService { renderTypeParametersForNode(node, renderMode) renderSupertypesForNode(node, renderMode) renderExtraTypeParameterConstraints(node, renderMode) + + if (node.kind == NodeKind.TypeAlias) { + nbsp() + symbol("=") + nbsp() + renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode) + } } private fun ContentBlock.renderFunction(node: DocumentationNode, diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index 9aacbca0..def0f626 100644 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -13,6 +13,7 @@ enum class NodeKind { Exception, EnumItem, Object, + TypeAlias, Constructor, Function, @@ -30,6 +31,8 @@ enum class NodeKind { UpperBound, LowerBound, + TypeAliasUnderlyingType, + Modifier, NullabilityModifier, @@ -55,7 +58,7 @@ enum class NodeKind { OverloadGroupNote; companion object { - val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object) + val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias) } } diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt index 7f0922ba..280e8e22 100644 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt @@ -28,7 +28,7 @@ class MarkdownFormatTest { } //TODO: Enable after typealias support - @Ignore("Disabled until we will correctly support typealias") + // @Ignore("Disabled until we will correctly support typealias") @Test fun exceptionClass() { verifyMarkdownNode("exceptionClass", withKotlinRuntime = true) verifyMarkdownPackage("exceptionClass", withKotlinRuntime = true) diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt new file mode 100644 index 00000000..e67572f1 --- /dev/null +++ b/core/src/test/kotlin/model/TypeAliasTest.kt @@ -0,0 +1,105 @@ +package org.jetbrains.dokka.tests + +import junit.framework.TestCase.assertEquals +import org.jetbrains.dokka.Content +import org.jetbrains.dokka.NodeKind +import org.junit.Test + +class TypeAliasTest { + @Test + fun testSimple() { + verifyModel("testdata/typealias/simple.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals(Content.Empty, content) + assertEquals("B", name) + assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) + } + } + } + + @Test + fun testInheritanceFromTypeAlias() { + verifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals(Content.Empty, content) + assertEquals("Same", name) + assertEquals("Some", detail(NodeKind.TypeAliasUnderlyingType).name) + assertEquals("My", inheritors.single().name) + } + with(pkg.members(NodeKind.Class).find { it.name == "My" }!!) { + assertEquals("Same", detail(NodeKind.Supertype).name) + } + } + } + + @Test + fun testChain() { + verifyModel("testdata/typealias/chain.kt") { + val pkg = it.members.single() + with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { + assertEquals(Content.Empty, content) + assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) + } + with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { + assertEquals(Content.Empty, content) + assertEquals("B", detail(NodeKind.TypeAliasUnderlyingType).name) + } + } + } + + @Test + fun testDocumented() { + verifyModel("testdata/typealias/documented.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals("Just typealias", content.summary.toTestString()) + } + } + } + + @Test + fun testDeprecated() { + verifyModel("testdata/typealias/deprecated.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals(Content.Empty, content) + assertEquals("Deprecated", deprecation!!.name) + assertEquals("\"Not mainstream now\"", deprecation!!.detail(NodeKind.Parameter).detail(NodeKind.Value).name) + } + } + } + + @Test + fun testGeneric() { + verifyModel("testdata/typealias/generic.kt") { + val pkg = it.members.single() + with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { + assertEquals("Any", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) + } + + with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { + assertEquals("T", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) + assertEquals("T", detail(NodeKind.TypeParameter).name) + } + } + } + + @Test + fun testFunctional() { + verifyModel("testdata/typealias/functional.kt") { + val pkg = it.members.single() + with(pkg.member(NodeKind.TypeAlias)) { + assertEquals("Function1", detail(NodeKind.TypeAliasUnderlyingType).name) + val typeParams = detail(NodeKind.TypeAliasUnderlyingType).details(NodeKind.Type) + assertEquals("A", typeParams.first().name) + assertEquals("B", typeParams.last().name) + } + + with(pkg.member(NodeKind.Function)) { + assertEquals("Spell", detail(NodeKind.Parameter).detail(NodeKind.Type).name) + } + } + } +}
\ No newline at end of file diff --git a/core/testdata/typealias/chain.kt b/core/testdata/typealias/chain.kt new file mode 100644 index 00000000..520be553 --- /dev/null +++ b/core/testdata/typealias/chain.kt @@ -0,0 +1,8 @@ +package _typealias.chain + +class A + +typealias B = A + +typealias C = B + diff --git a/core/testdata/typealias/deprecated.kt b/core/testdata/typealias/deprecated.kt new file mode 100644 index 00000000..b53d3a20 --- /dev/null +++ b/core/testdata/typealias/deprecated.kt @@ -0,0 +1,7 @@ +package _typealias.deprecated + +class Lol + +@Deprecated("Not mainstream now") +typealias Kek = Lol + diff --git a/core/testdata/typealias/documented.kt b/core/testdata/typealias/documented.kt new file mode 100644 index 00000000..3ca110e5 --- /dev/null +++ b/core/testdata/typealias/documented.kt @@ -0,0 +1,9 @@ +package _typealias.documented + +class A + +/** + * Just typealias + */ +typealias B = A + diff --git a/core/testdata/typealias/functional.kt b/core/testdata/typealias/functional.kt new file mode 100644 index 00000000..dadafa5e --- /dev/null +++ b/core/testdata/typealias/functional.kt @@ -0,0 +1,10 @@ +package _typealias.functional + +class A +class B + +typealias Spell = (A) -> B + +fun magic(spell: Spell) { + +}
\ No newline at end of file diff --git a/core/testdata/typealias/generic.kt b/core/testdata/typealias/generic.kt new file mode 100644 index 00000000..43bc0e23 --- /dev/null +++ b/core/testdata/typealias/generic.kt @@ -0,0 +1,7 @@ +package _typealias.generic + +interface A<T> + +typealias B = A<Any> + +typealias C<T> = A<T>
\ No newline at end of file diff --git a/core/testdata/typealias/inheritanceFromTypeAlias.kt b/core/testdata/typealias/inheritanceFromTypeAlias.kt new file mode 100644 index 00000000..f929ecd0 --- /dev/null +++ b/core/testdata/typealias/inheritanceFromTypeAlias.kt @@ -0,0 +1,7 @@ +package _typealias.inheritance + +open class Some + +typealias Same = Some + +class My : Same
\ No newline at end of file diff --git a/core/testdata/typealias/simple.kt b/core/testdata/typealias/simple.kt new file mode 100644 index 00000000..d688a84d --- /dev/null +++ b/core/testdata/typealias/simple.kt @@ -0,0 +1,5 @@ +package _typealias.simple + +class A + +typealias B = A
\ No newline at end of file |