diff options
Diffstat (limited to 'src/Java')
-rw-r--r-- | src/Java/JavaPsiDocumentationBuilder.kt | 170 | ||||
-rw-r--r-- | src/Java/JavadocParser.kt | 170 |
2 files changed, 171 insertions, 169 deletions
diff --git a/src/Java/JavaPsiDocumentationBuilder.kt b/src/Java/JavaPsiDocumentationBuilder.kt index bdc6a368..b1e3167d 100644 --- a/src/Java/JavaPsiDocumentationBuilder.kt +++ b/src/Java/JavaPsiDocumentationBuilder.kt @@ -1,177 +1,9 @@ package org.jetbrains.dokka import com.intellij.psi.* -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 org.jetbrains.dokka.DocumentationNode.Kind -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode - -data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { - companion object { - val Empty = JavadocParseResult(Content.Empty, null) - } -} - -interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): JavadocParseResult -} - -class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser { - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment - 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() }) - docComment.tags.forEach { tag -> - when(tag.name) { - "see" -> result.convertSeeTag(tag) - "deprecated" -> { - deprecatedContent = Content() - deprecatedContent!!.convertJavadocElements(tag.contentElements()) - } - else -> { - val subjectName = tag.getSubjectName() - val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - - section.convertJavadocElements(tag.contentElements()) - } - } - } - return JavadocParseResult(result, deprecatedContent) - } - - private fun PsiDocTag.contentElements(): Iterable<PsiElement> { - val tagValueElements = children - .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 - } - - private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>) { - val htmlBuilder = StringBuilder() - elements.forEach { - if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it)) - } else { - htmlBuilder.append(it.text) - } - } - val doc = Jsoup.parse(htmlBuilder.toString().trimStart()) - doc.body().childNodes().forEach { - convertHtmlNode(it) - } - } - - private fun ContentBlock.convertHtmlNode(node: Node) { - if (node is TextNode) { - append(ContentText(node.text())) - } else if (node is Element) { - val childBlock = createBlock(node) - node.childNodes().forEach { - childBlock.convertHtmlNode(it) - } - append(childBlock) - } - } - - private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { - "p" -> ContentParagraph() - "b", "strong" -> ContentStrong() - "i", "em" -> ContentEmphasis() - "s", "del" -> ContentStrikethrough() - "code" -> ContentCode() - "pre" -> ContentBlockCode() - "ul" -> ContentUnorderedList() - "ol" -> ContentOrderedList() - "li" -> ContentListItem() - "a" -> createLink(element) - else -> ContentBlock() - } - - private fun createLink(element: Element): ContentBlock { - val docref = element.attr("docref") - if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) - } - val href = element.attr("href") - if (href != null) { - return ContentExternalLink(href) - } else { - return ContentBlock() - } - } - - private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() - if (linkElement == null) { - 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.lookup(linkSignature)}) - linkNode.append(text) - seeSection.append(linkNode) - } else { - seeSection.append(text) - } - } - - private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { - "link", "linkplain" -> { - val valueElement = tag.linkElement() - val linkSignature = resolveLink(valueElement) - if (linkSignature != null) { - val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val link = "<a docref=\"$linkSignature\">${labelText.htmlEscape()}</a>" - if (tag.name == "link") "<code>$link</code>" else link - } - else if (valueElement != null) { - valueElement.text - } else { - "" - } - } - "code", "literal" -> { - val text = StringBuilder() - tag.dataElements.forEach { text.append(it.text) } - val escaped = text.toString().trimStart().htmlEscape() - if (tag.name == "code") "<code>$escaped</code>" else escaped - } - else -> tag.text - } - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - private fun resolveLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return getSignature(target) - } - return null - } - - fun PsiDocTag.getSubjectName(): String? { - if (name == "param" || name == "throws" || name == "exception") { - return valueElement?.text - } - return null - } -} - -private fun getSignature(element: PsiElement?) = when(element) { +fun getSignature(element: PsiElement?) = when(element) { is PsiClass -> element.qualifiedName is PsiField -> element.containingClass!!.qualifiedName + "#" + element.name is PsiMethod -> diff --git a/src/Java/JavadocParser.kt b/src/Java/JavadocParser.kt new file mode 100644 index 00000000..1378a5a7 --- /dev/null +++ b/src/Java/JavadocParser.kt @@ -0,0 +1,170 @@ +package org.jetbrains.dokka + +import com.intellij.psi.* +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 org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode + +data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { + companion object { + val Empty = JavadocParseResult(Content.Empty, null) + } +} + +interface JavaDocumentationParser { + fun parseDocumentation(element: PsiNamedElement): JavadocParseResult +} + +class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser { + override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { + val docComment = (element as? PsiDocCommentOwner)?.docComment + 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() }) + docComment.tags.forEach { tag -> + when(tag.name) { + "see" -> result.convertSeeTag(tag) + "deprecated" -> { + deprecatedContent = Content() + deprecatedContent!!.convertJavadocElements(tag.contentElements()) + } + else -> { + val subjectName = tag.getSubjectName() + val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) + + section.convertJavadocElements(tag.contentElements()) + } + } + } + return JavadocParseResult(result, deprecatedContent) + } + + private fun PsiDocTag.contentElements(): Iterable<PsiElement> { + val tagValueElements = children + .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 + } + + private fun ContentBlock.convertJavadocElements(elements: Iterable<PsiElement>) { + val htmlBuilder = StringBuilder() + elements.forEach { + if (it is PsiInlineDocTag) { + htmlBuilder.append(convertInlineDocTag(it)) + } else { + htmlBuilder.append(it.text) + } + } + val doc = Jsoup.parse(htmlBuilder.toString().trimStart()) + doc.body().childNodes().forEach { + convertHtmlNode(it) + } + } + + private fun ContentBlock.convertHtmlNode(node: Node) { + if (node is TextNode) { + append(ContentText(node.text())) + } else if (node is Element) { + val childBlock = createBlock(node) + node.childNodes().forEach { + childBlock.convertHtmlNode(it) + } + append(childBlock) + } + } + + private fun createBlock(element: Element): ContentBlock = when(element.tagName()) { + "p" -> ContentParagraph() + "b", "strong" -> ContentStrong() + "i", "em" -> ContentEmphasis() + "s", "del" -> ContentStrikethrough() + "code" -> ContentCode() + "pre" -> ContentBlockCode() + "ul" -> ContentUnorderedList() + "ol" -> ContentOrderedList() + "li" -> ContentListItem() + "a" -> createLink(element) + else -> ContentBlock() + } + + private fun createLink(element: Element): ContentBlock { + val docref = element.attr("docref") + if (docref != null) { + return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) + } + val href = element.attr("href") + if (href != null) { + return ContentExternalLink(href) + } else { + return ContentBlock() + } + } + + private fun MutableContent.convertSeeTag(tag: PsiDocTag) { + val linkElement = tag.linkElement() + if (linkElement == null) { + 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.lookup(linkSignature)}) + linkNode.append(text) + seeSection.append(linkNode) + } else { + seeSection.append(text) + } + } + + private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { + "link", "linkplain" -> { + val valueElement = tag.linkElement() + val linkSignature = resolveLink(valueElement) + if (linkSignature != null) { + val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text + val link = "<a docref=\"$linkSignature\">${labelText.htmlEscape()}</a>" + if (tag.name == "link") "<code>$link</code>" else link + } + else if (valueElement != null) { + valueElement.text + } else { + "" + } + } + "code", "literal" -> { + val text = StringBuilder() + tag.dataElements.forEach { text.append(it.text) } + val escaped = text.toString().trimStart().htmlEscape() + if (tag.name == "code") "<code>$escaped</code>" else escaped + } + else -> tag.text + } + + private fun PsiDocTag.linkElement(): PsiElement? = + valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } + + private fun resolveLink(valueElement: PsiElement?): String? { + val target = valueElement?.reference?.resolve() + if (target != null) { + return getSignature(target) + } + return null + } + + fun PsiDocTag.getSubjectName(): String? { + if (name == "param" || name == "throws" || name == "exception") { + return valueElement?.text + } + return null + } +} |