diff options
10 files changed, 168 insertions, 53 deletions
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 91b24476..6f980383 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -203,7 +203,7 @@ class DefaultPsiToDocumentableTranslator( name.orEmpty(), fields.filterIsInstance<PsiEnumConstant>().map { entry -> DEnumEntry( - dri.withClass("$name.${entry.name}"), + dri.withClass("${entry.name}"), entry.name, javadocParser.parseDocumentation(entry).toSourceSetDependent(), null, diff --git a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt index b1549729..52a8a50b 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt @@ -48,6 +48,8 @@ open class JavadocPageCreator( methods = c.functions.mapNotNull { it.toJavadocFunction() }, entries = (c as? DEnum)?.entries?.map { JavadocEntryNode( + it.dri, + it.name, signatureForNode(it, jvm), it.descriptionToContentNodes(jvm) ) @@ -55,6 +57,8 @@ open class JavadocPageCreator( classlikes = c.classlikes.mapNotNull { pageForClasslike(it) }, properties = c.properties.map { JavadocPropertyNode( + it.dri, + it.name, signatureForNode(it, jvm), it.descriptionToContentNodes(jvm) ) @@ -142,7 +146,9 @@ open class JavadocPageCreator( JavadocParameterNode( name = it.name.orEmpty(), type = type, - description = it.brief() + description = it.brief(), + typeBound = it.type, + dri = it.dri ) } }, diff --git a/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt b/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt index 56a9015a..f77970eb 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt @@ -2,18 +2,22 @@ package javadoc.location import javadoc.pages.* import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.resolvers.local.LocationProvider import org.jetbrains.dokka.base.resolvers.local.BaseLocationProvider import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.Nullable +import org.jetbrains.dokka.links.parent +import org.jetbrains.dokka.model.OtherParameter +import org.jetbrains.dokka.model.PrimitiveJavaType +import org.jetbrains.dokka.model.TypeConstructor +import org.jetbrains.dokka.model.UnresolvedBound import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext import java.util.* -class JavadocLocationProvider(pageRoot: RootPageNode, private val context: DokkaContext) : BaseLocationProvider(context) { +class JavadocLocationProvider(pageRoot: RootPageNode, dokkaContext: DokkaContext) : + BaseLocationProvider(dokkaContext) { private val pathIndex = IdentityHashMap<PageNode, List<String>>().apply { fun registerPath(page: PageNode, prefix: List<String> = emptyList()) { @@ -53,13 +57,46 @@ class JavadocLocationProvider(pageRoot: RootPageNode, private val context: Dokka private fun List<String>.relativeTo(context: List<String>): String { val contextPath = context.dropLast(1) - val commonPathElements = zip(contextPath).takeWhile { (a,b) -> a == b }.count() - return (List(contextPath.size - commonPathElements ) { ".." } + this.drop(commonPathElements)).joinToString("/") + val commonPathElements = zip(contextPath).takeWhile { (a, b) -> a == b }.count() + return (List(contextPath.size - commonPathElements) { ".." } + this.drop(commonPathElements)).joinToString("/") } - override fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode?): String = - nodeIndex[dri]?.let { resolve(it, context) } + private fun JavadocClasslikePageNode.findAnchorableByDRI(dri: DRI): AnchorableJavadocNode? = + (constructors + methods + entries + properties).firstOrNull { it.dri == dri } + + override fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode?): String { + return nodeIndex[dri]?.let { resolve(it, context) } + ?: nodeIndex[dri.parent]?.let { + val anchor = when (val anchorElement = (it as? JavadocClasslikePageNode)?.findAnchorableByDRI(dri)) { + is JavadocFunctionNode -> anchorElement.getAnchor() + is JavadocEntryNode -> anchorElement.name + is JavadocPropertyNode -> anchorElement.name + else -> anchorForDri(dri) + } + "${resolve(it, context, skipExtension = true)}.html#$anchor" + } ?: getExternalLocation(dri, sourceSets) + } + + private fun JavadocFunctionNode.getAnchor(): String = + "$name-${parameters.joinToString(",%20") { + when (val bound = it.typeBound) { + is TypeConstructor -> bound.dri.classNames.orEmpty() + is OtherParameter -> bound.name + is PrimitiveJavaType -> bound.name + is UnresolvedBound -> bound.name + else -> bound.toString() + } + }}-" + + fun anchorForFunctionNode(node: JavadocFunctionNode) = node.getAnchor() + + private fun anchorForDri(dri: DRI): String = + dri.callable?.let { callable -> + "${callable.name}-${callable.params.joinToString(",%20") { + ((it as? Nullable)?.wrapped ?: it).toString() + }}-" + } ?: dri.classNames.orEmpty() override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = pathIndex[node]?.relativeTo(pathIndex[context].orEmpty())?.let { diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt index 02e4b2d6..216b9319 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt @@ -79,30 +79,38 @@ class JavadocPackagePageNode( ) } +sealed class AnchorableJavadocNode(open val dri: DRI) + data class JavadocEntryNode( + override val dri: DRI, + val name: String, val signature: JavadocSignatureContentNode, val brief: List<ContentNode> -) +): AnchorableJavadocNode(dri) data class JavadocParameterNode( + override val dri: DRI, val name: String, val type: ContentNode, - val description: List<ContentNode> -) + val description: List<ContentNode>, + val typeBound: Bound +): AnchorableJavadocNode(dri) data class JavadocPropertyNode( + override val dri: DRI, + val name: String, val signature: JavadocSignatureContentNode, val brief: List<ContentNode> -) +): AnchorableJavadocNode(dri) data class JavadocFunctionNode( val signature: JavadocSignatureContentNode, val brief: List<ContentNode>, val parameters: List<JavadocParameterNode>, val name: String, - val dri: DRI, + override val dri: DRI, val extras: PropertyContainer<DFunction> = PropertyContainer.empty() -) +): AnchorableJavadocNode(dri) class JavadocClasslikePageNode( override val name: String, diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt index df6490cf..7d3a51a6 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt @@ -1,5 +1,6 @@ package javadoc.renderer +import javadoc.location.JavadocLocationProvider import javadoc.pages.JavadocSignatureContentNode import org.jetbrains.dokka.base.resolvers.local.LocationProvider import org.jetbrains.dokka.pages.* @@ -7,7 +8,7 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.htmlEscape internal class JavadocContentToHtmlTranslator( - private val locationProvider: LocationProvider, + private val locationProvider: JavadocLocationProvider, private val context: DokkaContext ) { @@ -50,6 +51,6 @@ internal class JavadocContentToHtmlTranslator( """<a href=${address.formatToEndWithHtml()}>$content</a>""" private fun String.formatToEndWithHtml() = - if (endsWith(".html")) this else "$this.html" + if (endsWith(".html") || contains(Regex("\\.html#"))) this else "$this.html" } }
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt index 0c952fe9..56df469b 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt @@ -1,5 +1,6 @@ package javadoc.renderer +import javadoc.location.JavadocLocationProvider import javadoc.pages.* import javadoc.toNormalized import org.jetbrains.dokka.Platform @@ -13,7 +14,7 @@ import org.jetbrains.dokka.plugability.DokkaContext import java.nio.file.Paths internal class JavadocContentToTemplateMapTranslator( - private val locationProvider: LocationProvider, + private val locationProvider: JavadocLocationProvider, private val context: DokkaContext, ) { @@ -82,6 +83,7 @@ internal class JavadocContentToTemplateMapTranslator( "brief" to htmlForContentNodes(node.brief, contextNode), "parameters" to node.parameters.map { templateMapForParameterNode(it) }, "inlineParameters" to node.parameters.joinToString { renderInlineParameter(it) }, + "anchorLink" to locationProvider.anchorForFunctionNode(node), "signature" to templateMapForSignatureNode(node.signature), "name" to node.name ) diff --git a/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt b/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt index c2483cc2..f9bee318 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt @@ -99,10 +99,10 @@ class JavadocSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLo text("(") list(f.parameters) { annotationsInline(it) - text(it.modifiers()[it]?.toSignatureString() ?: "") + text(it.modifiers()[it]?.toSignatureString().orEmpty()) signatureForProjection(it.type) text(Typography.nbsp.toString()) - link(it.name!!, it.dri) + text(it.name.orEmpty()) } text(")") } @@ -180,7 +180,7 @@ class JavadocSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLo private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(p: Projection): Unit = when (p) { is OtherParameter -> link(p.name, p.declarationDRI) is TypeConstructor -> group { - link(p.dri.fqName(), p.dri) + link(p.dri.classNames.orEmpty(), p.dri) list(p.projections, prefix = "<", suffix = ">") { signatureForProjection(it) } @@ -191,7 +191,7 @@ class JavadocSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLo } is Star -> text("?") is Nullable -> signatureForProjection(p.inner) - is JavaObject, is Dynamic -> link("java.lang.Object", DRI("java.lang", "Object")) + is JavaObject, is Dynamic -> link("Object", DRI("java.lang", "Object")) is Void -> text("void") is PrimitiveJavaType -> text(p.name) is UnresolvedBound -> text(p.name) diff --git a/plugins/javadoc/src/main/resources/views/class.korte b/plugins/javadoc/src/main/resources/views/class.korte index 62d90f7d..7c0ca368 100644 --- a/plugins/javadoc/src/main/resources/views/class.korte +++ b/plugins/javadoc/src/main/resources/views/class.korte @@ -254,9 +254,7 @@ </a> <h3>Method Detail</h3> {% for method in methods.own %} - <a id="transform(model.ModelGraph,transformation.Transformation)"> - <!-- TODO --> - </a> + <a name="{{ method.anchorLink }}"> <ul class="blockListLast"> <li class="blockList"> <h4>{{ method.name }}</h4> diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt index dc1573e1..340e4697 100644 --- a/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt @@ -2,7 +2,6 @@ package javadoc import javadoc.pages.JavadocClasslikePageNode import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import testApi.utils.assertIsInstance @@ -94,8 +93,8 @@ internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest( 0, assertIsInstance<List<*>>(method["parameters"]).size, "Expected no parameters" ) - assertEquals("final <a href=.html>java.lang.String</a>", method.modifiers()) - assertEquals("<a href=.html>testFunction</a>()", method.signatureWithoutModifiers()) + assertEquals("final <a href=.html>String</a>", method.modifiers()) + assertEquals("<a href=TestClass.html#testFunction-->testFunction</a>()", method.signatureWithoutModifiers()) } } @@ -204,8 +203,8 @@ internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest( assertEquals("Sample docs for first", first["brief"]) assertEquals("Sample docs for second", second["brief"]) - assertEquals("<a href=.html>FIRST</a>", first.signatureWithoutModifiers()) - assertEquals("<a href=.html>SECOND</a>", second.signatureWithoutModifiers()) + assertEquals("<a href=ClockDays.html#FIRST>FIRST</a>", first.signatureWithoutModifiers()) + assertEquals("<a href=ClockDays.html#SECOND>SECOND</a>", second.signatureWithoutModifiers()) } } @@ -258,19 +257,19 @@ internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest( assertParameterNode( node = first, expectedName = "simple", - expectedType = "<a href=.html>java.lang.String</a>", + expectedType = "<a href=.html>String</a>", expectedDescription = "simple String parameter" ) assertParameterNode( node = second, expectedName = "parameters", - expectedType = "<a href=.html>java.lang.Integer</a>", + expectedType = "<a href=.html>Integer</a>", expectedDescription = "simple Integer parameter" ) assertParameterNode( node = third, expectedName = "list", - expectedType = "<a href=.html>java.lang.Boolean</a>", + expectedType = "<a href=.html>Boolean</a>", expectedDescription = "simple Boolean parameter" ) } @@ -306,14 +305,14 @@ internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest( assertEquals("Generic", map["name"]) assertEquals( - "public final class <a href=Generic.html>Generic</a><T extends <a href=.html>java.io.Serializable</a>>", + "public final class <a href=Generic.html>Generic</a><T extends <a href=.html>Serializable</a>>", map.signatureWithModifiers() ) val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) val ownMethods = assertIsInstance<List<*>>(methods["own"]).first() val sampleFunction = assertIsInstance<Map<String, Any?>>(ownMethods) - assertEquals("final <D extends <a href=Generic.html>T</a>> <a href=.html>D</a> <a href=.html>sampleFunction</a>()", sampleFunction.signatureWithModifiers()) + assertEquals("final <D extends <a href=Generic.html>T</a>> <a href=Generic.html#sampleFunction-->D</a> <a href=Generic.html#sampleFunction-->sampleFunction</a>()", sampleFunction.signatureWithModifiers()) } } diff --git a/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt index d60e1070..65d5481d 100644 --- a/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt +++ b/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt @@ -7,16 +7,17 @@ import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.ExternalDocumentationLinkImpl import org.jetbrains.dokka.javadoc.JavadocPlugin import org.jetbrains.dokka.model.firstChildOfType +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.assertEquals class JavadocTest : AbstractCoreTest() { - @Test - fun `resolved signature with external links`() { - + private fun locationTestInline(testHandler: (RootPageNode, DokkaContext) -> Unit) { fun externalLink(link: String) = DokkaConfiguration.ExternalDocumentationLink .Builder(link) .build() as ExternalDocumentationLinkImpl @@ -34,31 +35,94 @@ class JavadocTest : AbstractCoreTest() { } } } - testInline( """ |/jvmSrc/javadoc/Test.kt |package javadoc |import java.io.Serializable - |class Test() : Serializable, Cloneable + |class Test<A>() : Serializable, Cloneable { + | fun test() {} + | fun test2(s: String) {} + | fun <T> test3(a: A, t: T) {} + |} """.trimIndent(), config, cleanupOutput = false, pluginOverrides = listOf(JavadocPlugin()) - ) { - renderingStage = { rootPageNode, dokkaContext -> - val transformer = JavadocContentToHtmlTranslator( - dokkaContext.plugin<JavadocPlugin>().querySingle { locationProviderFactory } - .getLocationProvider(rootPageNode), - dokkaContext + ) { renderingStage = testHandler } + } + + @Test + fun `resolved signature with external links`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClass = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode>() + assertEquals( + " implements <a href=https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html>Serializable</a>, <a href=https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html>Cloneable</a>", + transformer.htmlForContentNode(testClass.signature.supertypes!!, null) + ) + } + } + + @Test + fun `resolved signature to no argument function`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClassNode = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode> { it.name == "Test" } + val testFunctionNode = testClassNode.methods.first { it.name == "test" } + assertEquals( + """<a href=Test.html#test-->test</a>()""", + transformer.htmlForContentNode( + testFunctionNode.signature.signatureWithoutModifiers, + testClassNode ) - val testClass = rootPageNode.firstChildOfType<JavadocPackagePageNode>() - .firstChildOfType<JavadocClasslikePageNode>() - assert( - " implements <a href=https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html>Serializable</a>, <a href=https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html>Cloneable</a>" - == transformer.htmlForContentNode(testClass.signature.supertypes!!, null) + ) + } + } + + @Test + fun `resolved signature to one argument function`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClassNode = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode> { it.name == "Test" } + val testFunctionNode = testClassNode.methods.first { it.name == "test2" } + assertEquals( + """<a href=Test.html#test2-String->test2</a>(<a href=https://docs.oracle.com/javase/8/docs/api/java/lang/String.html>String</a> s)""", + transformer.htmlForContentNode( + testFunctionNode.signature.signatureWithoutModifiers, + testClassNode ) - } + ) } } + + @Test + fun `resolved signature to generic function`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClassNode = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode> { it.name == "Test" } + val testFunctionNode = testClassNode.methods.first { it.name == "test3" } + assertEquals( + """<a href=Test.html#test3-A,%20T->test3</a>(<a href=Test.html>A</a> a, <a href=Test.html#test3-A,%20T->T</a> t)""", + transformer.htmlForContentNode( + testFunctionNode.signature.signatureWithoutModifiers, + testClassNode + ) + ) + } + } + + private fun htmlTranslator(rootPageNode: RootPageNode, dokkaContext: DokkaContext) = JavadocContentToHtmlTranslator( + dokkaContext.plugin<JavadocPlugin>().querySingle { locationProviderFactory } + .getLocationProvider(rootPageNode), + dokkaContext + ) } |