diff options
Diffstat (limited to 'plugins/base')
5 files changed, 159 insertions, 42 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index dfa7b480..4f292ca1 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -147,7 +147,8 @@ private class DokkaDescriptorVisitor( sourceSets = setOf(sourceSet), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.interfaces) ) ) } @@ -173,7 +174,8 @@ private class DokkaDescriptorVisitor( sourceSets = setOf(sourceSet), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.interfaces) ) ) } @@ -201,7 +203,8 @@ private class DokkaDescriptorVisitor( sourceSets = setOf(sourceSet), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.interfaces) ) ) } @@ -284,7 +287,8 @@ private class DokkaDescriptorVisitor( sourceSets = setOf(sourceSet), extra = PropertyContainer.withAll( descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), - descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.interfaces) ) ) } @@ -322,14 +326,14 @@ private class DokkaDescriptorVisitor( ) } - fun CallableMemberDescriptor.createDRI(wasOverriden: Boolean = false): Pair<DRI, Boolean> = + fun CallableMemberDescriptor.createDRI(wasOverridenBy: DRI? = null): Pair<DRI, DRI?> = if (kind == CallableMemberDescriptor.Kind.DECLARATION || overriddenDescriptors.isEmpty()) - Pair(DRI.from(this), wasOverriden) + Pair(DRI.from(this), wasOverridenBy) else - overriddenDescriptors.first().createDRI(true) + overriddenDescriptors.first().createDRI(DRI.from(this)) override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): DFunction { - val (dri, isInherited) = descriptor.createDRI() + val (dri, inheritedFrom) = descriptor.createDRI() val isExpect = descriptor.isExpect val actual = descriptor.createSources() @@ -352,7 +356,7 @@ private class DokkaDescriptorVisitor( type = descriptor.returnType!!.toBound(), sourceSets = setOf(sourceSet), extra = PropertyContainer.withAll( - InheritedFunction(isInherited), + InheritedFunction(inheritedFrom), descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), descriptor.getAnnotations().toSourceSetDependent().toAnnotations() ) @@ -550,8 +554,17 @@ private class DokkaDescriptorVisitor( getDocumentation()?.toSourceSetDependent() ?: emptyMap() private fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo { + val superClasses = hashSetOf<ClassDescriptor>() + + fun processSuperClasses(supers: List<ClassDescriptor>) { + supers.forEach { + superClasses.add(it) + processSuperClasses(it.getSuperInterfaces() + it.getAllSuperclassesWithoutAny()) + } + } + processSuperClasses(getSuperInterfaces() + getAllSuperclassesWithoutAny()) return ClassInfo( - (getSuperInterfaces() + getAllSuperclassesWithoutAny()).map { DRI.from(it) }, + superClasses.map { Supertype(DRI.from(it), it.kind == ClassKind.INTERFACE) }.toList(), resolveDescriptorData() ) } @@ -726,7 +739,13 @@ private class DokkaDescriptorVisitor( private fun ValueArgument.childrenAsText() = this.safeAs<KtValueArgument>()?.children?.map {it.text }.orEmpty() - private data class ClassInfo(val supertypes: List<DRI>, val docs: SourceSetDependent<DocumentationNode>) + private data class ClassInfo(private val allSupertypes: List<Supertype>, val docs: SourceSetDependent<DocumentationNode>){ + val supertypes: List<DRI> + get() = allSupertypes.map { it.dri } + + val interfaces: List<DRI> + get() = allSupertypes.filter { it.isInterface }.map { it.dri } + } private fun Visibility.toDokkaVisibility(): org.jetbrains.dokka.model.Visibility = when (this) { Visibilities.PUBLIC -> KotlinVisibility.Public @@ -740,6 +759,8 @@ private class DokkaDescriptorVisitor( "${this.enumClassId.relativeClassName.asString()}.${this.enumEntryName.identifier}" private fun fallbackPackageName(): String = "[${sourceSet.displayName} root]"// TODO: error-prone, find a better way to do it + + private data class Supertype(val dri: DRI, val isInterface: Boolean) } private fun DRI.withPackageFallbackTo(fallbackPackage: String): DRI { diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 6ec5c4f5..8b397859 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -1,18 +1,20 @@ package org.jetbrains.dokka.base.translators.psi -import com.intellij.icons.AllIcons.Nodes.Static -import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute import com.intellij.lang.jvm.JvmModifier +import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute import com.intellij.lang.jvm.types.JvmReferenceType import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.* import com.intellij.psi.impl.source.PsiClassReferenceType import com.intellij.psi.impl.source.PsiImmediateClassType -import com.intellij.psi.impl.source.tree.java.PsiArrayInitializerMemberValueImpl import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.nextTarget import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.DocumentationLink +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator @@ -27,9 +29,9 @@ import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.io.File -import java.lang.ClassValue object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { @@ -120,23 +122,24 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { fun parseClasslike(psi: PsiClass, parent: DRI): DClasslike = with(psi) { val dri = parent.withClass(name.toString()) - val ancestorsSet = hashSetOf<DRI>() + val ancestorsSet = hashSetOf<Ancestor>() val superMethodsKeys = hashSetOf<Int>() - val superMethods = mutableListOf<PsiMethod>() + val superMethods = mutableListOf<Pair<PsiMethod, DRI>>() methods.forEach { superMethodsKeys.add(it.hash) } fun parseSupertypes(superTypes: Array<PsiClassType>) { superTypes.forEach { type -> (type as? PsiClassType)?.takeUnless { type.shouldBeIgnored }?.resolve()?.let { + val definedAt = DRI.from(it) it.methods.forEach { method -> val hash = method.hash if (!method.isConstructor && !superMethodsKeys.contains(hash) && method.getVisibility() != Visibilities.PRIVATE ) { superMethodsKeys.add(hash) - superMethods.add(method) + superMethods.add(Pair(method, definedAt)) } } - ancestorsSet.add(DRI.from(it)) + ancestorsSet.add(Ancestor(DRI.from(it), it.isInterface)) parseSupertypes(it.superTypes) } } @@ -145,12 +148,13 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { val (regularFunctions, accessors) = splitFunctionsAndAccessors() val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent() val allFunctions = regularFunctions.mapNotNull { if (!it.isConstructor) parseFunction(it) else null } + - superMethods.map { parseFunction(it, isInherited = true) } + superMethods.map { parseFunction(it.first, inheritedFrom = it.second) } val source = PsiDocumentableSource(this).toSourceSetDependent() val classlikes = innerClasses.map { parseClasslike(it, dri) } val visibility = getVisibility().toSourceSetDependent() - val ancestors = ancestorsSet.toList().toSourceSetDependent() + val ancestors = ancestorsSet.toList().map { it.dri }.toSourceSetDependent() val modifiers = getModifier().toSourceSetDependent() + val implementedInterfacesExtra = ImplementedInterfaces(ancestorsSet.filter { it.isInterface }.map { it.dri }.toList()) return when { isAnnotationType -> DAnnotation( @@ -167,8 +171,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { constructors.map { parseFunction(it, true) }, mapTypeParameters(dri), setOf(sourceSetData), - PropertyContainer.empty<DAnnotation>() + annotations.toList().getAnnotations() - .toSourceSetDependent().toAnnotations() + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) ) isEnum -> DEnum( dri, @@ -183,8 +187,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { emptyList(), emptyList(), setOf(sourceSetData), - PropertyContainer.empty<DEnumEntry>() + entry.annotations.toList().getAnnotations() - .toSourceSetDependent().toAnnotations() + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) ) }, documentation, @@ -198,8 +202,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { constructors.map { parseFunction(it, true) }, ancestors, setOf(sourceSetData), - PropertyContainer.empty<DEnum>() + annotations.toList().getAnnotations().toSourceSetDependent() - .toAnnotations() + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) ) isInterface -> DInterface( dri, @@ -215,8 +219,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { mapTypeParameters(dri), ancestors, setOf(sourceSetData), - PropertyContainer.empty<DInterface>() + annotations.toList().getAnnotations().toSourceSetDependent() - .toAnnotations() + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) ) else -> DClass( dri, @@ -234,8 +238,8 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { null, modifiers, setOf(sourceSetData), - PropertyContainer.empty<DClass>() + annotations.toList().getAnnotations().toSourceSetDependent() - .toAnnotations() + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) ) } } @@ -243,9 +247,10 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { private fun parseFunction( psi: PsiMethod, isConstructor: Boolean = false, - isInherited: Boolean = false + inheritedFrom: DRI? = null ): DFunction { val dri = DRI.from(psi) + val docs = javadocParser.parseDocumentation(psi).toSourceSetDependent() return DFunction( dri, if (isConstructor) "<init>" else psi.name, @@ -254,13 +259,13 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { DParameter( dri.copy(target = dri.target.nextTarget()), psiParameter.name, - javadocParser.parseDocumentation(psiParameter).toSourceSetDependent(), + DocumentationNode(docs.entries.mapNotNull { it.value.children.filterIsInstance<Param>().firstOrNull { it.root.children.firstIsInstanceOrNull<DocumentationLink>()?.children?.firstIsInstanceOrNull<Text>()?.body == psiParameter.name } }).toSourceSetDependent(), null, getBound(psiParameter.type), setOf(sourceSetData) ) }, - javadocParser.parseDocumentation(psi).toSourceSetDependent(), + docs, null, PsiDocumentableSource(psi).toSourceSetDependent(), psi.getVisibility().toSourceSetDependent(), @@ -271,9 +276,9 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { setOf(sourceSetData), psi.additionalExtras().let { PropertyContainer.withAll( - InheritedFunction(isInherited), + InheritedFunction(inheritedFrom), it.toSourceSetDependent().toAdditionalModifiers(), - (psi.annotations.toList().getAnnotations() + it.getAnnotations()).toSourceSetDependent() + (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() .toAnnotations() ) } @@ -291,7 +296,7 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { ).toSet() - private fun Set<ExtraModifiers>.getAnnotations() = map { + private fun Set<ExtraModifiers>.toListOfAnnotations() = map { if (it !is ExtraModifiers.JavaOnlyModifiers.Static) Annotations.Annotation(DRI("kotlin.jvm", it.name.toLowerCase().capitalize()), emptyMap()) else @@ -398,14 +403,14 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { psi.additionalExtras().let { PropertyContainer.withAll<DProperty>( it.toSourceSetDependent().toAdditionalModifiers(), - (psi.annotations.toList().getAnnotations() + it.getAnnotations()).toSourceSetDependent() + (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() .toAnnotations() ) } ) } - private fun Collection<PsiAnnotation>.getAnnotations() = + private fun Collection<PsiAnnotation>.toListOfAnnotations() = filter { it !is KtLightAbstractAnnotation }.mapNotNull { it.toAnnotation() } private fun JvmAnnotationAttribute.toValue(): AnnotationParameterValue = when (this) { @@ -436,4 +441,6 @@ object DefaultPsiToDocumentableTranslator : SourceToDocumentableTranslator { DRI.from(it) } } + + private data class Ancestor(val dri: DRI, val isInterface: Boolean) } diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt index 335d834e..f66f88db 100644 --- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt +++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt @@ -1,8 +1,18 @@ package content.params import matchers.content.* +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.model.DFunction +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.SourceSetData +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.MemberPageNode +import org.jetbrains.dokka.pages.dfs import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.junit.jupiter.api.Test import utils.* @@ -558,4 +568,45 @@ class ContentForParamsTest : AbstractCoreTest() { } } } + + @Test + fun javaDocCommentWithDocumentedParameters(){ + testInline( + """ + |/src/main/java/test/Main.java + |package test + | public class Main { + | + | /** + | * comment to function + | * @param first comment to first param + | * @param second comment to second param + | */ + | public void sample(String first, String second) { + | + | } + | } + """.trimIndent(), testConfiguration + ){ + pagesTransformationStage = { + module -> + val sampleFunction = module.dfs { + it is MemberPageNode && it.dri.first().toString() == "test/Main/sample/#java.lang.String#java.lang.String/PointingToDeclaration/" + } as MemberPageNode + val forJvm = (sampleFunction.documentable as DFunction).parameters.mapNotNull { + val jvm = it.documentation.keys.first { it.platform == Platform.jvm } + it.documentation[jvm] + } + + assert(forJvm.size == 2) + val (first, second) = forJvm.map { it.paramsDescription() } + assert(first == "comment to first param") + assert(second == "comment to second param") + } + } + } + + private fun DocumentationNode.paramsDescription(): String = + children.firstIsInstanceOrNull<Param>()?.root?.children?.firstIsInstanceOrNull<Text>()?.body.orEmpty() + }
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt index 5616a6c3..9121f9ae 100644 --- a/plugins/base/src/test/kotlin/model/ClassesTest.kt +++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt @@ -1,6 +1,7 @@ package model import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.KotlinModifier.* import org.junit.jupiter.api.Assertions.assertNull @@ -425,7 +426,7 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class """@Suppress("abc") class Foo() {}""" ) { with((this / "classes" / "Foo").cast<DClass>()) { - with(extra[Annotations]!!.content.entries.single().value.firstOrNull().assertNotNull("annotations")) { + with(extra[Annotations]?.content?.firstOrNull().assertNotNull("annotations")) { dri.toString() equals "kotlin/Suppress///PointingToDeclaration/" (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"abc\"")) } @@ -483,4 +484,20 @@ class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "class } } } + + @Test fun allImplementedInterfaces() { + inlineModelTest( + """ + | interface Highest { } + | open class HighestImpl: Highest { } + | interface Lower { } + | interface LowerImplInterface: Lower { } + | class Tested : HighestImpl(), LowerImplInterface { } + """.trimIndent() + ){ + with((this / "classes" / "Tested").cast<DClass>()){ + extra[ImplementedInterfaces]?.interfaces?.map { it.sureClassNames }?.sorted() equals listOf("Highest", "Lower", "LowerImplInterface").sorted() + } + } + } }
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt index 8f52fcc8..77fcc666 100644 --- a/plugins/base/src/test/kotlin/model/JavaTest.kt +++ b/plugins/base/src/test/kotlin/model/JavaTest.kt @@ -2,16 +2,22 @@ package model import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.pages.dfs +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import utils.AbstractModelTest import utils.assertNotNull +import utils.docs import utils.name class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { - @Test //todo params in comments + @Test fun function() { inlineModelTest( """ @@ -30,12 +36,27 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { children counts 1 with((this / "fn").cast<DFunction>()) { name equals "fn" - this + val params = parameters.map { it.documentation.values.first().children.first() as Param } + params.mapNotNull { it.root.children.firstIsInstanceOrNull<Text>()?.body } equals listOf("is String parameter", "is int parameter") } } } } + @Test fun allImplementedInterfacesInJava() { + inlineModelTest( + """ + |interface Highest { } + |interface Lower extends Highest { } + |class Extendable { } + |class Tested extends Extendable implements Lower { } + """){ + with((this / "java" / "Tested").cast<DClass>()){ + extra[ImplementedInterfaces]?.interfaces?.map { it.sureClassNames }?.sorted() equals listOf("Highest", "Lower").sorted() + } + } + } + //@Test fun function() { // verifyJavaPackageMember("testdata/java/member.java", defaultModelConfig) { cls -> // assertEquals("Test", cls.name) |