diff options
Diffstat (limited to 'plugins/base')
-rw-r--r-- | plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt | 241 |
1 files changed, 193 insertions, 48 deletions
diff --git a/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt index 364cb34b..da06e621 100644 --- a/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/transformers/psi/DefaultPsiToDocumentableTranslator.kt @@ -1,5 +1,7 @@ package org.jetbrains.dokka.base.transformers.psi +import com.intellij.lang.jvm.JvmModifier +import com.intellij.lang.jvm.types.JvmReferenceType import com.intellij.psi.* import org.jetbrains.dokka.JavadocParser import org.jetbrains.dokka.links.Callable @@ -7,7 +9,11 @@ import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.JavaClassReference import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Annotation +import org.jetbrains.dokka.model.Enum import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.InheritedFunction +import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.psi.PsiToDocumentableTranslator @@ -27,16 +33,22 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator { platformData, context.logger ) - return Module(moduleName, + return Module( + moduleName, psiFiles.map { psiFile -> val dri = DRI(packageName = psiFile.packageName) Package( dri, emptyList(), emptyList(), - psiFile.classes.map { docParser.parseClass(it, dri) } + psiFile.classes.map { docParser.parseClasslike(it, dri) }, + emptyList(), + PlatformDependent.empty(), + listOf(platformData) ) - } + }, + PlatformDependent.empty(), + listOf(platformData) ) } @@ -47,11 +59,6 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator { private val javadocParser: JavadocParser = JavadocParser(logger) - private fun getComment(psi: PsiNamedElement): List<PlatformInfo> { - val comment = javadocParser.parseDocumentation(psi) - return listOf(BasePlatformInfo(comment, listOf(platformData))) - } - private fun PsiModifierListOwner.getVisibility() = modifierList?.children?.toList()?.let { ml -> when { ml.any { it.text == PsiKeyword.PUBLIC } -> Visibilities.PUBLIC @@ -60,69 +67,203 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator { } } ?: Visibilities.PRIVATE - fun parseClass(psi: PsiClass, parent: DRI): Class = with(psi) { - val kind = when { - isAnnotationType -> JavaClassKindTypes.ANNOTATION_CLASS - isInterface -> JavaClassKindTypes.INTERFACE - isEnum -> JavaClassKindTypes.ENUM_CLASS - else -> JavaClassKindTypes.CLASS + private val PsiMethod.hash: Int + get() = "$returnType $name$parameterList".hashCode() + + private val PsiClassType.shouldBeIgnored: Boolean + get() = isClass("java.lang.Enum") || isClass("java.lang.Object") + + private fun PsiClassType.isClass(qName: String): Boolean { + val shortName = qName.substringAfterLast('.') + if (className == shortName) { + val psiClass = resolve() + return psiClass?.qualifiedName == qName } + return false + } + + private fun <T> T.toPlatformDependant() = + PlatformDependent(mapOf(platformData to this)) + + fun parseClasslike(psi: PsiClass, parent: DRI): Classlike = with(psi) { val dri = parent.withClass(name.toString()) - /*superTypes.filter { !ignoreSupertype(it) }.forEach { - node.appendType(it, NodeKind.Supertype) - val superClass = it.resolve() - if (superClass != null) { - link(superClass, node, RefKind.Inheritor) + val ancestorsSet = hashSetOf<DRI>() + val superMethodsKeys = hashSetOf<Int>() + val superMethods = mutableListOf<PsiMethod>() + methods.forEach { superMethodsKeys.add(it.hash) } + fun addAncestors(element: PsiClass) { + ancestorsSet.add(element.toDRI()) + element.interfaces.forEach(::addAncestors) + element.superClass?.let(::addAncestors) + } + + fun parseSupertypes(superTypes: Array<PsiClassType>) { + superTypes.forEach { type -> + (type as? PsiClassType)?.takeUnless { type.shouldBeIgnored }?.resolve()?.let { + it.methods.forEach { method -> + val hash = method.hash + if (!method.isConstructor && !superMethodsKeys.contains(hash) && + method.getVisibility() != Visibilities.PRIVATE + ) { + superMethodsKeys.add(hash) + superMethods.add(method) + } + } + addAncestors(it) + parseSupertypes(it.superTypes) + } + } + } + parseSupertypes(superTypes) + val documentation = javadocParser.parseDocumentation(this).toPlatformDependant() + val allFunctions = methods.mapNotNull { if (!it.isConstructor) parseFunction(it, dri) else null } + + superMethods.map { parseFunction(it, dri, isInherited = true) } + val source = PsiDocumentableSource(this).toPlatformDependant() + val classlikes = innerClasses.map { parseClasslike(it, dri) } + val visibility = getVisibility().toPlatformDependant() + val ancestors = ancestorsSet.toList().toPlatformDependant() + return when { + isAnnotationType -> + Annotation( + name.orEmpty(), + dri, + documentation, + source, + allFunctions, + fields.mapNotNull { parseField(it, dri) }, + classlikes, + visibility, + null, + constructors.map { parseFunction(it, dri, true) }, + listOf(platformData) + ) + isEnum -> Enum( + dri, + name.orEmpty(), + fields.filterIsInstance<PsiEnumConstant>().map { entry -> + EnumEntry( + dri.withClass("$name.${entry.name}"), + entry.name.orEmpty(), + javadocParser.parseDocumentation(entry).toPlatformDependant(), + emptyList(), + emptyList(), + emptyList(), + listOf(platformData) + ) + }, + documentation, + source, + allFunctions, + fields.filter { it !is PsiEnumConstant }.map { parseField(it, dri) }, + classlikes, + visibility, + null, + constructors.map { parseFunction(it, dri, true) }, + ancestors, + listOf(platformData) + ) + isInterface -> Interface( + dri, + name.orEmpty(), + documentation, + source, + allFunctions, + fields.mapNotNull { parseField(it, dri) }, + classlikes, + visibility, + null, + mapTypeParameters(dri), + ancestors, + listOf(platformData) + ) + else -> Class( + dri, + name.orEmpty(), + constructors.map { parseFunction(it, dri, true) }, + allFunctions, + fields.mapNotNull { parseField(it, dri) }, + classlikes, + source, + visibility, + null, + mapTypeParameters(dri), + ancestors, + documentation, + getModifier(), + listOf(platformData) + ) } - }*/ - val inherited = emptyList<DRI>() //listOf(psi.superClass) + psi.interfaces // TODO DRIs of inherited - val actual = getComment(psi).map { ClassPlatformInfo(it, inherited) } - - return Class( - dri = dri, - name = name.orEmpty(), - kind = kind, - constructors = constructors.map { parseFunction(it, dri, true) }, - functions = methods.mapNotNull { if (!it.isConstructor) parseFunction(it, dri) else null }, - properties = fields.mapNotNull { parseField(it, dri) }, - classlikes = innerClasses.map { parseClass(it, dri) }, - expected = null, - actual = actual, - extra = mutableSetOf(), - visibility = mapOf(platformData to psi.getVisibility()) - ) } - private fun parseFunction(psi: PsiMethod, parent: DRI, isConstructor: Boolean = false): Function { + private fun parseFunction( + psi: PsiMethod, + parent: DRI, + isConstructor: Boolean = false, + isInherited: Boolean = false + ): Function { val dri = parent.copy(callable = Callable( psi.name, JavaClassReference(psi.containingClass?.name.orEmpty()), psi.parameterList.parameters.map { parameter -> JavaClassReference(parameter.type.canonicalText) - } - ) + }) ) return Function( dri, if (isConstructor) "<init>" else psi.name, - psi.returnType?.let { JavaTypeWrapper(type = it) }, isConstructor, - null, psi.parameterList.parameters.mapIndexed { index, psiParameter -> Parameter( dri.copy(target = index + 1), psiParameter.name, + javadocParser.parseDocumentation(psiParameter).toPlatformDependant(), JavaTypeWrapper(psiParameter.type), - null, - getComment(psi) + listOf(platformData) ) }, + javadocParser.parseDocumentation(psi).toPlatformDependant(), + PsiDocumentableSource(psi).toPlatformDependant(), + psi.getVisibility().toPlatformDependant(), + psi.returnType?.let { JavaTypeWrapper(type = it) } ?: JavaTypeWrapper.VOID, + psi.mapTypeParameters(dri), null, - getComment(psi), - visibility = mapOf(platformData to psi.getVisibility()) + psi.getModifier(), + listOf(platformData), + PropertyContainer.empty<Function>() + InheritedFunction( + isInherited + ) + ) } + private fun PsiModifierListOwner.getModifier() = when { + hasModifier(JvmModifier.ABSTRACT) -> WithAbstraction.Modifier.Abstract + hasModifier(JvmModifier.STATIC) -> WithAbstraction.Modifier.Static + hasModifier(JvmModifier.FINAL) -> WithAbstraction.Modifier.Final + else -> null + } + + private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List<TypeParameter> { + fun mapProjections(bounds: Array<JvmReferenceType>) = + if (bounds.isEmpty()) listOf(Projection.Star) else bounds.mapNotNull { + (it as PsiClassType).let { classType -> + Projection.Nullable(Projection.TypeConstructor(classType.resolve()!!.toDRI(), emptyList())) + } + } + return typeParameters.mapIndexed { index, type -> + TypeParameter( + dri.copy(genericTarget = index), + type.name.orEmpty(), + javadocParser.parseDocumentation(type).toPlatformDependant(), + mapProjections(type.bounds), + listOf(platformData) + ) + } + } + + private fun PsiQualifiedNamedElement.toDRI() = + DRI(qualifiedName.orEmpty().substringBeforeLast('.', ""), name) + private fun parseField(psi: PsiField, parent: DRI): Property { val dri = parent.copy( callable = Callable( @@ -134,11 +275,15 @@ object DefaultPsiToDocumentableTranslator : PsiToDocumentableTranslator { return Property( dri, psi.name!!, // TODO: Investigate if this is indeed nullable + javadocParser.parseDocumentation(psi).toPlatformDependant(), + PsiDocumentableSource(psi).toPlatformDependant(), + psi.getVisibility().toPlatformDependant(), + JavaTypeWrapper(psi.type), + null, null, null, - getComment(psi), - accessors = emptyList(), - visibility = mapOf(platformData to psi.getVisibility()) + psi.getModifier(), + listOf(platformData) ) } } |