aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main
diff options
context:
space:
mode:
authorBłażej Kardyś <bkardys@virtuslab.com>2020-07-17 03:48:03 +0200
committerSebastian Sellmair <34319766+sellmair@users.noreply.github.com>2020-08-11 14:27:44 +0200
commitaeb2014eee704be377c06205d16f60562d2a8cf1 (patch)
treed144b627a0b379bebedf7e015f6d469fea61d0bb /plugins/base/src/main
parent49c9bcc586abb7c78f569526a05fea97da86993d (diff)
downloaddokka-aeb2014eee704be377c06205d16f60562d2a8cf1.tar.gz
dokka-aeb2014eee704be377c06205d16f60562d2a8cf1.tar.bz2
dokka-aeb2014eee704be377c06205d16f60562d2a8cf1.zip
Fixing javadoc comment parser for psi files
Diffstat (limited to 'plugins/base/src/main')
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt9
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt3
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt180
3 files changed, 121 insertions, 71 deletions
diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt
index 0f953e0f..9d667623 100644
--- a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt
+++ b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt
@@ -53,6 +53,9 @@ object DocTagToContentConverter : CommentsToContentConverter {
)
)
+ fun P.collapseParagraphs(): P =
+ if (children.size == 1 && children.first() is P) (children.first() as P).collapseParagraphs() else this
+
return when (docTag) {
is H1 -> buildHeader(1)
is H2 -> buildHeader(2)
@@ -63,12 +66,14 @@ object DocTagToContentConverter : CommentsToContentConverter {
is Ul -> buildList(false)
is Ol -> buildList(true, docTag.params["start"]?.toInt() ?: 1)
is Li -> listOf(
- ContentGroup(children = buildChildren(docTag), dci, sourceSets, styles, extra)
+ ContentGroup(buildChildren(docTag), dci, sourceSets, styles, extra)
)
is Br -> buildNewLine()
is B -> buildChildren(docTag, setOf(TextStyle.Strong))
is I -> buildChildren(docTag, setOf(TextStyle.Italic))
- is P -> buildChildren(docTag, newStyles = setOf(TextStyle.Paragraph))
+ is P -> listOf(
+ ContentGroup(buildChildren(docTag.collapseParagraphs()), dci, sourceSets, styles + setOf(TextStyle.Paragraph), extra)
+ )
is A -> listOf(
ContentResolvedLink(
buildChildren(docTag),
diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
index df5d4ee1..9ed37c30 100644
--- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt
@@ -285,8 +285,7 @@ class DefaultPsiToDocumentableTranslator(
psiParameter.name,
DocumentationNode(
listOfNotNull(docs.firstChildOfTypeOrNull<Param> {
- it.firstChildOfTypeOrNull<DocumentationLink>()
- ?.firstChildOfTypeOrNull<Text>()?.body == psiParameter.name
+ it.name == psiParameter.name
})).toSourceSetDependent(),
null,
getBound(psiParameter.type),
diff --git a/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt
index 81955fde..8262b3c6 100644
--- a/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt
@@ -34,18 +34,30 @@ class JavadocParser(
docComment.getDescription()?.let { nodes.add(it) }
nodes.addAll(docComment.tags.mapNotNull { tag ->
when (tag.name) {
- "param" -> Param(P(convertJavadocElements(tag.dataElements.toList())), tag.text)
- "throws" -> Throws(P(convertJavadocElements(tag.dataElements.toList())), tag.text)
- "return" -> Return(P(convertJavadocElements(tag.dataElements.toList())))
- "author" -> Author(P(convertJavadocElements(tag.dataElements.toList())))
- "see" -> See(P(getSeeTagElementContent(tag)), tag.referenceElement()?.text.orEmpty(), null)
- "deprecated" -> Deprecated(P(convertJavadocElements(tag.dataElements.toList())))
+ "param" -> Param(
+ wrapTagIfNecessary(convertJavadocElements(tag.contentElements())),
+ tag.children.firstIsInstanceOrNull<PsiDocParamRef>()?.text.orEmpty()
+ )
+ "throws" -> Throws(wrapTagIfNecessary(convertJavadocElements(tag.contentElements())), tag.text)
+ "return" -> Return(wrapTagIfNecessary(convertJavadocElements(tag.contentElements())))
+ "author" -> Author(wrapTagIfNecessary(convertJavadocElements(tag.contentElements())))
+ "see" -> getSeeTagElementContent(tag).let {
+ See(
+ wrapTagIfNecessary(it.first),
+ tag.referenceElement()?.text.orEmpty(),
+ it.second
+ )
+ }
+ "deprecated" -> Deprecated(wrapTagIfNecessary(convertJavadocElements(tag.dataElements.toList())))
else -> null
}
})
return DocumentationNode(nodes)
}
+ private fun wrapTagIfNecessary(list: List<DocTag>): DocTag =
+ if (list.size == 1) list.first() else P(list)
+
private fun findClosestDocComment(element: PsiNamedElement): PsiDocComment? {
(element as? PsiDocCommentOwner)?.docComment?.run { return this }
if (element is PsiMethod) {
@@ -119,88 +131,118 @@ class JavadocParser(
}
}
- private fun getSeeTagElementContent(tag: PsiDocTag): List<DocTag> =
- listOfNotNull(tag.referenceElement()?.toDocumentationLink())
+ private fun getSeeTagElementContent(tag: PsiDocTag): Pair<List<DocumentationLink>, DRI?> {
+ val content = tag.referenceElement()?.toDocumentationLink()
+ return Pair(listOfNotNull(content), content?.dri)
+ }
private fun PsiDocComment.getDescription(): Description? {
- val nonEmptyDescriptionElements = descriptionElements.filter { it.text.trim().isNotEmpty() }
- val convertedDescriptionElements = convertJavadocElements(nonEmptyDescriptionElements)
- if (convertedDescriptionElements.isNotEmpty()) {
- return Description(P(convertedDescriptionElements))
+ val nonEmptyDescriptionElements = descriptionElements.filter { it.text.isNotBlank() }
+ return convertJavadocElements(nonEmptyDescriptionElements).takeIf { it.isNotEmpty() }?.let {
+ Description(wrapTagIfNecessary(it))
}
-
- return null
}
- private fun convertJavadocElements(elements: Iterable<PsiElement>): List<DocTag> =
- elements.mapNotNull {
- when (it) {
- is PsiReference -> convertJavadocElements(it.children.toList())
- is PsiInlineDocTag -> listOfNotNull(convertInlineDocTag(it))
- is PsiDocParamRef -> listOfNotNull(it.toDocumentationLink())
- is PsiDocTagValue,
- is LeafPsiElement -> Jsoup.parse(it.text.trim()).body().childNodes().mapNotNull(::convertHtmlNode)
- else -> null
+ private inner class Parse : (Iterable<PsiElement>, Boolean) -> List<DocTag> {
+ val driMap = mutableMapOf<String, DRI>()
+
+ private fun PsiElement.stringify(): String? = when (this) {
+ is PsiReference -> children.joinToString("") { it.stringify().orEmpty() }
+ is PsiInlineDocTag -> convertInlineDocTag(this)
+ is PsiDocParamRef -> toDocumentationLinkString()
+ is PsiDocTagValue,
+ is LeafPsiElement -> (if ((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true) text?.trim() else text)?.takeUnless { it.isBlank() }
+ else -> null
+ }
+
+ 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()
+ """<a data-dri=$dri>${label.text}</a>"""
+ } else null
}
- }.flatten()
- private fun convertHtmlNode(node: Node, insidePre: Boolean = false): DocTag? = when (node) {
- is TextNode -> Text(body = if (insidePre) node.wholeText else node.text())
- is Element -> createBlock(node)
- else -> null
- }
+ private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) {
+ "link", "linkplain" -> {
+ tag.referenceElement()?.toDocumentationLinkString(tag.dataElements.firstIsInstanceOrNull<PsiDocToken>())
+ }
+ "code", "literal" -> {
+ "<code data-inline>${tag.text}</code>"
+ }
+ "index" -> "<index>${tag.children.filterIsInstance<PsiDocTagValue>().joinToString { it.text }}</index>"
+ else -> tag.text
+ }
- private fun createBlock(element: Element): DocTag {
- val children = element.childNodes().mapNotNull { convertHtmlNode(it) }
- return when (element.tagName()) {
- "p" -> P(listOf(Br, Br) + children)
- "b" -> B(children)
- "strong" -> Strong(children)
- "i" -> I(children)
- "em" -> Em(children)
- "code" -> CodeBlock(children)
- "pre" -> Pre(children)
- "ul" -> Ul(children)
- "ol" -> Ol(children)
- "li" -> Li(children)
- "a" -> createLink(element, children)
- else -> Text(body = element.ownText())
+ private fun createLink(element: Element, children: List<DocTag>): DocTag {
+ return when {
+ element.hasAttr("docref") ->
+ A(children, params = mapOf("docref" to element.attr("docref")))
+ element.hasAttr("href") ->
+ 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)
+ }
}
- }
- private fun createLink(element: Element, children: List<DocTag>): DocTag {
- return when {
- element.hasAttr("docref") -> {
- A(children, params = mapOf("docref" to element.attr("docref")))
+ private fun createBlock(element: Element): DocTag? {
+ val children = element.childNodes().mapNotNull { convertHtmlNode(it) }
+ fun ifChildrenPresent(operation: () -> DocTag): DocTag? {
+ return if (children.isNotEmpty()) operation() else null
}
- element.hasAttr("href") -> {
- A(children, params = mapOf("href" to element.attr("href")))
+ return when (element.tagName()) {
+ "blockquote" -> ifChildrenPresent { BlockQuote(children) }
+ "p" -> ifChildrenPresent { P(children) }
+ "b" -> ifChildrenPresent { B(children) }
+ "strong" -> ifChildrenPresent { Strong(children) }
+ "index" -> Index(children)
+ "i" -> ifChildrenPresent { I(children) }
+ "em" -> Em(children)
+ "code" -> ifChildrenPresent {
+ if (element.hasAttr("data-inline")) CodeInline(children) else CodeBlock(
+ children
+ )
+ }
+ "pre" -> Pre(children)
+ "ul" -> ifChildrenPresent { Ul(children) }
+ "ol" -> ifChildrenPresent { Ol(children) }
+ "li" -> Li(children)
+ "a" -> createLink(element, children)
+ else -> Text(body = element.ownText())
}
- else -> Text(children = children)
}
+
+ private fun convertHtmlNode(node: Node, insidePre: Boolean = false): DocTag? = when (node) {
+ is TextNode -> Text(body = if (insidePre) node.wholeText else node.text())
+ is Element -> createBlock(node)
+ else -> null
+ }
+
+ override fun invoke(elements: Iterable<PsiElement>, asParagraph: Boolean): List<DocTag> =
+ Jsoup.parseBodyFragment(elements.mapNotNull { it.stringify() }.joinToString(" ", prefix = if (asParagraph) "<p>" else ""))
+ .body().childNodes().mapNotNull { convertHtmlNode(it) }
}
+ private fun PsiDocTag.contentElements(): List<PsiElement> =
+ dataElements.mapNotNull { it.takeIf { it.text.isNotBlank() } }
+
+ private fun convertJavadocElements(elements: Iterable<PsiElement>, asParagraph: Boolean = true): List<DocTag> = Parse()(elements, asParagraph)
+
private fun PsiDocToken.isSharpToken() = tokenType.toString() == "DOC_TAG_VALUE_SHARP_TOKEN"
+ private fun PsiDocToken.isLeadingAsterisk() = tokenType.toString() == "DOC_COMMENT_LEADING_ASTERISKS"
+
private fun PsiElement.toDocumentationLink(labelElement: PsiElement? = null) =
reference?.resolve()?.let {
val dri = DRI.from(it)
- val label = labelElement ?: children.firstOrNull {
- it is PsiDocToken && it.text.isNotBlank() && !it.isSharpToken()
- } ?: this
- DocumentationLink(dri, convertJavadocElements(listOfNotNull(label)))
- }
-
- private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) {
- "link", "linkplain" -> {
- tag.referenceElement()?.toDocumentationLink(tag.dataElements.firstIsInstanceOrNull<PsiDocToken>())
+ val label = labelElement ?: defaultLabel()
+ DocumentationLink(dri, convertJavadocElements(listOfNotNull(label), asParagraph = false))
}
- "code", "literal" -> {
- CodeInline(listOf(Text(tag.text)))
- }
- "index" -> Index(tag.children.filterIsInstance<PsiDocTagValue>().map { Text(it.text) })
- else -> Text(tag.text)
- }
private fun PsiDocTag.referenceElement(): PsiElement? =
linkElement()?.let {
@@ -211,6 +253,10 @@ class JavadocParser(
}
}
+ private fun PsiElement.defaultLabel() = children.firstOrNull {
+ it is PsiDocToken && it.text.isNotBlank() && !it.isSharpToken()
+ } ?: this
+
private fun PsiDocTag.linkElement(): PsiElement? =
valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace }
}