diff options
| author | Błażej Kardyś <bkardys@virtuslab.com> | 2020-07-17 03:48:03 +0200 |
|---|---|---|
| committer | Sebastian Sellmair <34319766+sellmair@users.noreply.github.com> | 2020-08-11 14:27:44 +0200 |
| commit | aeb2014eee704be377c06205d16f60562d2a8cf1 (patch) | |
| tree | d144b627a0b379bebedf7e015f6d469fea61d0bb /plugins | |
| parent | 49c9bcc586abb7c78f569526a05fea97da86993d (diff) | |
| download | dokka-aeb2014eee704be377c06205d16f60562d2a8cf1.tar.gz dokka-aeb2014eee704be377c06205d16f60562d2a8cf1.tar.bz2 dokka-aeb2014eee704be377c06205d16f60562d2a8cf1.zip | |
Fixing javadoc comment parser for psi files
Diffstat (limited to 'plugins')
18 files changed, 531 insertions, 234 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 } } diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt index a9689bc5..4ac5717d 100644 --- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt +++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt @@ -86,7 +86,7 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") + group { pWrapped("comment to function") } } divergent { bareSignature( @@ -131,8 +131,8 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - unnamedTag("Author") { +"Kordyjan" } - unnamedTag("Since") { +"0.11" } + unnamedTag("Author") { group { +"Kordyjan" } } + unnamedTag("Since") { group { +"0.11" } } } divergent { bareSignature( @@ -178,9 +178,9 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") - unnamedTag("Author") { +"Kordyjan" } - unnamedTag("Since") { +"0.11" } + group { pWrapped("comment to function") } + unnamedTag("Author") { group { +"Kordyjan" } } + unnamedTag("Since") { group { +"0.11" } } } divergent { bareSignature( @@ -225,14 +225,14 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") + group { pWrapped("comment to function") } header(2) { +"Parameters" } group { platformHinted { table { group { +"abc" - group { +"comment to param" } + group { group { +"comment to param" } } } } } @@ -283,22 +283,22 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") + group { group { group { +"comment to function" } } } header(2) { +"Parameters" } group { platformHinted { table { group { +"first" - group { +"comment to first param" } + group { group { +"comment to first param" } } } group { +"second" - group { +"comment to second param" } + group { group { +"comment to second param" } } } group { +"third" - group { +"comment to third param" } + group { group { +"comment to third param" } } } } } @@ -351,15 +351,15 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"first" - group { +"comment to first param" } + group { group { +"comment to first param" } } } group { +"second" - group { +"comment to second param" } + group { group { +"comment to second param" } } } group { +"third" - group { +"comment to third param" } + group { group { +"comment to third param" } } } } } @@ -406,18 +406,18 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") + group { pWrapped("comment to function") } header(2) { +"Parameters" } group { platformHinted { table { group { +"<receiver>" - group { +"comment to receiver" } + group { group { +"comment to receiver" } } } group { +"abc" - group { +"comment to param" } + group { group { +"comment to param" } } } } } @@ -468,18 +468,18 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") + group { group { group { +"comment to function" } } } header(2) { +"Parameters" } group { platformHinted { table { group { +"first" - group { +"comment to first param" } + group { group { +"comment to first param" } } } group { +"third" - group { +"comment to third param" } + group { group { +"comment to third param" } } } } } @@ -529,9 +529,9 @@ class ContentForParamsTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("comment to function") - unnamedTag("Author") { +"Kordyjan" } - unnamedTag("Since") { +"0.11" } + group { pWrapped("comment to function") } + unnamedTag("Author") { group { +"Kordyjan" } } + unnamedTag("Since") { group { +"0.11" } } header(2) { +"Parameters" } group { @@ -539,15 +539,15 @@ class ContentForParamsTest : AbstractCoreTest() { table { group { +"first" - group { +"comment to first param" } + group { group { +"comment to first param" } } } group { +"second" - group { +"comment to second param" } + group { group { +"comment to second param" } } } group { +"third" - group { +"comment to third param" } + group { group { +"comment to third param" } } } } } diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt index 24970660..fd51c895 100644 --- a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt +++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt @@ -89,7 +89,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "test//abc/#/-1/" link { +"abc" } - group { } + group { group { } } } } } @@ -144,7 +144,9 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "test//abc/#/-1/" link { +"abc" } - group { +"Comment to abc" } + group { + group { +"Comment to abc" } + } } } } @@ -199,7 +201,9 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "kotlin.collections/Collection////" link { +"Collection" } - group { } + group { + group { } + } } } } @@ -254,7 +258,9 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "test//abc/#/-1/" link { +"Collection" } - group { +"Comment to stdliblink" } + group { + group { +"Comment to stdliblink" } + } } } } @@ -305,9 +311,9 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { divergentGroup { divergentInstance { before { - pWrapped("random comment") - unnamedTag("Author") { +"pikinier20" } - unnamedTag("Since") { +"0.11" } + group { group { group { +"random comment"} } } + unnamedTag("Author") { group { +"pikinier20" } } + unnamedTag("Since") { group { +"0.11" } } header(2) { +"See also" } group { @@ -316,7 +322,9 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "test//abc/#/-1/" link { +"Collection" } - group { +"Comment to stdliblink" } + group { + group { +"Comment to stdliblink" } + } } } } @@ -372,7 +380,9 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "test//abc/#/-1/" link { +"abc" } - group { +"Comment to abc2" } + group { + group { +"Comment to abc2" } + } } } } @@ -428,12 +438,14 @@ class ContentForSeeAlsoTest : AbstractCoreTest() { group { //DRI should be "test//abc/#/-1/" link { +"abc" } - group { +"Comment to abc1" } + group { + group { +"Comment to abc1" } + } } group { //DRI should be "test//abc/#/-1/" link { +"Collection" } - group { +"Comment to collection" } + group { group { +"Comment to collection" } } } } } diff --git a/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt b/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt index 90a38055..b3da3f71 100644 --- a/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt +++ b/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt @@ -230,7 +230,7 @@ class ConstructorsSignaturesTest : AbstractCoreTest() { platformHinted { group { group { - +"ctor comment" + group { +"ctor comment" } } } group { diff --git a/plugins/base/src/test/kotlin/enums/EnumsTest.kt b/plugins/base/src/test/kotlin/enums/EnumsTest.kt index 6a973f8e..9cd41dcd 100644 --- a/plugins/base/src/test/kotlin/enums/EnumsTest.kt +++ b/plugins/base/src/test/kotlin/enums/EnumsTest.kt @@ -210,7 +210,9 @@ class EnumsTest : AbstractCoreTest() { platformHinted { group { group { - + "Sample docs for E1" + group { + +"Sample docs for E1" + } } } group { diff --git a/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt index 5197afc6..ad023d84 100644 --- a/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt +++ b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt @@ -37,7 +37,7 @@ class CommentsToContentConverterTest { fun `simple text`() { val docTag = P(listOf(Text("This is simple test of string Next line"))) executeTest(docTag) { |
