From 0cdd2879a462b2481b39b67e3b1ae29f724e9e0e Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 17 Jan 2022 12:42:01 +0100 Subject: [PATCH] Unify DRIs for Kotlin and Java enums. Add EnumEntry linking tests --- .../jetbrains/dokka/analysis/DRIFactory.kt | 25 +++----- .../JavadocExternalLocationProvider.kt | 7 +-- .../resolvers/local/DokkaLocationProvider.kt | 1 + .../test/kotlin/linking/EnumValuesLinking.kt | 61 ++++++++++++++++--- 4 files changed, 61 insertions(+), 33 deletions(-) diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt index 2a358531a8..05102384d9 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt @@ -15,24 +15,12 @@ fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWi val callable = parameter?.containingDeclaration ?: firstIsInstanceOrNull() DRI( packageName = firstIsInstanceOrNull()?.fqName?.asString() ?: "", - classNames = (filterIsInstance().map { - if (it.kind == ClassKind.ENUM_ENTRY) - it.name.asString().split(".").dropLast(1).joinToString(".") - else - it.name.asString() - } + filterIsInstance().map { it.name.asString() } - ).toList() - .filter { it.isNotBlank() } + classNames = (filterIsInstance() + filterIsInstance()).toList() .takeIf { it.isNotEmpty() } ?.asReversed() - ?.joinToString(separator = "."), - callable = callable?.let { Callable.from(it) } - ?: descriptor.safeAs().takeIf { it?.kind == ClassKind.ENUM_ENTRY }?.let { Callable.from(it) } - ?: descriptor.safeAs()?.let { Callable.from(it) }, - target = DriTarget.from(parameter ?: descriptor), - extra = if (descriptor is EnumEntrySyntheticClassDescriptor) - DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() - else null + ?.joinToString(separator = ".") { it.name.asString() }, + callable = callable?.let { Callable.from(it) }, + target = DriTarget.from(parameter ?: descriptor) ) } @@ -43,9 +31,10 @@ fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run { .toList() // We only want exact PsiClass types, not PsiTypeParameter subtype DRI( packageName = classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', "") ?: "", - classNames = classes.toList().takeIf { it.isNotEmpty() }?.asReversed()?.mapNotNull { it.name } + classNames = (if (psi is PsiEnumConstant) listOfNotNull(psiField?.name) + classes.toList().mapNotNull { it.name } else classes.toList().mapNotNull { it.name }) + .takeIf { it.isNotEmpty() }?.asReversed() ?.joinToString("."), - callable = psiMethod?.let { Callable.from(it) } ?: psiField?.let { Callable.from(it) }, + callable = psiMethod?.let { Callable.from(it) } ?: psiField?.takeUnless { psi is PsiEnumConstant }?.let { Callable.from(it) }, target = DriTarget.from(psi), extra = if (psi is PsiEnumConstant) DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() diff --git a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt index 84445d2a05..a1f1542dbb 100644 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt @@ -30,13 +30,10 @@ open class JavadocExternalLocationProvider( return "$docWithModule$packageLink/package-summary$extension".htmlEscape() } - // in Kotlin DRI of enum entry is not callable if (DRIExtraContainer(extra)[EnumEntryDRIExtra] != null) { - val (classSplit, enumEntityAnchor) = if (callable == null) { - val lastIndex = classNames?.lastIndexOf(".") ?: 0 + val lastIndex = classNames?.lastIndexOf(".") ?: 0 + val (classSplit, enumEntityAnchor) = classNames?.substring(0, lastIndex) to classNames?.substring(lastIndex + 1) - } else - classNames to callable?.name val classLink = if (packageLink == null) "${classSplit}$extension" else "$packageLink/${classSplit}$extension" diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt index c4c4633178..691ae26e69 100644 --- a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt @@ -82,6 +82,7 @@ open class DokkaLocationProvider( val driWithSourceSets = DRIWithSourceSets(dri, setOfNotNull(sourceSet)) getLocalLocation(driWithSourceSets, context) ?: getLocalLocation(driWithSourceSets.copy(dri = dri.copy(target = PointingToDeclaration)), context) + ?: getLocalLocation(driWithSourceSets.copy(dri = dri.copy(target = PointingToDeclaration, extra = null)), context) // Not found in PageGraph, that means it's an external link ?: getExternalLocation(dri, sourceSets) ?: getExternalLocation(dri.copy(target = PointingToDeclaration), sourceSets) diff --git a/plugins/base/src/test/kotlin/linking/EnumValuesLinking.kt b/plugins/base/src/test/kotlin/linking/EnumValuesLinking.kt index f690dfbdae..0302555021 100644 --- a/plugins/base/src/test/kotlin/linking/EnumValuesLinking.kt +++ b/plugins/base/src/test/kotlin/linking/EnumValuesLinking.kt @@ -1,16 +1,23 @@ package linking import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.doc.DocumentationLink +import org.jetbrains.dokka.pages.ContentDRILink +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jsoup.Jsoup +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import java.nio.file.Paths -import org.junit.jupiter.api.Assertions.assertEquals +import utils.TestOutputWriterPlugin import java.lang.AssertionError class EnumValuesLinking : BaseAbstractTest() { @Test fun `check if enum values are correctly linked`() { + val writerPlugin = TestOutputWriterPlugin() val testDataDir = getTestDataDir("linking").toAbsolutePath() testFromData( dokkaConfiguration { @@ -21,7 +28,8 @@ class EnumValuesLinking : BaseAbstractTest() { name = "jvm" } } - } + }, + pluginOverrides = listOf(writerPlugin) ) { documentablesTransformationStage = { val classlikes = it.packages.single().children @@ -31,16 +39,16 @@ class EnumValuesLinking : BaseAbstractTest() { javaLinker.documentation.values.single().children.run { when (val kotlinLink = this[0].children[1].children[1]) { is DocumentationLink -> kotlinLink.dri.run { - assertEquals("KotlinEnum", this.classNames) - assertEquals("ON_CREATE", this.callable?.name) + assertEquals("KotlinEnum.ON_CREATE", this.classNames) + assertEquals(null, this.callable) } else -> throw AssertionError("Link node is not DocumentationLink type") } when (val javaLink = this[0].children[2].children[1]) { is DocumentationLink -> javaLink.dri.run { - assertEquals("JavaEnum", this.classNames) - assertEquals("ON_DECEIT", this.callable?.name) + assertEquals("JavaEnum.ON_DECEIT", this.classNames) + assertEquals(null, this.callable) } else -> throw AssertionError("Link node is not DocumentationLink type") } @@ -50,20 +58,53 @@ class EnumValuesLinking : BaseAbstractTest() { kotlinLinker.documentation.values.single().children.run { when (val kotlinLink = this[0].children[0].children[5]) { is DocumentationLink -> kotlinLink.dri.run { - assertEquals("KotlinEnum", this.classNames) - assertEquals("ON_CREATE", this.callable?.name) + assertEquals("KotlinEnum.ON_CREATE", this.classNames) + assertEquals(null, this.callable) } else -> throw AssertionError("Link node is not DocumentationLink type") } when (val javaLink = this[0].children[0].children[9]) { is DocumentationLink -> javaLink.dri.run { - assertEquals("JavaEnum", this.classNames) - assertEquals("ON_DECEIT", this.callable?.name) + assertEquals("JavaEnum.ON_DECEIT", this.classNames) + assertEquals(null, this.callable) } else -> throw AssertionError("Link node is not DocumentationLink type") } } + + assertEquals( + javaLinker.documentation.values.single().children[0].children[1].children[1].safeAs()?.dri?.copy(extra=null), + kotlinLinker.documentation.values.single().children[0].children[0].children[5].safeAs()?.dri + ) + + assertEquals( + javaLinker.documentation.values.single().children[0].children[2].children[1].safeAs()?.dri?.copy(extra=null), + kotlinLinker.documentation.values.single().children[0].children[0].children[9].safeAs()?.dri + ) + } + + renderingStage = { rootPageNode, _ -> + val classlikes = rootPageNode.children.single().children + assertEquals(4, classlikes.size) + + val javaLinker = classlikes.single { it.name == "JavaLinker" } + (javaLinker as ContentPage).run { + assertNotNull(content.dfs { it is ContentDRILink && it.address.classNames == "KotlinEnum.ON_CREATE" }) + assertNotNull(content.dfs { it is ContentDRILink && it.address.classNames == "JavaEnum.ON_DECEIT" }) + } + + val kotlinLinker = classlikes.single { it.name == "KotlinLinker" } + (kotlinLinker as ContentPage).run { + assertNotNull(content.dfs { it is ContentDRILink && it.address.classNames == "KotlinEnum.ON_CREATE" }) + assertNotNull(content.dfs { it is ContentDRILink && it.address.classNames == "JavaEnum.ON_DECEIT" }) + } + + // single method will throw an exception if there is no single element (0 or 2+) + Jsoup.parse(writerPlugin.writer.contents["root/linking.source/-java-linker/index.html"]).select("a[href=\"../-kotlin-enum/-o-n_-c-r-e-a-t-e/index.html\"]").single() + Jsoup.parse(writerPlugin.writer.contents["root/linking.source/-java-linker/index.html"]).select("a[href=\"../-java-enum/-o-n_-d-e-c-e-i-t/index.html\"]").single() + Jsoup.parse(writerPlugin.writer.contents["root/linking.source/-kotlin-linker/index.html"]).select("a[href=\"../-kotlin-enum/-o-n_-c-r-e-a-t-e/index.html\"]").single() + Jsoup.parse(writerPlugin.writer.contents["root/linking.source/-kotlin-linker/index.html"]).select("a[href=\"../-java-enum/-o-n_-d-e-c-e-i-t/index.html\"]").single() } } }