From ac9fe0e78e199b48ffab79c2935b734784e9d28d Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Wed, 30 Sep 2020 17:59:17 +0200 Subject: [PATCH] Fix multiline links in javadoc --- .../kotlin/translators/psi/JavadocParser.kt | 28 ++++---- .../javadoc/location/JavadocLinkingTest.kt | 68 +++++++++++++++++++ 2 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt diff --git a/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt index 5baaf35ac2..0e0bfd8825 100644 --- a/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt +++ b/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt @@ -5,6 +5,8 @@ import com.intellij.psi.impl.source.javadoc.PsiDocParamRef import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.javadoc.* +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.java.IJavaDocElementType import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.dokka.analysis.from import org.jetbrains.dokka.links.DRI @@ -40,7 +42,7 @@ class JavadocParser( ) "throws" -> Throws(wrapTagIfNecessary(convertJavadocElements(tag.contentElements())), tag.text) "return" -> Return(wrapTagIfNecessary(convertJavadocElements(tag.contentElements()))) - "author" -> Author(wrapTagIfNecessary(convertJavadocElements(tag.contentElements()))) + "author" -> Author(wrapTagIfNecessary(convertJavadocElements(tag.authorContentElements()))) // Workaround: PSI returns first word after @author tag as a `DOC_TAG_VALUE_ELEMENT`, then the rest as a `DOC_COMMENT_DATA`, so for `Name Surname` we get them parted "see" -> getSeeTagElementContent(tag).let { See( wrapTagIfNecessary(it.first), @@ -157,20 +159,19 @@ class JavadocParser( } private fun PsiElement.toDocumentationLinkString( - labelElement: PsiElement? = null - ): String? = - reference?.resolve()?.let { - if (it !is PsiParameter) { - val dri = DRI.from(it) - driMap[dri.toString()] = dri - val label = labelElement ?: defaultLabel() - """${label.text}""" - } else null + labelElement: List? = null + ): String = + (reference?.resolve()?.takeIf { it !is PsiParameter }?.let { + val dri = DRI.from(it) + driMap[dri.toString()] = dri + Pair(labelElement ?: listOf(defaultLabel()), dri.toString()) + } ?: Pair(listOf(defaultLabel()), "UNRESOLVED_PSI_ELEMENT")).let { (label, dri) -> + """${label.joinToString(" ") { it.text }}""" } private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { "link", "linkplain" -> { - tag.referenceElement()?.toDocumentationLinkString(tag.dataElements.firstIsInstanceOrNull()) + tag.referenceElement()?.toDocumentationLinkString(tag.dataElements.filterIsInstance()) } "code", "literal" -> { "${tag.text}" @@ -187,7 +188,7 @@ class JavadocParser( A(children, params = mapOf("href" to element.attr("href"))) element.hasAttr("data-dri") && driMap.containsKey(element.attr("data-dri")) -> DocumentationLink(driMap[element.attr("data-dri")]!!, children) - else -> Text(children = children) + else -> Text(body = children.filterIsInstance().joinToString { it.body }) } } @@ -226,6 +227,9 @@ class JavadocParser( } private fun PsiDocTag.contentElements(): List = + dataElements.mapNotNull { it.takeIf { it is PsiDocToken && it.text.isNotBlank() } } + + private fun PsiDocTag.authorContentElements(): List = dataElements.mapNotNull { it.takeIf { it.text.isNotBlank() } } private fun convertJavadocElements(elements: Iterable, asParagraph: Boolean = true): List = Parse()(elements, asParagraph) diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt new file mode 100644 index 0000000000..c3e5922c8c --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt @@ -0,0 +1,68 @@ +package org.jetbrains.dokka.javadoc.location + +import org.jetbrains.dokka.ExternalDocumentationLink +import org.jetbrains.dokka.model.doc.DocumentationLink +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.dokka.utilities.cast +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class JavadocLinkingTest : AbstractCoreTest() { + + @Test + fun `linebroken link`() { + fun externalLink(link: String) = ExternalDocumentationLink(link) + + val config = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("jvmSrc/") + externalDocumentationLinks = listOf( + externalLink("https://docs.oracle.com/javase/8/docs/api/"), + externalLink("https://kotlinlang.org/api/latest/jvm/stdlib/") + ) + analysisPlatform = "jvm" + } + } + } + testInline( + """ + |/jvmSrc/javadoc/test/SomeClass.kt + | + |package example + | + |class SomeClass { + | fun someFun(x: Int): Int = 1 + |} + |/jvmSrc/javadoc/test/SomeJavaDocExample.java + | + |package example; + | + |/** + | * Here comes some comment + | * + | * {@link example.SomeClass#someFun(int) someName(ads, + | * dsa)} + | * + | * longer comment + | */ + |public class SomeJavaDocExample { + | public void someFunc(int integer, Object object) { + | } + |} + """.trimIndent(), + config, + cleanupOutput = false + ) { + documentablesMergingStage = { + it.packages.single().classlikes.single { it.name == "SomeJavaDocExample" }.documentation.values.single().children.single().children.single { + it is DocumentationLink + }.children.single().cast().body.run { + assertEquals("someName(ads, dsa)", this) + } + } + } + } + +}