diff options
author | vmishenev <vad-mishenev@yandex.ru> | 2021-09-21 21:38:44 +0300 |
---|---|---|
committer | vmishenev <vad-mishenev@yandex.ru> | 2021-09-21 21:40:12 +0300 |
commit | 1ada27414d66e58b19e20a968834356e81dd26b2 (patch) | |
tree | 41a1f463df9f261fc8c0a7569088263310c227de | |
parent | 2c6169ad8c3f97e7b9f833879967497efd54ac2e (diff) | |
download | dokka-1ada27414d66e58b19e20a968834356e81dd26b2.tar.gz dokka-1ada27414d66e58b19e20a968834356e81dd26b2.tar.bz2 dokka-1ada27414d66e58b19e20a968834356e81dd26b2.zip |
Fix link to javadoc enum entry (#2131)
7 files changed, 370 insertions, 20 deletions
diff --git a/core/api/core.api b/core/api/core.api index ed83f3a7..24f103d7 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -460,6 +460,20 @@ public final class org/jetbrains/dokka/links/DRI$Companion { public final fun getTopLevel ()Lorg/jetbrains/dokka/links/DRI; } +public final class org/jetbrains/dokka/links/DRIExtraContainer { + public fun <init> ()V + public fun <init> (Ljava/lang/String;)V + public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun encode ()Ljava/lang/String; + public final fun getData ()Ljava/lang/String; + public final fun getMap ()Ljava/util/Map; +} + +public abstract class org/jetbrains/dokka/links/DRIExtraProperty { + public fun <init> ()V + public final fun getKey ()Ljava/lang/String; +} + public final class org/jetbrains/dokka/links/DRIKt { public static final fun getDriOfAny ()Lorg/jetbrains/dokka/links/DRI; public static final fun getDriOfUnit ()Lorg/jetbrains/dokka/links/DRI; @@ -478,6 +492,10 @@ public abstract class org/jetbrains/dokka/links/DriTarget { public final class org/jetbrains/dokka/links/DriTarget$Companion { } +public final class org/jetbrains/dokka/links/EnumEntryDRIExtra : org/jetbrains/dokka/links/DRIExtraProperty { + public static final field INSTANCE Lorg/jetbrains/dokka/links/EnumEntryDRIExtra; +} + public final class org/jetbrains/dokka/links/JavaClassReference : org/jetbrains/dokka/links/TypeReference { public fun <init> (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt index 25b9546f..c646934d 100644 --- a/core/src/main/kotlin/links/DRI.kt +++ b/core/src/main/kotlin/links/DRI.kt @@ -1,7 +1,10 @@ package org.jetbrains.dokka.links +import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue /** * [DRI] stands for DokkaResourceIdentifier @@ -23,6 +26,24 @@ data class DRI( } } +object EnumEntryDRIExtra: DRIExtraProperty<EnumEntryDRIExtra>() + +abstract class DRIExtraProperty<T> { + val key: String = this::class.qualifiedName + ?: (this.javaClass.let { it.`package`.name + "." + it.simpleName.ifEmpty { "anonymous" } }) +} + +class DRIExtraContainer(val data: String? = null) { + val map: MutableMap<String, Any> = if (data != null) ObjectMapper().readValue(data) else mutableMapOf() + inline operator fun <reified T> get(prop: DRIExtraProperty<T>): T? = + map[prop.key]?.let { prop as? T } + + inline operator fun <reified T> set(prop: DRIExtraProperty<T>, value: T) = + value.also { map[prop.key] = it as Any } + + fun encode(): String = ObjectMapper().writeValueAsString(map) +} + val DriOfUnit = DRI("kotlin", "Unit") val DriOfAny = DRI("kotlin", "Any") 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 3dd28fed..5f74c429 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 @@ -1,10 +1,9 @@ package org.jetbrains.dokka.analysis import com.intellij.psi.* -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.DriTarget +import org.jetbrains.dokka.links.* import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull @@ -14,13 +13,16 @@ fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWi val callable = parameter?.containingDeclaration ?: firstIsInstanceOrNull<CallableDescriptor>() DRI( - firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString() ?: "", - (filterIsInstance<ClassDescriptor>() + filterIsInstance<TypeAliasDescriptor>()).toList() + packageName = firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString() ?: "", + classNames = (filterIsInstance<ClassDescriptor>() + filterIsInstance<TypeAliasDescriptor>()).toList() .takeIf { it.isNotEmpty() } ?.asReversed() ?.joinToString(separator = ".") { it.name.asString() }, - callable?.let { Callable.from(it) }, - DriTarget.from(parameter ?: descriptor) + callable = callable?.let { Callable.from(it) }, + target = DriTarget.from(parameter ?: descriptor), + extra = if (descriptor is EnumEntrySyntheticClassDescriptor) + DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() + else null ) } @@ -30,9 +32,13 @@ fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run { val classes = filterIsInstance<PsiClass>().filterNot { it is PsiTypeParameter } .toList() // We only want exact PsiClass types, not PsiTypeParameter subtype DRI( - classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', "") ?: "", - classes.toList().takeIf { it.isNotEmpty() }?.asReversed()?.mapNotNull { it.name }?.joinToString("."), - psiMethod?.let { Callable.from(it) } ?: psiField?.let { Callable.from(it) }, - DriTarget.from(psi) + packageName = classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', "") ?: "", + classNames = classes.toList().takeIf { it.isNotEmpty() }?.asReversed()?.mapNotNull { it.name } + ?.joinToString("."), + callable = psiMethod?.let { Callable.from(it) } ?: psiField?.let { Callable.from(it) }, + target = DriTarget.from(psi), + extra = if (psi is PsiEnumConstant) + DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() + else null ) } 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 f1a32cb4..84445d2a 100644 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt @@ -2,8 +2,7 @@ package org.jetbrains.dokka.base.resolvers.external.javadoc import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.htmlEscape @@ -30,8 +29,21 @@ open class JavadocExternalLocationProvider( if (classNames == null) { return "$docWithModule$packageLink/package-summary$extension".htmlEscape() } - val classLink = - if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension" + + // 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 + 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" + return "$docWithModule$classLink#$enumEntityAnchor".htmlEscape() + } + + val classLink = if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension" val callableChecked = callable ?: return "$docWithModule$classLink".htmlEscape() return ("$docWithModule$classLink#" + anchorPart(callableChecked)).htmlEscape() diff --git a/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt new file mode 100644 index 00000000..cb2b0331 --- /dev/null +++ b/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt @@ -0,0 +1,78 @@ +package locationProvider + +import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider +import org.jetbrains.dokka.base.resolvers.external.javadoc.JavadocExternalLocationProvider +import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation +import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.net.URL + +class JavadocExternalLocationProviderTest : BaseAbstractTest() { + private val testDataDir = + getTestDataDir("locationProvider").toAbsolutePath().toString().removePrefix("/").let { "/$it" } + + private val jdk = "https://docs.oracle.com/javase/8/docs/api/" + private val jdkPackageListURL = URL("file://$testDataDir/jdk8-package-list") + + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath += jvmStdlibPath!! + } + } + } + + private fun getTestLocationProvider(context: DokkaContext? = null): DefaultExternalLocationProvider { + val dokkaContext = context ?: DokkaContext.create(configuration, logger, emptyList()) + val packageList = PackageList.load(jdkPackageListURL, 8, true)!! + val externalDocumentation = + ExternalDocumentation(URL(jdk), packageList) + return JavadocExternalLocationProvider(externalDocumentation, "--", "-", dokkaContext) + } + + @Test + fun `link to enum entity of javadoc`() { + val locationProvider = getTestLocationProvider() + val ktDri = DRI( + "java.nio.file", + "StandardOpenOption.CREATE", + extra = DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() + ) + val javaDri = DRI( + "java.nio.file", + "StandardOpenOption", + Callable("CREATE", null, emptyList()), + PointingToDeclaration, + DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() + ) + + assertEquals( + "https://docs.oracle.com/javase/8/docs/api/java/nio/file/StandardOpenOption.html#CREATE", + locationProvider.resolve(ktDri) + ) + + assertEquals( + "https://docs.oracle.com/javase/8/docs/api/java/nio/file/StandardOpenOption.html#CREATE", + locationProvider.resolve(javaDri) + ) + } + + @Test + fun `link to nested class of javadoc`() { + val locationProvider = getTestLocationProvider() + val dri = DRI( + "java.rmi.activation", + "ActivationGroupDesc.CommandEnvironment" + ) + + assertEquals( + "https://docs.oracle.com/javase/8/docs/api/java/rmi/activation/ActivationGroupDesc.CommandEnvironment.html", + locationProvider.resolve(dri) + ) + } +} diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt index da45a920..991b48ef 100644 --- a/plugins/base/src/test/kotlin/model/JavaTest.kt +++ b/plugins/base/src/test/kotlin/model/JavaTest.kt @@ -2,10 +2,7 @@ package model import org.jetbrains.dokka.Platform import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.JavaClassReference -import org.jetbrains.dokka.links.PointingToDeclaration -import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.links.* import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.Param import org.jetbrains.dokka.model.doc.Text @@ -369,7 +366,8 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { "java.lang.annotation", "RetentionPolicy", DRICallable("RUNTIME", null, emptyList()), - PointingToDeclaration + PointingToDeclaration, + DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() ) ) diff --git a/plugins/base/src/test/resources/locationProvider/jdk8-package-list b/plugins/base/src/test/resources/locationProvider/jdk8-package-list new file mode 100644 index 00000000..351c1868 --- /dev/null +++ b/plugins/base/src/test/resources/locationProvider/jdk8-package-list @@ -0,0 +1,217 @@ +java.applet +java.awt +java.awt.color +java.awt.datatransfer +java.awt.dnd +java.awt.event +java.awt.font +java.awt.geom +java.awt.im +java.awt.im.spi +java.awt.image +java.awt.image.renderable +java.awt.print +java.beans +java.beans.beancontext +java.io +java.lang +java.lang.annotation +java.lang.instrument +java.lang.invoke +java.lang.management +java.lang.ref +java.lang.reflect +java.math +java.net +java.nio +java.nio.channels +java.nio.channels.spi +java.nio.charset +java.nio.charset.spi +java.nio.file +java.nio.file.attribute +java.nio.file.spi +java.rmi +java.rmi.activation +java.rmi.dgc +java.rmi.registry +java.rmi.server +java.security +java.security.acl +java.security.cert +java.security.interfaces +java.security.spec +java.sql +java.text +java.text.spi +java.time +java.time.chrono +java.time.format +java.time.temporal +java.time.zone +java.util +java.util.concurrent +java.util.concurrent.atomic +java.util.concurrent.locks +java.util.function +java.util.jar +java.util.logging +java.util.prefs +java.util.regex +java.util.spi +java.util.stream +java.util.zip +javax.accessibility +javax.activation +javax.activity +javax.annotation +javax.annotation.processing +javax.crypto +javax.crypto.interfaces +javax.crypto.spec +javax.imageio +javax.imageio.event +javax.imageio.metadata +javax.imageio.plugins.bmp +javax.imageio.plugins.jpeg +javax.imageio.spi +javax.imageio.stream +javax.jws +javax.jws.soap +javax.lang.model +javax.lang.model.element +javax.lang.model.type +javax.lang.model.util +javax.management +javax.management.loading +javax.management.modelmbean +javax.management.monitor +javax.management.openmbean +javax.management.relation +javax.management.remote +javax.management.remote.rmi +javax.management.timer +javax.naming +javax.naming.directory +javax.naming.event +javax.naming.ldap +javax.naming.spi +javax.net +javax.net.ssl +javax.print +javax.print.attribute +javax.print.attribute.standard +javax.print.event +javax.rmi +javax.rmi.CORBA +javax.rmi.ssl +javax.script +javax.security.auth +javax.security.auth.callback +javax.security.auth.kerberos +javax.security.auth.login +javax.security.auth.spi +javax.security.auth.x500 +javax.security.cert +javax.security.sasl +javax.sound.midi +javax.sound.midi.spi +javax.sound.sampled +javax.sound.sampled.spi +javax.sql +javax.sql.rowset +javax.sql.rowset.serial +javax.sql.rowset.spi +javax.swing +javax.swing.border +javax.swing.colorchooser +javax.swing.event +javax.swing.filechooser +javax.swing.plaf +javax.swing.plaf.basic +javax.swing.plaf.metal +javax.swing.plaf.multi +javax.swing.plaf.nimbus +javax.swing.plaf.synth +javax.swing.table +javax.swing.text +javax.swing.text.html +javax.swing.text.html.parser +javax.swing.text.rtf +javax.swing.tree +javax.swing.undo +javax.tools +javax.transaction +javax.transaction.xa +javax.xml +javax.xml.bind +javax.xml.bind.annotation +javax.xml.bind.annotation.adapters +javax.xml.bind.attachment +javax.xml.bind.helpers +javax.xml.bind.util +javax.xml.crypto +javax.xml.crypto.dom +javax.xml.crypto.dsig +javax.xml.crypto.dsig.dom +javax.xml.crypto.dsig.keyinfo +javax.xml.crypto.dsig.spec +javax.xml.datatype +javax.xml.namespace +javax.xml.parsers +javax.xml.soap +javax.xml.stream +javax.xml.stream.events +javax.xml.stream.util +javax.xml.transform +javax.xml.transform.dom +javax.xml.transform.sax +javax.xml.transform.stax +javax.xml.transform.stream +javax.xml.validation +javax.xml.ws +javax.xml.ws.handler +javax.xml.ws.handler.soap +javax.xml.ws.http +javax.xml.ws.soap +javax.xml.ws.spi +javax.xml.ws.spi.http +javax.xml.ws.wsaddressing +javax.xml.xpath +org.ietf.jgss +org.omg.CORBA +org.omg.CORBA.DynAnyPackage +org.omg.CORBA.ORBPackage +org.omg.CORBA.TypeCodePackage +org.omg.CORBA.portable +org.omg.CORBA_2_3 +org.omg.CORBA_2_3.portable +org.omg.CosNaming +org.omg.CosNaming.NamingContextExtPackage +org.omg.CosNaming.NamingContextPackage +org.omg.Dynamic +org.omg.DynamicAny +org.omg.DynamicAny.DynAnyFactoryPackage +org.omg.DynamicAny.DynAnyPackage +org.omg.IOP +org.omg.IOP.CodecFactoryPackage +org.omg.IOP.CodecPackage +org.omg.Messaging +org.omg.PortableInterceptor +org.omg.PortableInterceptor.ORBInitInfoPackage +org.omg.PortableServer +org.omg.PortableServer.CurrentPackage +org.omg.PortableServer.POAManagerPackage +org.omg.PortableServer.POAPackage +org.omg.PortableServer.ServantLocatorPackage +org.omg.PortableServer.portable +org.omg.SendingContext +org.omg.stub.java.rmi +org.w3c.dom +org.w3c.dom.bootstrap +org.w3c.dom.events +org.w3c.dom.ls +org.w3c.dom.views +org.xml.sax +org.xml.sax.ext +org.xml.sax.helpers |