aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt85
-rw-r--r--plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt62
2 files changed, 106 insertions, 41 deletions
diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
index ce022dd7..9cb362cb 100644
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
@@ -8,12 +8,10 @@ import com.intellij.psi.impl.source.tree.LazyParseablePsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.javadoc.*
import org.intellij.markdown.MarkdownElementTypes
-import org.intellij.markdown.lexer.Compat.forEachCodePoint
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
import org.jetbrains.dokka.analysis.from
import org.jetbrains.dokka.base.parsers.MarkdownParser
import org.jetbrains.dokka.base.translators.parseHtmlEncodedWithNormalisedSpaces
-import org.jetbrains.dokka.base.translators.parseWithNormalisedSpaces
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.utilities.DokkaLogger
@@ -24,12 +22,11 @@ import org.jetbrains.kotlin.idea.util.CommentSaver.Companion.tokenType
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jsoup.Jsoup
-import org.jsoup.internal.StringUtil
import org.jsoup.nodes.Element
-import org.jsoup.nodes.Entities
import org.jsoup.nodes.Node
import org.jsoup.nodes.TextNode
import java.util.*
+import org.jetbrains.dokka.utilities.htmlEscape
interface JavaDocumentationParser {
fun parseDocumentation(element: PsiNamedElement): DocumentationNode
@@ -251,32 +248,7 @@ class JavadocParser(
is PsiInlineDocTag -> convertInlineDocTag(this, state.currentJavadocTag, context)
is PsiDocParamRef -> toDocumentationLinkString()
is PsiDocTagValue,
- is LeafPsiElement -> {
- if (isInsidePre) {
- /*
- For values in the <pre> tag we try to keep formatting, so only the leading space is trimmed,
- since it is there because it separates this line from the leading asterisk
- */
- text.let {
- if ((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true && it.firstOrNull() == ' ')
- it.drop(1) else it
- }.let {
- if ((nextSibling as? PsiDocToken)?.isLeadingAsterisk() == true) it.dropLastWhile { it == ' ' } else it
- }
- } else {
- /*
- Outside of the <pre> we would like to trim everything from the start and end of a line since
- javadoc doesn't care about it.
- */
- text.let {
- if ((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true && text.isNotBlank() && state.previousElement !is PsiInlineDocTag) it?.trimStart() else it
- }?.let {
- if ((nextSibling as? PsiDocToken)?.isLeadingAsterisk() == true && text.isNotBlank()) it.trimEnd() else it
- }?.let {
- if (shouldHaveSpaceAtTheEnd()) "$it " else it
- }
- }
- }
+ is LeafPsiElement -> stringifyElementAsText(isInsidePre, state.previousElement)
else -> null
}
val previousElement = if (text.trim() == "") state.previousElement else this
@@ -289,6 +261,31 @@ class JavadocParser(
)
}
+ private fun PsiElement.stringifyElementAsText(keepFormatting: Boolean, previousElement: PsiElement? = null) = if (keepFormatting) {
+ /*
+ For values in the <pre> tag we try to keep formatting, so only the leading space is trimmed,
+ since it is there because it separates this line from the leading asterisk
+ */
+ text.let {
+ if (((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true || (prevSibling as? PsiDocToken)?.isTagName() == true ) && it.firstOrNull() == ' ')
+ it.drop(1) else it
+ }.let {
+ if ((nextSibling as? PsiDocToken)?.isLeadingAsterisk() == true) it.dropLastWhile { it == ' ' } else it
+ }
+ } else {
+ /*
+ Outside of the <pre> we would like to trim everything from the start and end of a line since
+ javadoc doesn't care about it.
+ */
+ text.let {
+ if ((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true && text.isNotBlank() && previousElement !is PsiInlineDocTag) it?.trimStart() else it
+ }?.let {
+ if ((nextSibling as? PsiDocToken)?.isLeadingAsterisk() == true && text.isNotBlank()) it.trimEnd() else it
+ }?.let {
+ if (shouldHaveSpaceAtTheEnd()) "$it " else it
+ }
+ }
+
/**
* We would like to know if we need to have a space after a this tag
*
@@ -338,7 +335,8 @@ class JavadocParser(
when (tag.name) {
"link", "linkplain" -> tag.referenceElement()
?.toDocumentationLinkString(tag.dataElements.filterIsInstance<PsiDocToken>())
- "code", "literal" -> "<code data-inline>${tag.text}</code>"
+ "code", "literal" -> "<code data-inline>${tag.dataElements.joinToString("") { it.stringifyElementAsText(keepFormatting = true)
+ .toString() }.htmlEscape()}</code>"
"index" -> "<index>${tag.children.filterIsInstance<PsiDocTagValue>().joinToString { it.text }}</index>"
"inheritDoc" -> inheritDocResolver.resolveFromContext(context)
?.fold(ParsingResult(javadocTag)) { result, e ->
@@ -359,14 +357,15 @@ class JavadocParser(
}
}
- private fun createBlock(element: Element, insidePre: Boolean = false): List<DocTag> {
+ private fun createBlock(element: Element, keepFormatting: Boolean = false): List<DocTag> {
+ val tagName = element.tagName()
val children = element.childNodes()
- .flatMap { convertHtmlNode(it, insidePre = insidePre || element.tagName() == "pre") }
+ .flatMap { convertHtmlNode(it, keepFormatting = keepFormatting || tagName == "pre" || tagName == "code") }
fun ifChildrenPresent(operation: () -> DocTag): List<DocTag> {
return if (children.isNotEmpty()) listOf(operation()) else emptyList()
}
- return when (element.tagName()) {
+ return when (tagName) {
"blockquote" -> ifChildrenPresent { BlockQuote(children) }
"p" -> ifChildrenPresent { P(children) }
"b" -> ifChildrenPresent { B(children) }
@@ -374,9 +373,13 @@ class JavadocParser(
"index" -> listOf(Index(children))
"i" -> ifChildrenPresent { I(children) }
"em" -> listOf(Em(children))
- "code" -> ifChildrenPresent { if(insidePre) CodeBlock(children) else CodeInline(children) }
- "pre" -> if(children.size == 1 && children.first() is CodeInline) {
- listOf(CodeBlock(children.first().children))
+ "code" -> ifChildrenPresent { if(keepFormatting) CodeBlock(children) else CodeInline(children) }
+ "pre" -> if(children.size == 1) {
+ when(children.first()) {
+ is CodeInline -> listOf(CodeBlock(children.first().children))
+ is CodeBlock -> listOf(children.first())
+ else -> listOf(Pre(children))
+ }
} else {
listOf(Pre(children))
}
@@ -405,13 +408,13 @@ class JavadocParser(
}
}
- private fun convertHtmlNode(node: Node, insidePre: Boolean = false): List<DocTag> = when (node) {
- is TextNode -> (if (insidePre) {
+ private fun convertHtmlNode(node: Node, keepFormatting: Boolean = false): List<DocTag> = when (node) {
+ is TextNode -> (if (keepFormatting) {
node.wholeText.takeIf { it.isNotBlank() }?.let { listOf(Text(body = it)) }
} else {
node.wholeText.parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces = true)
}).orEmpty()
- is Element -> createBlock(node)
+ is Element -> createBlock(node, keepFormatting)
else -> emptyList()
}
@@ -438,6 +441,8 @@ class JavadocParser(
private fun PsiDocToken.isSharpToken() = tokenType == JavaDocTokenType.DOC_TAG_VALUE_SHARP_TOKEN
+ private fun PsiDocToken.isTagName() = tokenType == JavaDocTokenType.DOC_TAG_NAME
+
private fun PsiDocToken.isLeadingAsterisk() = tokenType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS
private fun PsiElement.toDocumentationLink(labelElement: PsiElement? = null, context: CommentResolutionContext) =
diff --git a/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt b/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
index a6a1413c..d6fffee3 100644
--- a/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
+++ b/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
@@ -1,14 +1,27 @@
package parsers
+import com.jetbrains.rd.util.first
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.model.DEnum
import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.model.doc.CodeBlock
+import org.jetbrains.dokka.model.doc.CodeInline
+import org.jetbrains.dokka.model.doc.Text
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
-import utils.text
+import utils.*
class JavadocParserTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
private fun performJavadocTest(testOperation: (DModule) -> Unit) {
val configuration = dokkaConfiguration {
sourceSets {
@@ -47,4 +60,51 @@ class JavadocParserTest : BaseAbstractTest() {
assertEquals("content being refreshed, which can be a result of invalidation, refresh that may contain content updates, or the initial load.", docs.trimEnd())
}
}
+
+ @Test
+ fun `code tag`() {
+ val source = """
+ |/src/main/kotlin/test/Test.java
+ |package example
+ |
+ | /**
+ | * Identifies calls to {@code assertThat}.
+ | *
+ | * {@code
+ | * Set<String> s;
+ | * System.out.println("s1 = " + s);
+ | * }
+ | * <pre>{@code
+ | * Set<String> s2;
+ | * System.out
+ | * .println("s2 = " + s2);
+ | * }</pre>
+ | *
+ | */
+ | public class Test {}
+ """.trimIndent()
+ testInline(
+ source,
+ configuration,
+ ) {
+ documentablesCreationStage = { modules ->
+ val docs = modules.first().packages.first().classlikes.single().documentation.first().value
+ val root = docs.children.first().root
+
+ kotlin.test.assertEquals(
+ listOf(
+ Text(body = "Identifies calls to "),
+ CodeInline(children = listOf(Text(body = "assertThat"))),
+ Text(body = ". "),
+ CodeInline(children = listOf(Text(body = "\nSet<String> s;\nSystem.out.println(\"s1 = \" + s);\n")))
+ ),
+ root.children[0].children
+ )
+ kotlin.test.assertEquals(
+ CodeBlock(children = listOf(Text(body = "\nSet<String> s2;\nSystem.out\n .println(\"s2 = \" + s2);\n"))),
+ root.children[1]
+ )
+ }
+ }
+ }
}