From 8883839b8796589a1fa02b3dca5d1aae172b5c56 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 13 Apr 2018 19:47:07 +0300 Subject: [backport] Fix problems in as-java java-layout-html mode Extract common part of LanguageServices & other improvements Original: 853262e --- core/src/main/kotlin/Java/JavadocParser.kt | 43 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index c25f5813..db61a00a 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -20,8 +20,10 @@ interface JavaDocumentationParser { fun parseDocumentation(element: PsiNamedElement): JavadocParseResult } -class JavadocParser(private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger) : JavaDocumentationParser { +class JavadocParser( + private val refGraph: NodeReferenceGraph, + private val logger: DokkaLogger +) : JavaDocumentationParser { override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment if (docComment == null) return JavadocParseResult.Empty @@ -31,7 +33,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, result.append(para) para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) docComment.tags.forEach { tag -> - when(tag.name) { + when (tag.name) { "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content() @@ -50,9 +52,9 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, private fun PsiDocTag.contentElements(): Iterable { val tagValueElements = children - .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } - .dropWhile { it is PsiWhiteSpace } - .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } + .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } + .dropWhile { it is PsiWhiteSpace } + .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements } @@ -83,7 +85,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, } } - private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { + private fun createBlock(element: Element): ContentBlock = when (element.tagName()) { "p" -> ContentParagraph() "b", "strong" -> ContentStrong() "i", "em" -> ContentEmphasis() @@ -99,28 +101,26 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, } private fun createLink(element: Element): ContentBlock { - val docref = element.attr("docref") - if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)}) + if (element.hasAttr("docref")) { + val docref = element.attr("docref") + return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) }) } - val href = element.attr("href") - if (href != null) { - return ContentExternalLink(href) + return if (element.hasAttr("href")) { + val href = element.attr("href") + ContentExternalLink(href) } else { - return ContentBlock() + ContentBlock() } } private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() - if (linkElement == null) { - return - } + val linkElement = tag.linkElement() ?: return val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) val linkSignature = resolveLink(linkElement) val text = ContentText(linkElement.text) if (linkSignature != null) { - val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)}) + val linkNode = + ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) linkNode.append(text) seeSection.append(linkNode) } else { @@ -136,8 +136,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text val link = "${labelText.htmlEscape()}" if (tag.name == "link") "$link" else link - } - else if (valueElement != null) { + } else if (valueElement != null) { valueElement.text } else { "" @@ -153,7 +152,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph, } private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } + valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } private fun resolveLink(valueElement: PsiElement?): String? { val target = valueElement?.reference?.resolve() -- cgit From a3f16fd75c200020465f79563ca58b2833236865 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Tue, 24 Apr 2018 20:35:42 +0300 Subject: [backport] Stabilize signatures to fix linking from Java to Kotlin Basically there is 2 ways to get signature, from PSI and from descriptors, and there should be only one way in one session Original: adc09f2 --- core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt | 6 +++--- core/src/main/kotlin/Java/JavadocParser.kt | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index cf2b0514..646096e5 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -45,10 +45,10 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) { + @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger, signatureProvider: ElementSignatureProvider) { this.options = options this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger) + this.docParser = JavadocParser(refGraph, logger, signatureProvider) } constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { @@ -61,7 +61,7 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { if (skipFile(file) || file.classes.all { skipElement(it) }) { return } - val packageNode = module.findOrCreatePackageNode(file.packageName, emptyMap(), refGraph) + val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph) appendClasses(packageNode, file.classes) } diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index db61a00a..365ae298 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -22,7 +22,8 @@ interface JavaDocumentationParser { class JavadocParser( private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger + private val logger: DokkaLogger, + private val signatureProvider: ElementSignatureProvider ) : JavaDocumentationParser { override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment @@ -157,7 +158,7 @@ class JavadocParser( private fun resolveLink(valueElement: PsiElement?): String? { val target = valueElement?.reference?.resolve() if (target != null) { - return getSignature(target) + return signatureProvider.signature(target) } return null } -- cgit From 57a6bb55ddafbde4eab7c1c4344fff3ad3f0fe1f Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 27 Apr 2018 18:05:35 +0300 Subject: [backport] KT-24042: Fix structure of content paragraphs in Javadoc parser Original: edc0581 --- core/src/main/kotlin/Java/JavadocParser.kt | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 365ae298..08ae5354 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -30,9 +30,18 @@ class JavadocParser( if (docComment == null) return JavadocParseResult.Empty val result = MutableContent() var deprecatedContent: Content? = null - val para = ContentParagraph() - result.append(para) - para.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) + val firstParagraph = ContentParagraph() + firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) + val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph } + firstParagraph.children.removeAll(paragraphs) + if (!firstParagraph.isEmpty()) { + result.append(firstParagraph) + } + paragraphs.forEach { + result.append(it) + } + val attrs = mutableListOf() + var since: DocumentationNode? = null docComment.tags.forEach { tag -> when (tag.name) { "see" -> result.convertSeeTag(tag) @@ -70,20 +79,24 @@ class JavadocParser( } val doc = Jsoup.parse(htmlBuilder.toString().trim()) doc.body().childNodes().forEach { - convertHtmlNode(it) + convertHtmlNode(it)?.let { append(it) } } } - private fun ContentBlock.convertHtmlNode(node: Node) { + private fun convertHtmlNode(node: Node): ContentNode? { if (node is TextNode) { - append(ContentText(node.text())) + return ContentText(node.text()) } else if (node is Element) { val childBlock = createBlock(node) node.childNodes().forEach { - childBlock.convertHtmlNode(it) + val child = convertHtmlNode(it) + if (child != null) { + childBlock.append(child) + } } - append(childBlock) + return (childBlock) } + return null } private fun createBlock(element: Element): ContentBlock = when (element.tagName()) { -- cgit From 6cfaf04a6a0a34299d3a39803322b2eea2afbaac Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 27 Apr 2018 19:42:25 +0300 Subject: [backport] KT-24149: Fix resolution of links in Javadoc Original: edc0581 --- core/src/main/kotlin/Java/JavadocParser.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 08ae5354..0869081b 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -1,10 +1,12 @@ package org.jetbrains.dokka import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.PsiDocTag import com.intellij.psi.javadoc.PsiDocTagValue import com.intellij.psi.javadoc.PsiDocToken import com.intellij.psi.javadoc.PsiInlineDocTag +import com.intellij.psi.util.PsiTreeUtil import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -130,11 +132,11 @@ class JavadocParser( private fun MutableContent.convertSeeTag(tag: PsiDocTag) { val linkElement = tag.linkElement() ?: return val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - val linkSignature = resolveLink(linkElement) + val linkSignature = resolveLink(tag.referenceElement()) val text = ContentText(linkElement.text) if (linkSignature != null) { val linkNode = - ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) + ContentNodeLazyLink((tag.valueElement ?: linkElement).text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) linkNode.append(text) seeSection.append(linkNode) } else { @@ -144,7 +146,7 @@ class JavadocParser( private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { "link", "linkplain" -> { - val valueElement = tag.linkElement() + val valueElement = tag.referenceElement() val linkSignature = resolveLink(valueElement) if (linkSignature != null) { val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text @@ -165,6 +167,15 @@ class JavadocParser( else -> tag.text } + private fun PsiDocTag.referenceElement(): PsiElement? = + linkElement()?.let { + if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { + PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java) + } else { + it + } + } + private fun PsiDocTag.linkElement(): PsiElement? = valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } -- cgit From 432868e8732085a0b160466ddbe8262b793b4f95 Mon Sep 17 00:00:00 2001 From: Sean McQuillan Date: Thu, 26 Apr 2018 18:03:04 -0700 Subject: [backport] Support for {@inheritDoc} including grand inheritance and skip levels. Original: f100b73 --- core/src/main/kotlin/Java/JavadocParser.kt | 127 +++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 14 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 0869081b..f88f557d 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -2,11 +2,10 @@ package org.jetbrains.dokka import com.intellij.psi.* import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.javadoc.PsiDocTag -import com.intellij.psi.javadoc.PsiDocTagValue -import com.intellij.psi.javadoc.PsiDocToken -import com.intellij.psi.javadoc.PsiInlineDocTag +import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.IncorrectOperationException +import com.intellij.util.containers.isNullOrEmpty import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -33,7 +32,7 @@ class JavadocParser( val result = MutableContent() var deprecatedContent: Content? = null val firstParagraph = ContentParagraph() - firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }) + firstParagraph.convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element) val paragraphs = firstParagraph.children.dropWhile { it !is ContentParagraph } firstParagraph.children.removeAll(paragraphs) if (!firstParagraph.isEmpty()) { @@ -49,13 +48,13 @@ class JavadocParser( "see" -> result.convertSeeTag(tag) "deprecated" -> { deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements()) + deprecatedContent!!.convertJavadocElements(tag.contentElements(), element) } else -> { val subjectName = tag.getSubjectName() val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - section.convertJavadocElements(tag.contentElements()) + section.convertJavadocElements(tag.contentElements(), element) } } } @@ -70,19 +69,23 @@ class JavadocParser( return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements } - private fun ContentBlock.convertJavadocElements(elements: Iterable) { + private fun ContentBlock.convertJavadocElements(elements: Iterable, element: PsiNamedElement) { + val doc = Jsoup.parse(expandAllForElements(elements, element)) + doc.body().childNodes().forEach { + convertHtmlNode(it)?.let { append(it) } + } + } + + private fun expandAllForElements(elements: Iterable, element: PsiNamedElement): String { val htmlBuilder = StringBuilder() elements.forEach { if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it)) + htmlBuilder.append(convertInlineDocTag(it, element)) } else { htmlBuilder.append(it.text) } } - val doc = Jsoup.parse(htmlBuilder.toString().trim()) - doc.body().childNodes().forEach { - convertHtmlNode(it)?.let { append(it) } - } + return htmlBuilder.toString().trim() } private fun convertHtmlNode(node: Node): ContentNode? { @@ -144,7 +147,7 @@ class JavadocParser( } } - private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { + private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { "link", "linkplain" -> { val valueElement = tag.referenceElement() val linkSignature = resolveLink(valueElement) @@ -164,6 +167,18 @@ class JavadocParser( val escaped = text.toString().trimStart().htmlEscape() if (tag.name == "code") "$escaped" else escaped } + "inheritDoc" -> { + val result = (element as? PsiMethod)?.let { + // @{inheritDoc} is only allowed on functions + val parent = tag.parent + when (parent) { + is PsiDocComment -> element.findSuperDocCommentOrWarn() + is PsiDocTag -> element.findSuperDocTagOrWarn(parent) + else -> null + } + } + result ?: tag.text + } else -> tag.text } @@ -193,4 +208,88 @@ class JavadocParser( } return null } + + private fun PsiMethod.findSuperDocCommentOrWarn(): String { + val method = findFirstSuperMethodWithDocumentation(this) + if (method != null) { + val descriptionElements = method.docComment?.descriptionElements?.dropWhile { + it.text.trim().isEmpty() + } ?: return "" + + return expandAllForElements(descriptionElements, method) + } + logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") + return "" + } + + + private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String { + val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this) + + if (result != null) { + val (method, tag) = result + + val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() } + + val expandedString = expandAllForElements(contentElements, method) + + return expandedString + } + logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") + return "" + } + + private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? { + val superMethods = current.findSuperMethods() + for (method in superMethods) { + val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() } + if (!docs.isNullOrEmpty()) { + return method + } + } + for (method in superMethods) { + val result = findFirstSuperMethodWithDocumentation(method) + if (result != null) { + return result + } + } + + return null + } + + private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair? { + val superMethods = current.findSuperMethods() + val mappedFilteredTags = superMethods.map { + it to it.docComment?.tags?.filter { it.name == elementToExpand.name } + } + + for ((method, tags) in mappedFilteredTags) { + tags ?: continue + for (tag in tags) { + val (tagSubject, elementSubject) = when (tag.name) { + "throws" -> { + // match class names only for throws, ignore possibly fully qualified path + // TODO: Always match exactly here + tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last() + } + else -> { + tag.getSubjectName() to elementToExpand.getSubjectName() + } + } + + if (tagSubject == elementSubject) { + return method to tag + } + } + } + + for (method in superMethods) { + val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method) + if (result != null) { + return result + } + } + return null + } + } -- cgit From a16ae6c02e1ec7270fb161c63908b5b3831bce8e Mon Sep 17 00:00:00 2001 From: Sean McQuillan Date: Thu, 26 Apr 2018 18:03:04 -0700 Subject: [backport] Support for {@inheritDoc} including grand inheritance and skip levels. Original: 0eda743 --- core/src/main/kotlin/Java/JavadocParser.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index f88f557d..554cd2d3 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -1,6 +1,8 @@ package org.jetbrains.dokka import com.intellij.psi.* +import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl +import com.intellij.psi.javadoc.* import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil @@ -41,14 +43,13 @@ class JavadocParser( paragraphs.forEach { result.append(it) } - val attrs = mutableListOf() - var since: DocumentationNode? = null docComment.tags.forEach { tag -> when (tag.name) { "see" -> result.convertSeeTag(tag) "deprecated" -> { - deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements(), element) + deprecatedContent = Content().apply { + convertJavadocElements(tag.contentElements(), element) + } } else -> { val subjectName = tag.getSubjectName() -- cgit From 74b228108445a8a9024b6892f6562d77c658fc64 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Thu, 3 May 2018 18:20:33 +0300 Subject: [backport] KT-24152: Support implicit sections inheritance Original: 5636fac --- core/src/main/kotlin/Java/JavadocParser.kt | 40 +++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 554cd2d3..41b77aed 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -2,12 +2,12 @@ package org.jetbrains.dokka import com.intellij.psi.* import com.intellij.psi.impl.source.javadoc.CorePsiDocTagValueImpl -import com.intellij.psi.javadoc.* import com.intellij.psi.impl.source.tree.JavaDocElementType import com.intellij.psi.javadoc.* import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.IncorrectOperationException import com.intellij.util.containers.isNullOrEmpty +import org.jetbrains.kotlin.utils.keysToMap import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node @@ -43,6 +43,17 @@ class JavadocParser( paragraphs.forEach { result.append(it) } + + if (element is PsiMethod) { + val tagsByName = element.searchInheritedTags() + for ((tagName, tags) in tagsByName) { + for ((tag, context) in tags) { + val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName()) + section.convertJavadocElements(tag.contentElements(), context) + } + } + } + docComment.tags.forEach { tag -> when (tag.name) { "see" -> result.convertSeeTag(tag) @@ -51,6 +62,7 @@ class JavadocParser( convertJavadocElements(tag.contentElements(), element) } } + in tagsToInherit -> {} else -> { val subjectName = tag.getSubjectName() val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) @@ -62,6 +74,32 @@ class JavadocParser( return JavadocParseResult(result, deprecatedContent) } + private val tagsToInherit = setOf("param", "return", "throws") + + private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement) + + private fun PsiMethod.searchInheritedTags(): Map> { + + val output = tagsToInherit.keysToMap { mutableMapOf() } + + fun recursiveSearch(methods: Array) { + for (method in methods) { + recursiveSearch(method.findSuperMethods()) + } + for (method in methods) { + for (tag in method.docComment?.tags.orEmpty()) { + if (tag.name in tagsToInherit) { + output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method) + } + } + } + } + + recursiveSearch(arrayOf(this)) + return output.mapValues { it.value.values } + } + + private fun PsiDocTag.contentElements(): Iterable { val tagValueElements = children .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } -- cgit From a50de81d3d0ce88d2fd8e91a55b203ba49e66eb1 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 4 May 2018 00:32:40 +0300 Subject: [backport] KT-24028: Add type info to param and return section in javadoc Original: a603157 --- core/src/main/kotlin/Java/JavadocParser.kt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 41b77aed..5e23e357 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -28,6 +28,18 @@ class JavadocParser( private val logger: DokkaLogger, private val signatureProvider: ElementSignatureProvider ) : JavaDocumentationParser { + + private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { + append(LazyContentBlock { + val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) + if (node != null) { + it.append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) + it.symbol(":") + it.text(" ") + } + }) + } + override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment if (docComment == null) return JavadocParseResult.Empty @@ -49,6 +61,17 @@ class JavadocParser( for ((tagName, tags) in tagsByName) { for ((tag, context) in tags) { val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName()) + val signature = signatureProvider.signature(element) + when (tagName) { + "param" -> { + section.appendTypeElement(signature) { + it.details.find { it.kind == NodeKind.Parameter }?.detailOrNull(NodeKind.Type) + } + } + "return" -> { + section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } + } + } section.convertJavadocElements(tag.contentElements(), context) } } -- cgit From b00dabc4c53a71f745c29a135541b02f8dd7d266 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 4 May 2018 21:35:02 +0300 Subject: [backport] KT-24271: Support external link resolution in JavadocParser Original: ace3914 --- .../kotlin/Java/JavaPsiDocumentationBuilder.kt | 10 ++++- core/src/main/kotlin/Java/JavadocParser.kt | 51 ++++++++++++++++------ 2 files changed, 46 insertions(+), 15 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 646096e5..8b30a6b4 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -45,10 +45,16 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger, signatureProvider: ElementSignatureProvider) { + @Inject constructor( + options: DocumentationOptions, + refGraph: NodeReferenceGraph, + logger: DokkaLogger, + signatureProvider: ElementSignatureProvider, + externalDocumentationLinkResolver: ExternalDocumentationLinkResolver + ) { this.options = options this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger, signatureProvider) + this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) } constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 5e23e357..e1b15cb2 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -26,7 +26,8 @@ interface JavaDocumentationParser { class JavadocParser( private val refGraph: NodeReferenceGraph, private val logger: DokkaLogger, - private val signatureProvider: ElementSignatureProvider + private val signatureProvider: ElementSignatureProvider, + private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver ) : JavaDocumentationParser { private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { @@ -197,25 +198,41 @@ class JavadocParser( private fun MutableContent.convertSeeTag(tag: PsiDocTag) { val linkElement = tag.linkElement() ?: return val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - val linkSignature = resolveLink(tag.referenceElement()) + + val valueElement = tag.referenceElement() + val externalLink = resolveExternalLink(valueElement) val text = ContentText(linkElement.text) - if (linkSignature != null) { - val linkNode = - ContentNodeLazyLink((tag.valueElement ?: linkElement).text, { -> refGraph.lookupOrWarn(linkSignature, logger) }) - linkNode.append(text) - seeSection.append(linkNode) - } else { - seeSection.append(text) + + val linkSignature by lazy { resolveInternalLink(valueElement) } + val node = when { + externalLink != null -> { + val linkNode = ContentExternalLink(externalLink) + linkNode.append(text) + linkNode + } + linkSignature != null -> { + val linkNode = + ContentNodeLazyLink( + (tag.valueElement ?: linkElement).text, + { -> refGraph.lookupOrWarn(linkSignature, logger) } + ) + linkNode.append(text) + linkNode + } + else -> text } + seeSection.append(node) } private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { "link", "linkplain" -> { val valueElement = tag.referenceElement() - val linkSignature = resolveLink(valueElement) - if (linkSignature != null) { + val externalLink = resolveExternalLink(valueElement) + val linkSignature by lazy { resolveInternalLink(valueElement) } + if (externalLink != null || linkSignature != null) { val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val link = "${labelText.htmlEscape()}" + val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\"" + val link = "${labelText.htmlEscape()}" if (tag.name == "link") "$link" else link } else if (valueElement != null) { valueElement.text @@ -256,7 +273,15 @@ class JavadocParser( private fun PsiDocTag.linkElement(): PsiElement? = valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - private fun resolveLink(valueElement: PsiElement?): String? { + private fun resolveExternalLink(valueElement: PsiElement?): String? { + val target = valueElement?.reference?.resolve() + if (target != null) { + return externalDocumentationLinkResolver.buildExternalDocumentationLink(target) + } + return null + } + + private fun resolveInternalLink(valueElement: PsiElement?): String? { val target = valueElement?.reference?.resolve() if (target != null) { return signatureProvider.signature(target) -- cgit From 44ad68386658dde985207ea0aedb19c96d9b3f93 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 7 May 2018 16:37:12 +0300 Subject: [backport] KT-24277: Persist inpage anchors (aka bookmarks) from Javadoc Original: 4fddba5 --- core/src/main/kotlin/Java/JavadocParser.kt | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index e1b15cb2..8cd274e7 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -183,15 +183,19 @@ class JavadocParser( } private fun createLink(element: Element): ContentBlock { - if (element.hasAttr("docref")) { - val docref = element.attr("docref") - return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) }) - } - return if (element.hasAttr("href")) { - val href = element.attr("href") - ContentExternalLink(href) - } else { - ContentBlock() + return when { + element.hasAttr("docref") -> { + val docref = element.attr("docref") + ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger) }) + } + element.hasAttr("href") -> { + val href = element.attr("href") + ContentExternalLink(href) + } + element.hasAttr("name") -> { + ContentBookmark(element.attr("name")) + } + else -> ContentBlock() } } -- cgit From 4301503416ed45783a81250295adf4b5a86c4280 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Mon, 7 May 2018 17:28:22 +0300 Subject: [backport] KT-24299: Fix local links resolved in incorrect context Original: fababd4 --- core/src/main/kotlin/Java/JavadocParser.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 8cd274e7..9f9ea017 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -12,6 +12,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.nodes.Node import org.jsoup.nodes.TextNode +import java.net.URI data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { companion object { @@ -190,7 +191,18 @@ class JavadocParser( } element.hasAttr("href") -> { val href = element.attr("href") - ContentExternalLink(href) + + val uri = try { + URI(href) + } catch (_: Exception) { + null + } + + if (uri?.isAbsolute == false) { + ContentLocalLink(href) + } else { + ContentExternalLink(href) + } } element.hasAttr("name") -> { ContentBookmark(element.attr("name")) -- cgit From 9715d6300d6e0082a3e900c7e5cff873b8c294f2 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Sat, 26 May 2018 00:12:38 +0300 Subject: Provide signature for packages --- core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt | 1 + 1 file changed, 1 insertion(+) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 8b30a6b4..6d5825e9 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.psi.KtModifierListOwner import java.io.File fun getSignature(element: PsiElement?) = when(element) { + is PsiPackage -> element.qualifiedName is PsiClass -> element.qualifiedName is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name is PsiMethod -> -- cgit From 633fda36403e8c5483054737d285dd01b5c190bb Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Sat, 26 May 2018 00:15:47 +0300 Subject: KT-24025: Support constants in as java mode #KT-24025 fixed --- .../main/kotlin/Java/JavaPsiDocumentationBuilder.kt | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 6d5825e9..9eabc227 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -1,7 +1,9 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.* +import com.intellij.psi.impl.JavaConstantExpressionEvaluator import com.intellij.psi.util.InheritanceUtil import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration @@ -207,11 +209,28 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { fun PsiField.build(): DocumentationNode { val node = nodeForElement(this, nodeKind()) node.appendType(type) - node.appendModifiers(this) + + node.appendConstantValueIfAny(this) register(this, node) return node } + private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) { + val modifierList = field.modifierList ?: return + val initializer = field.initializer ?: return + if (field.type is PsiPrimitiveType && + modifierList.hasExplicitModifier(PsiModifier.FINAL) && + modifierList.hasExplicitModifier(PsiModifier.STATIC)) { + val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false) + val text = when(value) { + is String -> + "\"" + StringUtil.escapeStringCharacters(value) + "\"" + else -> value.toString() + } + append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail) + } + } + private fun PsiField.nodeKind(): NodeKind = when { this is PsiEnumConstant -> NodeKind.EnumItem else -> NodeKind.Field -- cgit From 0485472951134685c434d148b6fe5b6393217023 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Sat, 9 Jun 2018 23:51:43 +0300 Subject: KT-24032: Fix parameter rendering in asJava mode #KT-24032 fixed --- core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 9eabc227..332afffb 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -148,11 +148,13 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { hasSuppressDocTag(element) || skipElementBySuppressedFiles(element) - private fun skipElementByVisibility(element: Any): Boolean = element is PsiModifierListOwner && - !(options.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && - (element.hasModifierProperty(PsiModifier.PRIVATE) || - element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || - element.isInternal()) + private fun skipElementByVisibility(element: Any): Boolean = + element is PsiModifierListOwner && + element !is PsiParameter && + !(options.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && + (element.hasModifierProperty(PsiModifier.PRIVATE) || + element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || + element.isInternal()) private fun skipElementBySuppressedFiles(element: Any): Boolean = element is PsiElement && File(element.containingFile.virtualFile.path).absoluteFile in options.suppressedFiles -- cgit From 052a218dcf8bc565d5e78dc900a9647f0da5350a Mon Sep 17 00:00:00 2001 From: aleksZubakov Date: Wed, 1 Aug 2018 19:48:45 +0300 Subject: Refactoring, replace DocumentationOption with PassConfiguration --- .../kotlin/Java/JavaPsiDocumentationBuilder.kt | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'core/src/main/kotlin/Java') diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 332afffb..1fe4d180 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtModifierListOwner -import java.io.File fun getSignature(element: PsiElement?) = when(element) { is PsiPackage -> element.qualifiedName @@ -44,24 +43,24 @@ interface JavaDocumentationBuilder { } class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { - private val options: DocumentationOptions + private val passConfiguration: DokkaConfiguration.PassConfiguration private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser @Inject constructor( - options: DocumentationOptions, - refGraph: NodeReferenceGraph, - logger: DokkaLogger, - signatureProvider: ElementSignatureProvider, - externalDocumentationLinkResolver: ExternalDocumentationLinkResolver + passConfiguration: DokkaConfiguration.PassConfiguration, + refGraph: NodeReferenceGraph, + logger: DokkaLogger, + signatureProvider: ElementSignatureProvider, + externalDocumentationLinkResolver: ExternalDocumentationLinkResolver ) { - this.options = options + this.passConfiguration = passConfiguration this.refGraph = refGraph this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) } - constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { - this.options = options + constructor(passConfiguration: DokkaConfiguration.PassConfiguration, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { + this.passConfiguration = passConfiguration this.refGraph = refGraph this.docParser = docParser } @@ -141,7 +140,7 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { } } - private fun skipFile(javaFile: PsiJavaFile): Boolean = options.effectivePackageOptions(javaFile.packageName).suppress + private fun skipFile(javaFile: PsiJavaFile): Boolean = passConfiguration.effectivePackageOptions(javaFile.packageName).suppress private fun skipElement(element: Any) = skipElementByVisibility(element) || @@ -151,13 +150,13 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private fun skipElementByVisibility(element: Any): Boolean = element is PsiModifierListOwner && element !is PsiParameter && - !(options.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && + !(passConfiguration.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && (element.hasModifierProperty(PsiModifier.PRIVATE) || element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || element.isInternal()) private fun skipElementBySuppressedFiles(element: Any): Boolean = - element is PsiElement && File(element.containingFile.virtualFile.path).absoluteFile in options.suppressedFiles + element is PsiElement && element.containingFile.virtualFile.path in passConfiguration.suppressedFiles private fun PsiElement.isInternal(): Boolean { val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false -- cgit