diff options
17 files changed, 751 insertions, 99 deletions
diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 797b1d01..e9bdb9c1 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -38,6 +38,8 @@ <module name="dokka.integration.test" target="1.8" /> <module name="dokka.plugins.javadoc8.main" target="1.8" /> <module name="dokka.plugins.javadoc8.test" target="1.8" /> + <module name="dokka.plugins.kotlin-as-java.main" target="1.8" /> + <module name="dokka.plugins.kotlin-as-java.test" target="1.8" /> <module name="dokka.plugins.ma.main" target="1.8" /> <module name="dokka.plugins.ma.test" target="1.8" /> <module name="dokka.plugins.main" target="1.8" /> diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index 86ba24b7..1744e27f 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -4,7 +4,9 @@ import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.dokka.transformers.descriptors.KotlinClassKindTypes -import org.jetbrains.dokka.transformers.descriptors.KotlinTypeWrapper +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility class Module(override val name: String, val packages: List<Package>) : Documentable() { override val dri: DRI = DRI.topLevel @@ -32,7 +34,8 @@ class Class( override val classlikes: List<Classlike>, override val expected: ClassPlatformInfo?, override val actual: List<ClassPlatformInfo>, - override val extra: MutableSet<Extra> = mutableSetOf() + override val extra: MutableSet<Extra> = mutableSetOf(), + override val visibility: Map<PlatformData, Visibility> ) : Classlike( name = name, dri = dri, @@ -43,7 +46,7 @@ class Class( expected = expected, actual = actual, extra = extra -) +), WithVisibility class Enum( override val dri: DRI, @@ -55,8 +58,9 @@ class Enum( override val classlikes: List<Classlike> = emptyList(), override val expected: ClassPlatformInfo? = null, override val actual: List<ClassPlatformInfo>, - override val extra: MutableSet<Extra> = mutableSetOf() -) : Classlike(dri = dri, name = name, kind = KotlinClassKindTypes.ENUM_CLASS, actual = actual) { + override val extra: MutableSet<Extra> = mutableSetOf(), + override val visibility: Map<PlatformData, Visibility> +) : Classlike(dri = dri, name = name, kind = KotlinClassKindTypes.ENUM_CLASS, actual = actual), WithVisibility { constructor(c: Classlike, entries: List<EnumEntry>, ctors: List<Function>) : this( dri = c.dri, name = c.name, @@ -67,7 +71,8 @@ class Enum( classlikes = c.classlikes, expected = c.expected, actual = c.actual, - extra = c.extra + extra = c.extra, + visibility = c.visibility ) override val children: List<Documentable> @@ -79,7 +84,8 @@ class EnumEntry( override val name: String, override val expected: ClassPlatformInfo? = null, override val actual: List<ClassPlatformInfo>, - override val extra: MutableSet<Extra> = mutableSetOf() + override val extra: MutableSet<Extra> = mutableSetOf(), + override val visibility: Map<PlatformData, Visibility> ) : Classlike( dri = dri, name = name, @@ -93,7 +99,8 @@ class EnumEntry( name = c.name, actual = c.actual, expected = c.expected, - extra = c.extra + extra = c.extra, + visibility = c.visibility ) override val children: List<Parameter> @@ -109,8 +116,9 @@ class Function( val parameters: List<Parameter>, override val expected: PlatformInfo?, override val actual: List<PlatformInfo>, - override val extra: MutableSet<Extra> = mutableSetOf() -) : CallableNode() { + override val extra: MutableSet<Extra> = mutableSetOf(), + override val visibility: Map<PlatformData, Visibility> +) : CallableNode(), WithVisibility { override val children: List<Parameter> get() = listOfNotNull(receiver) + parameters } @@ -121,8 +129,10 @@ class Property( override val receiver: Parameter?, override val expected: PlatformInfo?, override val actual: List<PlatformInfo>, - override val extra: MutableSet<Extra> = mutableSetOf() -) : CallableNode() { + override val extra: MutableSet<Extra> = mutableSetOf(), + val accessors: List<Function>, + override val visibility: Map<PlatformData, Visibility> +) : CallableNode(), WithVisibility { override val children: List<Parameter> get() = listOfNotNull(receiver) } @@ -208,7 +218,7 @@ abstract class Classlike( override val expected: ClassPlatformInfo? = null, override val actual: List<ClassPlatformInfo>, override val extra: MutableSet<Extra> = mutableSetOf() -) : ScopeNode() { +) : ScopeNode(), WithVisibility { val inherited by lazy { platformInfo.mapNotNull { (it as? ClassPlatformInfo)?.inherited }.flatten() } } @@ -217,8 +227,12 @@ abstract class ScopeNode : Documentable() { abstract val properties: List<Property> abstract val classlikes: List<Classlike> - override val children: List<Documentable> - get() = functions + properties + classlikes + override val children: List<Documentable> // It is written so awkwardly because of type inference being lost here + get() = mutableListOf<Documentable>().apply { + addAll(functions) + addAll(properties) + addAll(classlikes) + } } abstract class CallableNode : Documentable() { @@ -235,6 +249,7 @@ interface TypeWrapper { val arguments: List<TypeWrapper> val dri: DRI? } + interface ClassKind fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = @@ -244,4 +259,9 @@ fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } -interface Extra
\ No newline at end of file +interface Extra +object STATIC : Extra + +interface WithVisibility { + val visibility: Map<PlatformData, Visibility> +}
\ No newline at end of file diff --git a/core/src/main/kotlin/pages/PageBuilder.kt b/core/src/main/kotlin/pages/PageBuilder.kt index 7fe07ae4..df0c12c2 100644 --- a/core/src/main/kotlin/pages/PageBuilder.kt +++ b/core/src/main/kotlin/pages/PageBuilder.kt @@ -5,7 +5,7 @@ import org.jetbrains.dokka.model.Enum import org.jetbrains.dokka.model.Function import org.jetbrains.dokka.model.doc.TagWrapper -class DefaultPageBuilder( +open class DefaultPageBuilder( override val rootContentGroup: RootContentBuilder ) : PageBuilder { @@ -37,10 +37,10 @@ class DefaultPageBuilder( else -> throw IllegalStateException("$m should not be present here") } - private fun group(node: Documentable, content: PageContentBuilderFunction) = + protected open fun group(node: Documentable, content: PageContentBuilderFunction) = rootContentGroup(node, ContentKind.Main, content) - private fun contentForModule(m: Module) = group(m) { + protected open fun contentForModule(m: Module) = group(m) { header(1) { text("root") } block("Packages", 2, ContentKind.Packages, m.packages, m.platformData) { link(it.name, it.dri) @@ -49,7 +49,7 @@ class DefaultPageBuilder( text("Link to allpage here") } - private fun contentForPackage(p: Package) = group(p) { + protected open fun contentForPackage(p: Package) = group(p) { header(1) { text("Package ${p.name}") } block("Types", 2, ContentKind.Properties, p.classlikes, p.platformData) { link(it.name, it.dri) @@ -62,13 +62,13 @@ class DefaultPageBuilder( } } - private fun contentForClasslike(c: Classlike): ContentGroup = when (c) { + fun contentForClasslike(c: Classlike): ContentGroup = when (c) { is Class -> contentForClass(c) is Enum -> contentForEnum(c) else -> throw IllegalStateException("$c should not be present here") } - private fun contentForClass(c: Class) = group(c) { + protected fun contentForClass(c: Class) = group(c) { header(1) { text(c.name) } c.inherited.takeIf { it.isNotEmpty() }?.let { diff --git a/core/src/main/kotlin/pages/PageContentBuilder.kt b/core/src/main/kotlin/pages/PageContentBuilder.kt index d5ffe27a..bc01b03e 100644 --- a/core/src/main/kotlin/pages/PageContentBuilder.kt +++ b/core/src/main/kotlin/pages/PageContentBuilder.kt @@ -8,21 +8,21 @@ import org.jetbrains.dokka.model.TypeWrapper import org.jetbrains.dokka.model.doc.DocTag import org.jetbrains.dokka.utilities.DokkaLogger -class DefaultPageContentBuilder( +open class DefaultPageContentBuilder( private val dri: Set<DRI>, private val platformData: Set<PlatformData>, private val kind: Kind, private val commentsConverter: CommentsToContentConverter, - val logger: DokkaLogger, + open val logger: DokkaLogger, private val styles: Set<Style> = emptySet(), private val extras: Set<Extra> = emptySet() ) : PageContentBuilder { private val contents = mutableListOf<ContentNode>() - private fun createText(text: String, kind: Kind = ContentKind.Symbol) = + protected fun createText(text: String, kind: Kind = ContentKind.Symbol) = ContentText(text, DCI(dri, kind), platformData, styles, extras) - private fun build() = ContentGroup( + protected fun build() = ContentGroup( contents.toList(), DCI(dri, kind), platformData, @@ -38,7 +38,7 @@ class DefaultPageContentBuilder( contents += createText(text, kind) } - private fun signature(f: Function, block: PageContentBuilderFunction) { + protected fun signature(f: Function, block: PageContentBuilderFunction) { contents += group(setOf(f.dri), f.platformData, ContentKind.Symbol, block) } @@ -163,7 +163,7 @@ class DefaultPageContentBuilder( } -private fun PageContentBuilder.type(t: TypeWrapper) { +fun PageContentBuilder.type(t: TypeWrapper) { if (t.constructorNamePathSegments.isNotEmpty() && t.dri != null) link(t.constructorNamePathSegments.last(), t.dri!!) else if (t.constructorNamePathSegments.isNotEmpty() && t.dri == null) diff --git a/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt index 651bd223..dd2d1681 100644 --- a/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt +++ b/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt @@ -5,10 +5,10 @@ import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.ClassKind import org.jetbrains.dokka.model.Enum import org.jetbrains.dokka.model.Function import org.jetbrains.dokka.model.Property -import org.jetbrains.dokka.model.ClassKind import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.dokka.parsers.MarkdownParser @@ -31,21 +31,26 @@ object DefaultDescriptorToDocumentationTranslator : DescriptorToDocumentationTra platformData: PlatformData, context: DokkaContext ) = DokkaDescriptorVisitor(platformData, context.platforms[platformData]?.facade!!).run { - packageFragments.map { visitPackageFragmentDescriptor(it, DRIWithPlatformInfo(DRI.topLevel, null, emptyList())) } + packageFragments.map { + visitPackageFragmentDescriptor( + it, + DRIWithPlatformInfo(DRI.topLevel, null, emptyList()) + ) + } }.let { Module(moduleName, it) } } -internal data class DRIWithPlatformInfo( +data class DRIWithPlatformInfo( val dri: DRI, val expected: PlatformInfo?, val actual: List<PlatformInfo> ) -private fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, null, emptyList()) +fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, null, emptyList()) -internal class DokkaDescriptorVisitor( +open class DokkaDescriptorVisitor( private val platformData: PlatformData, private val resolutionFacade: DokkaResolutionFacade ) : DeclarationDescriptorVisitorEmptyBodies<Documentable, DRIWithPlatformInfo>() { @@ -59,6 +64,7 @@ internal class DokkaDescriptorVisitor( ): Package { val driWithPlatform = DRI(packageName = descriptor.fqName.asString()).withEmptyInfo() val scope = descriptor.getMemberScope() + return Package( dri = driWithPlatform.dri, functions = scope.functions(driWithPlatform), @@ -67,10 +73,11 @@ internal class DokkaDescriptorVisitor( ) } - override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Classlike = when (descriptor.kind) { - org.jetbrains.kotlin.descriptors.ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent) - else -> classDescriptor(descriptor, parent) - } + override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Classlike = + when (descriptor.kind) { + org.jetbrains.kotlin.descriptors.ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent) + else -> classDescriptor(descriptor, parent) + } fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Enum { val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() @@ -80,14 +87,19 @@ internal class DokkaDescriptorVisitor( return Enum( dri = driWithPlatform.dri, name = descriptor.name.asString(), - entries = scope.classlikes(driWithPlatform).filter { it.kind == KotlinClassKindTypes.ENUM_ENTRY }.map { EnumEntry(it) }, + entries = scope.classlikes(driWithPlatform).filter { it.kind == KotlinClassKindTypes.ENUM_ENTRY }.map { + EnumEntry( + it + ) + }, constructors = descriptor.constructors.map { visitConstructorDescriptor(it, driWithPlatform) }, functions = scope.functions(driWithPlatform), properties = scope.properties(driWithPlatform), classlikes = scope.classlikes(driWithPlatform), expected = descriptor.takeIf { it.isExpect }?.resolveClassDescriptionData(), actual = listOfNotNull(descriptorData), - extra = mutableSetOf() // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet() + extra = mutableSetOf(), // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet() + visibility = mapOf(platformData to descriptor.visibility) ) } @@ -101,23 +113,26 @@ internal class DokkaDescriptorVisitor( dri = driWithPlatform.dri, name = descriptor.name.asString(), kind = KotlinClassKindTypes.valueOf(descriptor.kind.toString()), - constructors = descriptor.constructors.map { visitConstructorDescriptor( - it, - if(it.isPrimary) - DRIWithPlatformInfo( - driWithPlatform.dri, - expected?.info.filterTagWrappers(listOf(Constructor::class)), - actual.filterTagWrappers(listOf(Constructor::class)) - ) - else - DRIWithPlatformInfo(driWithPlatform.dri, null, emptyList()) - ) }, + constructors = descriptor.constructors.map { + visitConstructorDescriptor( + it, + if (it.isPrimary) + DRIWithPlatformInfo( + driWithPlatform.dri, + expected?.info.filterTagWrappers(listOf(Constructor::class)), + actual.filterTagWrappers(listOf(Constructor::class)) + ) + else + DRIWithPlatformInfo(driWithPlatform.dri, null, emptyList()) + ) + }, functions = scope.functions(driWithPlatform), properties = scope.properties(driWithPlatform), classlikes = scope.classlikes(driWithPlatform), expected = expected, actual = actual, - extra = mutableSetOf() // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet() + extra = mutableSetOf(), // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet() + visibility = mapOf(platformData to descriptor.visibility) ) } @@ -125,19 +140,24 @@ internal class DokkaDescriptorVisitor( val expected = descriptor.takeIf { it.isExpect }?.resolveDescriptorData() val actual = listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()) val dri = parent.dri.copy(callable = Callable.from(descriptor)) + return Property( dri = dri, name = descriptor.name.asString(), - receiver = descriptor.extensionReceiverParameter?.let { visitReceiverParameterDescriptor( - it, - DRIWithPlatformInfo( - dri, - expected?.filterTagWrappers(listOf(Receiver::class)), - actual.filterTagWrappers(listOf(Receiver::class)) + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor( + it, + DRIWithPlatformInfo( + dri, + expected?.filterTagWrappers(listOf(Receiver::class)), + actual.filterTagWrappers(listOf(Receiver::class)) + ) ) - ) }, + }, expected = expected, - actual = actual + actual = actual, + accessors = descriptor.accessors.map { visitPropertyAccessorDescriptor(it, descriptor, dri) }, + visibility = mapOf(platformData to descriptor.visibility) ) } @@ -145,28 +165,35 @@ internal class DokkaDescriptorVisitor( val expected = descriptor.takeIf { it.isExpect }?.resolveDescriptorData() val actual = listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()) val dri = parent.dri.copy(callable = Callable.from(descriptor)) + return Function( dri = dri, name = descriptor.name.asString(), returnType = descriptor.returnType?.let { KotlinTypeWrapper(it) }, isConstructor = false, - receiver = descriptor.extensionReceiverParameter?.let { visitReceiverParameterDescriptor( - it, - DRIWithPlatformInfo( - dri, - expected?.filterTagWrappers(listOf(Receiver::class)), - actual.filterTagWrappers(listOf(Receiver::class)) + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor( + it, + DRIWithPlatformInfo( + dri, + expected?.filterTagWrappers(listOf(Receiver::class)), + actual.filterTagWrappers(listOf(Receiver::class)) + ) ) - ) }, - parameters = descriptor.valueParameters.mapIndexed { index, desc -> parameter(index, desc, - DRIWithPlatformInfo( - dri, - expected.filterTagWrappers(listOf(Param::class), desc.name.asString()), - actual.filterTagWrappers(listOf(Param::class), desc.name.asString()) + }, + parameters = descriptor.valueParameters.mapIndexed { index, desc -> + parameter( + index, desc, + DRIWithPlatformInfo( + dri, + expected.filterTagWrappers(listOf(Param::class), desc.name.asString()), + actual.filterTagWrappers(listOf(Param::class), desc.name.asString()) + ) ) - ) }, + }, expected = expected, - actual = actual + actual = actual, + visibility = mapOf(platformData to descriptor.visibility) ) } @@ -178,15 +205,19 @@ internal class DokkaDescriptorVisitor( returnType = KotlinTypeWrapper(descriptor.returnType), isConstructor = true, receiver = null, - parameters = descriptor.valueParameters.mapIndexed { index, desc -> parameter(index, desc, - DRIWithPlatformInfo( - dri, - parent.expected.filterTagWrappers(listOf(Param::class)), - parent.actual.filterTagWrappers(listOf(Param::class)) + parameters = descriptor.valueParameters.mapIndexed { index, desc -> + parameter( + index, desc, + DRIWithPlatformInfo( + dri, + parent.expected.filterTagWrappers(listOf(Param::class)), + parent.actual.filterTagWrappers(listOf(Param::class)) + ) ) - ) }, + }, expected = parent.expected ?: descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), - actual = parent.actual + actual = parent.actual, + visibility = mapOf(platformData to descriptor.visibility) ) } @@ -201,6 +232,50 @@ internal class DokkaDescriptorVisitor( actual = parent.actual ) + open fun visitPropertyAccessorDescriptor( + descriptor: PropertyAccessorDescriptor, + propertyDescriptor: PropertyDescriptor, + parent: DRI + ): Function { + val dri = parent.copy(callable = Callable.from(descriptor)) + val isGetter = descriptor is PropertyGetterDescriptor + + fun PropertyDescriptor.asParameter(parent: DRI) = + Parameter( + parent.copy(target = 1), + this.name.asString(), + KotlinTypeWrapper(this.type), + descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), + listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()) + ) + + val name = run { + val modifier = if (isGetter) "get" else "set" + val rawName = propertyDescriptor.name.asString() + "$modifier${rawName[0].toUpperCase()}${rawName.drop(1)}" + } + + descriptor.visibility + val parameters = + if (isGetter) { + emptyList() + } else { + listOf(propertyDescriptor.asParameter(dri)) + } + + return Function( + dri, + name, + descriptor.returnType?.let { KotlinTypeWrapper(it) }, + false, + null, + parameters, + descriptor.takeIf { it.isExpect }?.resolveDescriptorData(), + listOfNotNull(descriptor.takeUnless { it.isExpect }?.resolveDescriptorData()), + visibility = mapOf(platformData to descriptor.visibility) + ) + } + private fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) = Parameter( dri = parent.dri.copy(target = index + 1), @@ -229,6 +304,7 @@ internal class DokkaDescriptorVisitor( val doc = findKDoc() val parser: MarkdownParser = MarkdownParser(resolutionFacade, this) val docHeader = parser.parseFromKDocTag(doc) + return BasePlatformInfo(docHeader, listOf(platformData)) } @@ -237,8 +313,11 @@ internal class DokkaDescriptorVisitor( (getSuperInterfaces() + getAllSuperclassesWithoutAny()).map { DRI.from(it) }) } - private fun PlatformInfo?.filterTagWrappers(types: List<KClass<out TagWrapper>>, name: String? = null): PlatformInfo? { - if(this == null) + private fun PlatformInfo?.filterTagWrappers( + types: List<KClass<out TagWrapper>>, + name: String? = null + ): PlatformInfo? { + if (this == null) return null return BasePlatformInfo( DocumentationNode( @@ -248,7 +327,10 @@ internal class DokkaDescriptorVisitor( ) } - private fun List<PlatformInfo>.filterTagWrappers(types: List<KClass<out TagWrapper>>, name: String? = null): List<PlatformInfo> = + private fun List<PlatformInfo>.filterTagWrappers( + types: List<KClass<out TagWrapper>>, + name: String? = null + ): List<PlatformInfo> = this.map { it.filterTagWrappers(types, name)!! } } diff --git a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt index ec67ea88..0d7fa249 100644 --- a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt +++ b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt @@ -52,7 +52,8 @@ fun Function.mergeWith(other: Function): Function = Function( if (receiver != null && other.receiver != null) receiver.mergeWith(other.receiver) else null, merge(parameters + other.parameters, Parameter::mergeWith), expected?.mergeWith(other.expected), - (actual + other.actual).merge() + (actual + other.actual).merge(), + visibility = (visibility + other.visibility) ) fun Property.mergeWith(other: Property) = Property( @@ -60,7 +61,9 @@ fun Property.mergeWith(other: Property) = Property( name, if (receiver != null && other.receiver != null) receiver.mergeWith(other.receiver) else null, expected?.mergeWith(other.expected), - (actual + other.actual).merge() + (actual + other.actual).merge(), + accessors = (this.accessors + other.accessors).distinct(), + visibility = (visibility + other.visibility) ) fun Classlike.mergeWith(other: Classlike): Classlike = when { @@ -78,7 +81,8 @@ fun Class.mergeWith(other: Class) = Class( properties = merge(properties + other.properties, Property::mergeWith), classlikes = merge(classlikes + other.classlikes, Classlike::mergeWith), expected = expected?.mergeWith(other.expected), - actual = (actual + other.actual).mergeClassPlatformInfo() + actual = (actual + other.actual).mergeClassPlatformInfo(), + visibility = (visibility + other.visibility) ) fun Enum.mergeWith(other: Enum) = Enum( @@ -90,7 +94,8 @@ fun Enum.mergeWith(other: Enum) = Enum( expected = expected?.mergeWith(other.expected), actual = (actual + other.actual).mergeClassPlatformInfo(), entries = (this.entries + other.entries.distinctBy { it.dri }.toList()), - constructors = merge(constructors + other.constructors, Function::mergeWith) + constructors = merge(constructors + other.constructors, Function::mergeWith), + visibility = visibility ) fun Parameter.mergeWith(other: Parameter) = Parameter( diff --git a/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt index 05c07070..2078faa7 100644 --- a/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt +++ b/core/src/main/kotlin/transformers/psi/DefaultPsiToDocumentationTranslator.kt @@ -12,6 +12,7 @@ import org.jetbrains.dokka.model.Function import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.descriptors.Visibilities object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator { @@ -47,6 +48,14 @@ object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator { return listOf(BasePlatformInfo(comment, listOf(platformData))) } + private fun PsiModifierListOwner.getVisibility() = modifierList?.children?.toList()?.let { ml -> + when { + ml.any { it.text == PsiKeyword.PUBLIC } -> Visibilities.PUBLIC + ml.any { it.text == PsiKeyword.PROTECTED } -> Visibilities.PROTECTED + else -> Visibilities.PRIVATE + } + } ?: Visibilities.PRIVATE + fun parseClass(psi: PsiClass, parent: DRI): Class = with(psi) { val kind = when { isAnnotationType -> JavaClassKindTypes.ANNOTATION_CLASS @@ -62,6 +71,7 @@ object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator { link(superClass, node, RefKind.Inheritor) } }*/ + return Class( dri, name.orEmpty(), @@ -72,7 +82,8 @@ object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator { innerClasses.map { parseClass(it, dri) }, null, emptyList(), - mutableSetOf() + mutableSetOf(), + visibility = mapOf(platformData to psi.getVisibility()) ) } @@ -101,7 +112,8 @@ object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator { ) }, null, - getComment(psi) + getComment(psi), + visibility = mapOf(platformData to psi.getVisibility()) ) } @@ -118,7 +130,9 @@ object DefaultPsiToDocumentationTranslator : PsiToDocumentationTranslator { psi.name, null, null, - getComment(psi) + getComment(psi), + accessors = emptyList(), + visibility = mapOf(platformData to psi.getVisibility()) ) } } @@ -132,16 +146,28 @@ enum class JavaClassKindTypes : ClassKind { ANNOTATION_CLASS; } -class JavaTypeWrapper( - type: PsiType -) : TypeWrapper { +class JavaTypeWrapper : TypeWrapper { override val constructorFqName: String? override val constructorNamePathSegments: List<String> - override val arguments: List<JavaTypeWrapper> + override val arguments: List<TypeWrapper> override val dri: DRI? + val isPrimitive: Boolean + + constructor( + constructorNamePathSegments: List<String>, + arguments: List<TypeWrapper>, + dri: DRI?, + isPrimitiveType: Boolean + ) { + this.constructorFqName = constructorNamePathSegments.joinToString(".") + this.constructorNamePathSegments = constructorNamePathSegments + this.arguments = arguments + this.dri = dri + this.isPrimitive = isPrimitiveType + } - init { + constructor(type: PsiType) { if (type is PsiClassReferenceType) { val resolved = type.resolve() constructorFqName = resolved?.qualifiedName @@ -150,22 +176,26 @@ class JavaTypeWrapper( if (it is PsiClassReferenceType) JavaTypeWrapper(it) else null } dri = fromPsi(type) + this.isPrimitive = false } else if (type is PsiEllipsisType) { constructorFqName = type.canonicalText constructorNamePathSegments = listOf(type.canonicalText) // TODO arguments = emptyList() dri = DRI("java.lang", "Object") // TODO + this.isPrimitive = false } else if (type is PsiArrayType) { constructorFqName = type.canonicalText constructorNamePathSegments = listOf(type.canonicalText) arguments = emptyList() dri = (type as? PsiClassReferenceType)?.let { fromPsi(it) } // TODO + this.isPrimitive = false } else { type as PsiPrimitiveType constructorFqName = type.name constructorNamePathSegments = type.name.split('.') arguments = emptyList() dri = null + this.isPrimitive = true } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a869af2d..1b16c34a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed Jan 29 14:26:26 CET 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/plugins/kotlin-as-java/build.gradle.kts b/plugins/kotlin-as-java/build.gradle.kts new file mode 100644 index 00000000..5d04060f --- /dev/null +++ b/plugins/kotlin-as-java/build.gradle.kts @@ -0,0 +1,17 @@ +publishing { + publications { + register<MavenPublication>("kotlin-as-java-plugin") { + artifactId = "kotlin-as-java-plugin" + from(components["java"]) + } + } +} + +dependencies { + implementation(kotlin("stdlib-jdk8")) + compileOnly(project(":coreDependencies", configuration = "shadow")) + testImplementation(project(":core")) + testImplementation(project(":coreDependencies", configuration = "shadow")) + testImplementation(project(":testApi")) + testImplementation("junit:junit:4.13") +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaDescriptorToDocumentationTranslator.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaDescriptorToDocumentationTranslator.kt new file mode 100644 index 00000000..9c4ee9aa --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaDescriptorToDocumentationTranslator.kt @@ -0,0 +1,75 @@ +package org.jetbrains.dokka.kotlinAsJava + +import org.jetbrains.dokka.analysis.DokkaResolutionFacade +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.withClass +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.descriptors.DRIWithPlatformInfo +import org.jetbrains.dokka.transformers.descriptors.DescriptorToDocumentationTranslator +import org.jetbrains.dokka.transformers.descriptors.DokkaDescriptorVisitor +import org.jetbrains.dokka.transformers.descriptors.withEmptyInfo +import org.jetbrains.kotlin.descriptors.* + +object KotlinAsJavaDescriptorToDocumentationTranslator : DescriptorToDocumentationTranslator { + override fun invoke( + moduleName: String, + packageFragments: Iterable<PackageFragmentDescriptor>, + platformData: PlatformData, + context: DokkaContext + ): Module = + KotlinAsJavaDokkaDescriptorVisitor(platformData, context.platforms[platformData]?.facade!!).run { + packageFragments.map { visitPackageFragmentDescriptor(it, DRI.topLevel.withEmptyInfo()) } + }.let { Module(moduleName, it) } +} + +class KotlinAsJavaDokkaDescriptorVisitor( + platformData: PlatformData, + resolutionFacade: DokkaResolutionFacade +) : DokkaDescriptorVisitor(platformData, resolutionFacade) { + override fun visitPackageFragmentDescriptor( + descriptor: PackageFragmentDescriptor, + parent: DRIWithPlatformInfo + ): Package { + val dri = DRI(packageName = descriptor.fqName.asString()) + DescriptorCache.add(dri, descriptor) + return super.visitPackageFragmentDescriptor(descriptor, parent) + } + + override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Classlike { + val dri = parent.dri.withClass(descriptor.name.asString()) + DescriptorCache.add(dri, descriptor) + return super.visitClassDescriptor(descriptor, parent) + } + + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): Property { + val dri = parent.dri.copy(callable = Callable.from(descriptor)) + DescriptorCache.add(dri, descriptor) + return super.visitPropertyDescriptor(descriptor, parent) + } + + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): Function { + val dri = parent.dri.copy(callable = Callable.from(descriptor)) + DescriptorCache.add(dri, descriptor) + return super.visitFunctionDescriptor(descriptor, parent) + } + + override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): Function { + val dri = parent.dri.copy(callable = Callable.from(descriptor)) + DescriptorCache.add(dri, descriptor) + return super.visitConstructorDescriptor(descriptor, parent) + } + + override fun visitPropertyAccessorDescriptor( + descriptor: PropertyAccessorDescriptor, + propertyDescriptor: PropertyDescriptor, + parent: DRI + ): Function { + val dri = parent.copy(callable = Callable.from(descriptor)) + DescriptorCache.add(dri, descriptor) + return super.visitPropertyAccessorDescriptor(descriptor, propertyDescriptor, parent) + } +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageBuilder.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageBuilder.kt new file mode 100644 index 00000000..67a3ee86 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageBuilder.kt @@ -0,0 +1,68 @@ +package org.jetbrains.dokka.kotlinAsJava + +import org.jetbrains.dokka.kotlinAsJava.conversions.asJava +import org.jetbrains.dokka.kotlinAsJava.conversions.asStatic +import org.jetbrains.dokka.kotlinAsJava.conversions.withClass +import org.jetbrains.dokka.links.withClass +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.Enum +import org.jetbrains.dokka.model.doc.TagWrapper +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.transformers.descriptors.KotlinClassKindTypes +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi + +fun DeclarationDescriptor.sourceLocation(): String? = this.findPsi()?.containingFile?.virtualFile?.path +fun <T : Documentable> List<T>.groupedByLocation(): Map<String, List<T>> = + this.map { DescriptorCache[it.dri]?.sourceLocation() to it } + .filter { it.first != null }.groupBy({ (location, _) -> + location!!.let { it.split("/").last().split(".").first() + "Kt" } + }) { it.second } + +class KotlinAsJavaPageBuilder(rootContentGroup: RootContentBuilder) : DefaultPageBuilder(rootContentGroup) { + + data class FunsAndProps(val key: String, val funs: List<Function>, val props: List<Property>) + + override fun pageForPackage(p: Package): PackagePageNode { + + val funs = p.functions.groupedByLocation() + + val props = p.properties.groupedByLocation() + + val zipped = (funs.keys + props.keys) + .map { k -> FunsAndProps(k, funs[k].orEmpty(), props[k].orEmpty()) } + + val classes = (p.classlikes + zipped.map { (key, funs, props) -> + val dri = p.dri.withClass(key) + Class( + dri = dri, + name = key, + kind = KotlinClassKindTypes.CLASS, + constructors = emptyList(), + functions = funs.map { it.withClass(key, dri).asStatic() }, + properties = props.map { it.withClass(key, dri) }, + classlikes = emptyList(), + actual = emptyList(), + expected = null, + visibility = p.platformData.map { it to Visibilities.PUBLIC }.toMap() + ) + }).map { it.asJava() } + + return PackagePageNode( + p.name, contentForPackage(p, classes), setOf(p.dri), p, + classes.map(::pageForClasslike) + ) + } + + private fun contentForPackage(p: Package, nClasses: List<Classlike>) = group(p) { + header(1) { text("Package ${p.name}") } + block("Types", 2, ContentKind.Properties, nClasses, p.platformData) { + link(it.name, it.dri) + text(it.briefDocTagString) + } + } + + private fun TagWrapper.toHeaderString() = this.javaClass.toGenericString().split('.').last() +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageContentBuilder.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageContentBuilder.kt new file mode 100644 index 00000000..65925fb2 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageContentBuilder.kt @@ -0,0 +1,65 @@ +package org.jetbrains.dokka.kotlinAsJava + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.TypeWrapper +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.transformers.psi.JavaTypeWrapper +import org.jetbrains.dokka.utilities.DokkaLogger + +class KotlinAsJavaPageContentBuilder( + private val dri: Set<DRI>, + private val platformData: Set<PlatformData>, + private val kind: Kind, + private val commentsConverter: CommentsToContentConverter, + override val logger: DokkaLogger, + private val styles: Set<Style> = emptySet(), + private val extras: Set<Extra> = emptySet() +) : DefaultPageContentBuilder(dri, platformData, kind, commentsConverter, logger, styles, extras) { + private val contents = mutableListOf<ContentNode>() + + override fun signature(f: Function) = signature(f) { + + val returnType = f.returnType + if (!f.isConstructor) { + if (returnType != null && + returnType.constructorFqName != Unit::class.qualifiedName + ) { + if ((returnType as? JavaTypeWrapper)?.isPrimitive == true) + text(returnType.constructorFqName ?: "") + else + type(returnType) + text(" ") + } else text("void ") + + } + + link(f.name, f.dri) + text("(") + val params = listOfNotNull(f.receiver) + f.parameters + list(params) { + if ((it.type as? JavaTypeWrapper)?.isPrimitive == true) + text(it.type.constructorFqName ?: "") + else + type(it.type) + + text(" ") + link(it.name ?: "receiver", it.dri) + } + text(")") + } + + companion object { + fun group( + dri: Set<DRI>, + platformData: Set<PlatformData>, + kind: Kind, + commentsConverter: CommentsToContentConverter, + logger: DokkaLogger, + block: PageContentBuilderFunction + ): ContentGroup = + KotlinAsJavaPageContentBuilder(dri, platformData, kind, commentsConverter, logger).apply(block).build() + } +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt new file mode 100644 index 00000000..345dc9be --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.kotlinAsJava + + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Module +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.single +import org.jetbrains.dokka.transformers.documentation.DocumentationToPageTranslator +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor + +class KotlinAsJavaPlugin : DokkaPlugin() { + val kotlinAsJavaDescriptorToDocumentableTranslator by extending { CoreExtensions.descriptorToDocumentationTranslator with KotlinAsJavaDescriptorToDocumentationTranslator } + val kotlinAsJavaDocumentableToPageTranslator by extending { CoreExtensions.documentationToPageTranslator with KotlinAsJavaDocumentationToPageTranslator } +} + +object DescriptorCache { + private val cache: HashMap<DRI, DeclarationDescriptor> = HashMap() + + fun add(dri: DRI, descriptor: DeclarationDescriptor): Boolean = cache.putIfAbsent(dri, descriptor) == null + operator fun get(dri: DRI): DeclarationDescriptor? = cache[dri] +} + +object KotlinAsJavaDocumentationToPageTranslator : DocumentationToPageTranslator { + override fun invoke(module: Module, context: DokkaContext): ModulePageNode = + KotlinAsJavaPageBuilder { node, kind, operation -> + KotlinAsJavaPageContentBuilder.group( + setOf(node.dri), + node.platformData, + kind, + context.single(CoreExtensions.commentsToContentConverter), + context.logger, + operation + ) + }.pageForModule(module) + +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinToJVMResolver.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinToJVMResolver.kt new file mode 100644 index 00000000..87a173f3 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinToJVMResolver.kt @@ -0,0 +1,151 @@ +package org.jetbrains.dokka.kotlinAsJava.conversions + +import org.jetbrains.dokka.kotlinAsJava.DescriptorCache +import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.Enum +import org.jetbrains.dokka.transformers.psi.JavaTypeWrapper +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType + +fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() + .find { it.typeFqName.asString() == this } + ?.let { JvmPrimitiveType.get(it) } + +fun TypeWrapper.getAsType(classId: ClassId, fqName: String, top: Boolean): TypeWrapper { + val fqNameSplitted = fqName.takeIf { top }?.getAsPrimitive()?.name?.toLowerCase() + ?.let { listOf(it) } ?: classId.asString().split("/") + return JavaTypeWrapper( + fqNameSplitted, + arguments.mapNotNull { it.asJava(false) }, + classId.toDRI(dri), + fqNameSplitted.last()[0].isLowerCase() + ) +} + +fun TypeWrapper?.asJava(top: Boolean = true): TypeWrapper? = this?.constructorFqName + ?.takeUnless { it.endsWith(".Unit") } + ?.let { fqName -> + fqName.mapToJava() + ?.let { getAsType(it, fqName, top) } ?: this + } + +fun Classlike.asJava(): Classlike = when { + this is Class -> this.asJava() + this is Enum -> this.asJava() + this is EnumEntry -> this + else -> throw IllegalArgumentException("$this shouldn't be here") +} + +fun Class.asJava(): Class = Class( + dri, name, kind, + constructors.map { it.asJava() }, + (functions + properties.flatMap { it.accessors }).map { it.asJava() }, + properties, classlikes.mapNotNull { (it as? Class)?.asJava() }, expected, actual, extra, visibility +) + +fun Enum.asJava(): Enum = Enum( + dri = dri, + name = name, + entries = entries.mapNotNull { it.asJava() as? EnumEntry }, + constructors = constructors.map(Function::asJava), + functions = (functions + properties.flatMap { it.accessors }).map(Function::asJava), + properties = properties, + classlikes = classlikes.map(Classlike::asJava), + expected = expected, + actual = actual, + extra = extra, + visibility = visibility +) + +fun tcAsJava(tc: TypeConstructor): TypeReference = + tc.fullyQualifiedName.mapToJava() + ?.let { + tc.copy( + fullyQualifiedName = it.asString(), + params = tc.params.map { it.asJava() } + ) + } ?: tc + +fun tpAsJava(tp: TypeParam): TypeReference = + tp.copy(bounds = tp.bounds.map { it.asJava() }) + +fun TypeReference.asJava(): TypeReference = when (this) { + is TypeConstructor -> tcAsJava(this) + is TypeParam -> tpAsJava(this) + else -> this +} + +fun Callable.asJava(): Callable = copy(params = params.mapNotNull { (it as? TypeConstructor)?.asJava() }) + + +fun Parameter.asJava(): Parameter = Parameter( + dri.copy(callable = dri.callable?.asJava()), + name, + type.asJava()!!, + expected, + actual, + extra +) + +fun Function.asJava(): Function { + val newName = when { + isConstructor -> "init" + else -> name + } + return Function( + dri.copy(callable = dri.callable?.asJava()), + newName, + returnType.asJava(), + isConstructor, + receiver, + parameters.map { it.asJava() }, + expected, + actual, + extra, + visibility + ) +} + +private fun String.mapToJava(): ClassId? = + JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) + +fun ClassId.toDRI(dri: DRI?): DRI = DRI( + packageName = packageFqName.asString(), + classNames = classNames(), + callable = dri?.callable?.asJava(), + extra = null, + target = null +) + +fun ClassId.classNames(): String = + shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") + +fun Function.asStatic(): Function = also { it.extra.add(STATIC) } + +fun Property.withClass(className: String, dri: DRI): Property { + val nDri = dri.withClass(className).copy( + callable = getDescriptor()?.let { Callable.from(it) } + ) + return Property( + nDri, name, receiver, expected, actual, extra, accessors, visibility + ) +} + +fun Function.withClass(className: String, dri: DRI): Function { + val nDri = dri.withClass(className).copy( + callable = getDescriptor()?.let { Callable.from(it) } + ) + return Function( + nDri, name, returnType, isConstructor, receiver, parameters, expected, actual, extra, visibility + ) +} + +fun Function.getDescriptor(): FunctionDescriptor? = DescriptorCache[dri].let { it as? FunctionDescriptor } + +fun Property.getDescriptor(): PropertyDescriptor? = DescriptorCache[dri].let { it as? PropertyDescriptor } diff --git a/plugins/kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..8ff3df82 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin diff --git a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt new file mode 100644 index 00000000..c0833293 --- /dev/null +++ b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt @@ -0,0 +1,98 @@ +package kotlinAsJavaPlugin + +import junit.framework.Assert.fail +import org.jetbrains.dokka.pages.ContentGroup +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.ContentTable +import org.jetbrains.dokka.pages.children +import org.junit.Test +import testApi.testRunner.AbstractCoreTest + +class KotlinAsJavaPluginTest : AbstractCoreTest() { + + @Test + fun topLevelTest() { + val configuration = dokkaConfiguration { + passes { + pass { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + |object TestObj {} + | + |fun testFL(l: List<String>) = l + |fun testF() {} + |fun testF2(i: Int) = i + |fun testF3(to: TestObj) = to + |fun <T : Char> testF4(t: T) = listOf(t) + |val testV = 1 + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val content = (root.children.firstOrNull()?.children?.firstOrNull() as? ContentPage )?.content ?: run { + fail("Either children or content is null") + } + + val children = + if (content is ContentGroup) + content.children.filterIsInstance<ContentTable>().filter { it.children.isNotEmpty() } + else emptyList() + + children.assertCount(2) + } + } + } + + @Test + fun topLevelWithClassTest() { + val configuration = dokkaConfiguration { + passes { + pass { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + |class Test { + | fun testFC() {} + | val testVC = 1 + |} + | + |fun testF(i: Int) = i + |val testV = 1 + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val contentList = root.children + .flatMap { it.children<ContentPage>() } + .map { it.content } + + val children = contentList.flatMap { content -> + if (content is ContentGroup) + content.children.filterIsInstance<ContentTable>().filter { it.children.isNotEmpty() } + else emptyList() + }.filterNot { it.toString().contains("<init>") } + + children.assertCount(4) + } + } + } + + private fun <T> Collection<T>.assertCount(n: Int) = + assert(count() == n) { "Expected $n, got ${count()}" } + +}
\ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 295c484b..8efca079 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,9 +8,9 @@ include("runners:cli") include("runners:maven-plugin") include("plugins:xml") include("plugins:mathjax") +include("plugins:kotlin-as-java") include("integration-tests:gradle-integration-tests") - pluginManagement { val kotlin_version: String by settings plugins { |