aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin/translators/psi/parsers
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2023-07-05 10:04:55 +0200
committerGitHub <noreply@github.com>2023-07-05 10:04:55 +0200
commit9559158bfeeb274e9ccf1b4563f1b23b42afc493 (patch)
tree3ece0887623cfe2b7148af23001867a1dd5e6597 /plugins/base/src/main/kotlin/translators/psi/parsers
parentcbd9733d3dd2f52992e98e7cebd072091a572529 (diff)
downloaddokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.gz
dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.bz2
dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.zip
Decompose Kotlin/Java analysis (#3034)
* Extract analysis into separate modules
Diffstat (limited to 'plugins/base/src/main/kotlin/translators/psi/parsers')
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt129
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt511
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt32
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt146
-rw-r--r--plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt14
5 files changed, 0 insertions, 832 deletions
diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt
index e7f8c9ec..e69de29b 100644
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt
@@ -1,129 +0,0 @@
-package org.jetbrains.dokka.base.translators.psi.parsers
-
-import com.intellij.psi.PsiClass
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiMethod
-import com.intellij.psi.javadoc.PsiDocComment
-import com.intellij.psi.javadoc.PsiDocTag
-import org.jetbrains.dokka.utilities.DokkaLogger
-
-internal data class CommentResolutionContext(
- val comment: PsiDocComment,
- val tag: JavadocTag?,
- val name: String? = null,
- val parameterIndex: Int? = null,
-)
-
-internal class InheritDocResolver(
- private val logger: DokkaLogger
-) {
- internal fun resolveFromContext(context: CommentResolutionContext) =
- when (context.tag) {
- JavadocTag.THROWS, JavadocTag.EXCEPTION -> context.name?.let { name ->
- resolveThrowsTag(
- context.tag,
- context.comment,
- name
- )
- }
- JavadocTag.PARAM -> context.parameterIndex?.let { paramIndex ->
- resolveParamTag(
- context.comment,
- paramIndex
- )
- }
- JavadocTag.DEPRECATED -> resolveGenericTag(context.comment, JavadocTag.DESCRIPTION)
- JavadocTag.SEE -> emptyList()
- else -> context.tag?.let { tag -> resolveGenericTag(context.comment, tag) }
- }
-
- private fun resolveGenericTag(currentElement: PsiDocComment, tag: JavadocTag) =
- when (val owner = currentElement.owner) {
- is PsiClass -> lowestClassWithTag(owner, tag)
- is PsiMethod -> lowestMethodWithTag(owner, tag)
- else -> null
- }?.tagsByName(tag)?.flatMap {
- when {
- it is PsiDocumentationContent && it.psiElement is PsiDocTag ->
- it.psiElement.contentElementsWithSiblingIfNeeded()
- .map { content -> PsiDocumentationContent(content, it.tag) }
- else -> listOf(it)
- }
- }.orEmpty()
-
- /**
- * Main resolution point for exception like tags
- *
- * This should be used only with [JavadocTag.EXCEPTION] or [JavadocTag.THROWS] as their resolution path should be the same
- */
- private fun resolveThrowsTag(
- tag: JavadocTag,
- currentElement: PsiDocComment,
- exceptionFqName: String
- ): List<DocumentationContent> {
- val closestDocs = (currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, tag) }
- .orEmpty().firstOrNull {
- findClosestDocComment(it, logger)?.hasTagWithExceptionOfType(tag, exceptionFqName) == true
- }
-
- return when (closestDocs?.language?.id) {
- "kotlin" -> closestDocs.toKdocComment()?.tagsByName(tag, exceptionFqName).orEmpty()
- else -> closestDocs?.docComment?.tagsByName(tag)?.flatMap {
- when (it) {
- is PsiDocTag -> it.contentElementsWithSiblingIfNeeded()
- else -> listOf(it)
- }
- }?.withoutReferenceLink().orEmpty().map { PsiDocumentationContent(it, tag) }
- }
- }
-
- private fun resolveParamTag(
- currentElement: PsiDocComment,
- parameterIndex: Int,
- ): List<DocumentationContent> =
- (currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, JavadocTag.PARAM) }
- .orEmpty().flatMap {
- if (parameterIndex >= it.parameterList.parametersCount || parameterIndex < 0) emptyList()
- else {
- val closestTag = findClosestDocComment(it, logger)
- val hasTag = closestTag?.hasTag(JavadocTag.PARAM)
- when {
- hasTag != true -> emptyList()
- closestTag is JavaDocComment -> resolveJavaParamTag(closestTag, parameterIndex, it)
- .withoutReferenceLink().map { PsiDocumentationContent(it, JavadocTag.PARAM) }
- closestTag is KotlinDocComment -> resolveKdocTag(closestTag, parameterIndex)
- else -> emptyList()
- }
- }
- }
-
- private fun resolveJavaParamTag(comment: JavaDocComment, parameterIndex: Int, method: PsiMethod) =
- comment.comment.tagsByName(JavadocTag.PARAM)
- .filterIsInstance<PsiDocTag>().map { it.contentElementsWithSiblingIfNeeded() }.firstOrNull {
- it.firstOrNull()?.text == method.parameterList.parameters[parameterIndex].name
- }.orEmpty()
-
- private fun resolveKdocTag(comment: KotlinDocComment, parameterIndex: Int): List<DocumentationContent> =
- listOf(comment.tagsByName(JavadocTag.PARAM)[parameterIndex])
-
- //if we are in psi class javadoc only inherits docs from classes and not from interfaces
- private fun lowestClassWithTag(baseClass: PsiClass, javadocTag: JavadocTag): DocComment? =
- baseClass.superClass?.let {
- findClosestDocComment(it, logger)?.takeIf { tag -> tag.hasTag(javadocTag) } ?: lowestClassWithTag(
- it,
- javadocTag
- )
- }
-
- private fun lowestMethodWithTag(
- baseMethod: PsiMethod,
- javadocTag: JavadocTag,
- ): DocComment? =
- lowestMethodsWithTag(baseMethod, javadocTag).firstOrNull()
- ?.let { it.docComment?.let { JavaDocComment(it) } ?: it.toKdocComment() }
-
- private fun lowestMethodsWithTag(baseMethod: PsiMethod, javadocTag: JavadocTag) =
- baseMethod.findSuperMethods().filter { findClosestDocComment(it, logger)?.hasTag(javadocTag) == true }
-
- private fun List<PsiElement>.withoutReferenceLink(): List<PsiElement> = drop(1)
-}
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 59c6f702..e69de29b 100644
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
@@ -1,511 +0,0 @@
-package org.jetbrains.dokka.base.translators.psi.parsers
-
-import com.intellij.lexer.JavaDocTokenTypes
-import com.intellij.psi.*
-import com.intellij.psi.impl.source.javadoc.PsiDocParamRef
-import com.intellij.psi.impl.source.tree.JavaDocElementType
-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.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.links.DRI
-import org.jetbrains.dokka.model.doc.*
-import org.jetbrains.dokka.model.doc.Deprecated
-import org.jetbrains.dokka.utilities.DokkaLogger
-import org.jetbrains.dokka.utilities.htmlEscape
-import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
-import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
-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.nodes.Comment
-import org.jsoup.nodes.Element
-import org.jsoup.nodes.Node
-import org.jsoup.nodes.TextNode
-import java.util.*
-
-fun interface JavaDocumentationParser {
- fun parseDocumentation(element: PsiNamedElement): DocumentationNode
-}
-
-class JavadocParser(
- private val logger: DokkaLogger,
- private val resolutionFacade: DokkaResolutionFacade,
-) : JavaDocumentationParser {
- private val inheritDocResolver = InheritDocResolver(logger)
-
- /**
- * Cache created to make storing entries from kotlin easier.
- *
- * It has to be mutable to allow for adding entries when @inheritDoc resolves to kotlin code,
- * from which we get a DocTags not descriptors.
- */
- private var inheritDocSections: MutableMap<UUID, DocumentationNode> = mutableMapOf()
-
- override fun parseDocumentation(element: PsiNamedElement): DocumentationNode {
- return when(val comment = findClosestDocComment(element, logger)){
- is JavaDocComment -> parseDocComment(comment.comment, element)
- is KotlinDocComment -> parseDocumentation(comment)
- else -> DocumentationNode(emptyList())
- }
- }
-
- internal fun parseDocComment(docComment: PsiDocComment, context: PsiNamedElement): DocumentationNode {
- val nodes = listOfNotNull(docComment.getDescription()) + docComment.tags.mapNotNull { tag ->
- parseDocTag(tag, docComment, context)
- }
- return DocumentationNode(nodes)
- }
-
- private fun parseDocumentation(element: KotlinDocComment, parseWithChildren: Boolean = true): DocumentationNode =
- MarkdownParser.parseFromKDocTag(
- kDocTag = element.comment,
- externalDri = { link: String ->
- try {
- resolveKDocLink(
- context = resolutionFacade.resolveSession.bindingContext,
- resolutionFacade = resolutionFacade,
- fromDescriptor = element.descriptor,
- fromSubjectOfTag = null,
- qualifiedName = link.split('.')
- ).firstOrNull()?.let { DRI.from(it) }
- } catch (e1: IllegalArgumentException) {
- logger.warn("Couldn't resolve link for $link")
- null
- }
- },
- kdocLocation = null,
- parseWithChildren = parseWithChildren
- )
-
- private fun parseDocTag(tag: PsiDocTag, docComment: PsiDocComment, analysedElement: PsiNamedElement): TagWrapper {
- val javadocTag = JavadocTag.lowercaseValueOfOrNull(tag.name)
- if (javadocTag == null) {
- return emptyTagWrapper(tag, docComment)
- }
- // Javadoc tag found
- val resolutionContext = CommentResolutionContext(comment = docComment, tag = javadocTag)
- return when (resolutionContext.tag) {
- JavadocTag.PARAM -> {
- val name = tag.dataElements.firstOrNull()?.text.orEmpty()
- val index =
- (analysedElement as? PsiMethod)?.parameterList?.parameters?.map { it.name }?.indexOf(name)
- Param(
- wrapTagIfNecessary(
- convertJavadocElements(
- tag.contentElementsWithSiblingIfNeeded().drop(1),
- context = resolutionContext.copy(name = name, parameterIndex = index)
- )
- ),
- name
- )
- }
-
- JavadocTag.THROWS, JavadocTag.EXCEPTION -> {
- val resolved = tag.resolveToElement()
- val dri = resolved?.let { DRI.from(it) }
- val name = resolved?.getKotlinFqName()?.asString()
- ?: tag.dataElements.firstOrNull()?.text.orEmpty()
- Throws(
- root = wrapTagIfNecessary(
- convertJavadocElements(
- tag.dataElements.drop(1),
- context = resolutionContext.copy(name = name)
- )
- ),
- /* we always would like to have a fully qualified name as name,
- * because it will be used as a display name later and we would like to have those unified
- * even if documentation states shortened version
- *
- * Only if dri search fails we should use the provided phrase (since then we are not able to get a fq name)
- * */
- name = name,
- exceptionAddress = dri
- )
- }
-
- JavadocTag.RETURN -> Return(
- wrapTagIfNecessary(
- convertJavadocElements(
- tag.contentElementsWithSiblingIfNeeded(),
- context = resolutionContext
- )
- )
- )
-
- JavadocTag.AUTHOR -> Author(
- wrapTagIfNecessary(
- convertJavadocElements(
- tag.contentElementsWithSiblingIfNeeded(),
- context = resolutionContext
- )
- )
- ) // Workaround: PSI returns first word after @author tag as a `DOC_TAG_VALUE_ELEMENT`, then the rest as a `DOC_COMMENT_DATA`, so for `Name Surname` we get them parted
- JavadocTag.SEE -> {
- val name =
- tag.resolveToElement()?.getKotlinFqName()?.asString() ?: tag.referenceElement()?.text.orEmpty()
- .removePrefix("#")
- getSeeTagElementContent(tag, resolutionContext.copy(name = name)).let {
- See(
- wrapTagIfNecessary(it.first),
- name,
- it.second
- )
- }
- }
-
- JavadocTag.DEPRECATED -> Deprecated(
- wrapTagIfNecessary(
- convertJavadocElements(
- tag.contentElementsWithSiblingIfNeeded(),
- context = resolutionContext
- )
- )
- )
-
- JavadocTag.SINCE -> Since(
- wrapTagIfNecessary(
- convertJavadocElements(
- tag.contentElementsWithSiblingIfNeeded(),
- context = resolutionContext
- )
- )
- )
-
- else -> emptyTagWrapper(tag, docComment)
- }
- }
-
- // Wrapper for unsupported tags https://github.com/Kotlin/dokka/issues/1618
- private fun emptyTagWrapper(
- tag: PsiDocTag,
- docComment: PsiDocComment
- ) = CustomTagWrapper(
- wrapTagIfNecessary(
- convertJavadocElements(
- tag.contentElementsWithSiblingIfNeeded(),
- context = CommentResolutionContext(docComment, null)
- )), tag.name
- )
-
- private fun wrapTagIfNecessary(list: List<DocTag>): CustomDocTag =
- if (list.size == 1 && (list.first() as? CustomDocTag)?.name == MarkdownElementTypes.MARKDOWN_FILE.name)
- list.first() as CustomDocTag
- else
- CustomDocTag(list, name = MarkdownElementTypes.MARKDOWN_FILE.name)
-
- private fun getSeeTagElementContent(
- tag: PsiDocTag,
- context: CommentResolutionContext
- ): Pair<List<DocTag>, DRI?> {
- val referenceElement = tag.referenceElement()
- val linkElement = referenceElement?.toDocumentationLink(context = context)
- val content = convertJavadocElements(
- tag.dataElements.dropWhile { it is PsiWhiteSpace || (it as? LazyParseablePsiElement)?.tokenType == JavaDocElementType.DOC_REFERENCE_HOLDER || it == referenceElement },
- context = context
- )
- return Pair(content, linkElement?.dri)
- }
-
- private fun PsiDocComment.getDescription(): Description? {
- return convertJavadocElements(
- descriptionElements.asIterable(),
- context = CommentResolutionContext(this, JavadocTag.DESCRIPTION)
- ).takeIf { it.isNotEmpty() }?.let {
- Description(wrapTagIfNecessary(it))
- }
- }
-
- private data class ParserState(
- val currentJavadocTag: JavadocTag?,
- val previousElement: PsiElement? = null,
- val openPreTags: Int = 0,
- val closedPreTags: Int = 0
- )
-
- private data class ParsingResult(val newState: ParserState, val parsedLine: String? = null) {
- constructor(tag: JavadocTag?) : this(ParserState(tag))
-
- operator fun plus(other: ParsingResult): ParsingResult =
- ParsingResult(
- other.newState,
- listOfNotNull(parsedLine, other.parsedLine).joinToString(separator = "")
- )
- }
-
- private inner class Parse : (Iterable<PsiElement>, Boolean, CommentResolutionContext) -> List<DocTag> {
- val driMap = mutableMapOf<String, DRI>()
-
- private fun PsiElement.stringify(state: ParserState, context: CommentResolutionContext): ParsingResult =
- when (this) {
- is PsiReference -> children.fold(ParsingResult(state)) { acc, e ->
- acc + e.stringify(acc.newState, context)
- }
- else -> stringifySimpleElement(state, context)
- }
-
- private fun DocumentationContent.stringify(state: ParserState, context: CommentResolutionContext): ParsingResult =
- when(this){
- is PsiDocumentationContent -> psiElement.stringify(state, context)
- is DescriptorDocumentationContent -> {
- val id = UUID.randomUUID()
- inheritDocSections[id] = parseDocumentation(KotlinDocComment(element, descriptor), parseWithChildren = false)
- ParsingResult(state, """<inheritdoc id="$id"/>""")
- }
- else -> throw IllegalStateException("Unrecognised documentation content: $this")
- }
-
- private fun PsiElement.stringifySimpleElement(
- state: ParserState,
- context: CommentResolutionContext
- ): ParsingResult {
- val openPre = state.openPreTags + "<pre(\\s+.*)?>".toRegex().findAll(text).toList().size
- val closedPre = state.closedPreTags + "</pre>".toRegex().findAll(text).toList().size
- val isInsidePre = openPre > closedPre
- val parsed = when (this) {
- is PsiInlineDocTag -> convertInlineDocTag(this, state.currentJavadocTag, context)
- is PsiDocParamRef -> toDocumentationLinkString()
- is PsiDocTagValue,
- is LeafPsiElement -> stringifyElementAsText(isInsidePre, state.previousElement)
- else -> null
- }
- val previousElement = if (text.trim() == "") state.previousElement else this
- return ParsingResult(
- state.copy(
- previousElement = previousElement,
- closedPreTags = closedPre,
- openPreTags = openPre
- ), parsed
- )
- }
-
- 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
- *
- * The space is required when:
- * - tag spans multiple lines, between every line we would need a space
- *
- * We wouldn't like to render a space if:
- * - tag is followed by an end of comment
- * - after a tag there is another tag (eg. multiple @author tags)
- * - they end with an html tag like: <a href="...">Something</a> since then the space will be displayed in the following text
- * - next line starts with a <p> or <pre> token
- */
- private fun PsiElement.shouldHaveSpaceAtTheEnd(): Boolean {
- val siblings = siblings(withItself = false).toList().filterNot { it.text.trim() == "" }
- val nextNotEmptySibling = (siblings.firstOrNull() as? PsiDocToken)
- val furtherNotEmptySibling =
- (siblings.drop(1).firstOrNull { it is PsiDocToken && !it.isLeadingAsterisk() } as? PsiDocToken)
- val lastHtmlTag = text.trim().substringAfterLast("<")
- val endsWithAnUnclosedTag = lastHtmlTag.endsWith(">") && !lastHtmlTag.startsWith("</")
-
- return (nextSibling as? PsiWhiteSpace)?.text?.startsWith("\n ") == true &&
- (getNextSiblingIgnoringWhitespace() as? PsiDocToken)?.tokenType != JavaDocTokenTypes.INSTANCE.commentEnd() &&
- nextNotEmptySibling?.isLeadingAsterisk() == true &&
- furtherNotEmptySibling?.tokenType == JavaDocTokenTypes.INSTANCE.commentData() &&
- !endsWithAnUnclosedTag
- }
-
- private fun PsiElement.toDocumentationLinkString(
- label: String = ""
- ): String {
-
- val dri = reference?.resolve()?.takeIf { it !is PsiParameter }?.let {
- val dri = DRI.from(it)
- driMap[dri.toString()] = dri
- dri.toString()
- } ?: UNRESOLVED_PSI_ELEMENT
-
- return """<a data-dri="${dri.htmlEscape()}">${label.ifBlank{ defaultLabel().text }}</a>"""
- }
-
- private fun convertInlineDocTag(
- tag: PsiInlineDocTag,
- javadocTag: JavadocTag?,
- context: CommentResolutionContext
- ) =
- when (tag.name) {
- "link", "linkplain" -> tag.referenceElement()
- ?.toDocumentationLinkString(tag.dataElements.filterIsInstance<PsiDocToken>().joinToString(" ") {
- it.stringifyElementAsText(keepFormatting = false).orEmpty()
- })
- "code" -> "<code data-inline>${dataElementsAsText(tag)}</code>"
- "literal" -> "<literal>${dataElementsAsText(tag)}</literal>"
- "index" -> "<index>${tag.children.filterIsInstance<PsiDocTagValue>().joinToString { it.text }}</index>"
- "inheritDoc" -> inheritDocResolver.resolveFromContext(context)
- ?.fold(ParsingResult(javadocTag)) { result, e ->
- result + e.stringify(result.newState, context)
- }?.parsedLine.orEmpty()
- else -> tag.text
- }
-
- private fun dataElementsAsText(tag: PsiInlineDocTag) =
- tag.dataElements.joinToString("") {
- it.stringifyElementAsText(keepFormatting = true).orEmpty()
- }.htmlEscape()
-
- 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(body = children.filterIsInstance<Text>().joinToString { it.body })
- }
- }
-
- private fun createBlock(element: Element, keepFormatting: Boolean = false): List<DocTag> {
- val tagName = element.tagName()
- val children = element.childNodes()
- .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 (tagName) {
- "blockquote" -> ifChildrenPresent { BlockQuote(children) }
- "p" -> ifChildrenPresent { P(children) }
- "b" -> ifChildrenPresent { B(children) }
- "strong" -> ifChildrenPresent { Strong(children) }
- "index" -> listOf(Index(children))
- "i" -> ifChildrenPresent { I(children) }
- "img" -> listOf(
- Img(
- children,
- element.attributes().associate { (if (it.key == "src") "href" else it.key) to it.value })
- )
- "em" -> listOf(Em(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))
- }
- "ul" -> ifChildrenPresent { Ul(children) }
- "ol" -> ifChildrenPresent { Ol(children) }
- "li" -> listOf(Li(children))
- "dl" -> ifChildrenPresent { Dl(children) }
- "dt" -> listOf(Dt(children))
- "dd" -> listOf(Dd(children))
- "a" -> listOf(createLink(element, children))
- "table" -> ifChildrenPresent { Table(children) }
- "tr" -> ifChildrenPresent { Tr(children) }
- "td" -> listOf(Td(children))
- "thead" -> listOf(THead(children))
- "tbody" -> listOf(TBody(children))
- "tfoot" -> listOf(TFoot(children))
- "caption" -> ifChildrenPresent { Caption(children) }
- "inheritdoc" -> {
- val id = UUID.fromString(element.attr("id"))
- val section = inheritDocSections[id]
- val parsed = section?.children?.flatMap { it.root.children }.orEmpty()
- if(parsed.size == 1 && parsed.first() is P){
- parsed.first().children
- } else {
- parsed
- }
- }
- "h1" -> ifChildrenPresent { H1(children) }
- "h2" -> ifChildrenPresent { H2(children) }
- "h3" -> ifChildrenPresent { H3(children) }
- "var" -> ifChildrenPresent { Var(children) }
- "u" -> ifChildrenPresent { U(children) }
- else -> listOf(Text(body = element.ownText()))
- }
- }
-
- 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 Comment -> listOf(Text(body = node.outerHtml(), params = DocTag.contentTypeParam("html")))
- is Element -> createBlock(node, keepFormatting)
- else -> emptyList()
- }
-
- override fun invoke(
- elements: Iterable<PsiElement>,
- asParagraph: Boolean,
- context: CommentResolutionContext
- ): List<DocTag> =
- elements.fold(ParsingResult(context.tag)) { acc, e ->
- acc + e.stringify(acc.newState, context)
- }.parsedLine?.let {
- val trimmed = it.trim()
- val toParse = if (asParagraph) "<p>$trimmed</p>" else trimmed
- Jsoup.parseBodyFragment(toParse).body().childNodes().flatMap { convertHtmlNode(it) }
- }.orEmpty()
- }
-
- private fun convertJavadocElements(
- elements: Iterable<PsiElement>,
- asParagraph: Boolean = true,
- context: CommentResolutionContext
- ): List<DocTag> =
- Parse()(elements, asParagraph, context)
-
- 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) =
- resolveToGetDri()?.let {
- val dri = DRI.from(it)
- val label = labelElement ?: defaultLabel()
- DocumentationLink(dri, convertJavadocElements(listOfNotNull(label), asParagraph = false, context))
- }
-
- private fun PsiDocTag.referenceElement(): PsiElement? =
- linkElement()?.referenceElementOrSelf()
-
- 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 }
-
- companion object {
- private const val UNRESOLVED_PSI_ELEMENT = "UNRESOLVED_PSI_ELEMENT"
- }
-}
diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt
index 5b3be7e3..e69de29b 100644
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt
+++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt
@@ -1,32 +0,0 @@
-package org.jetbrains.dokka.base.translators.psi.parsers
-
-internal enum class JavadocTag {
- PARAM, THROWS, RETURN, AUTHOR, SEE, DEPRECATED, EXCEPTION, HIDE, SINCE,
-
- /**
- * Artificial tag created to handle tag-less section
- */
- DESCRIPTION,;
-
- override fun toString(): String = super.toString().toLowerCase()
-
- /* Missing tags:
- SERIAL,
- SERIAL_DATA,
- SERIAL_FIELD,
- SINCE,
- VERSION
- */
-
- companion object {
- private val name2Value = values().associateBy { it.name.toLowerCase() }
-
- /**
- * Lowercase-based `Enum.valueOf` variation for [JavadocTag].
- *
- * Note: tags are [case-sensitive](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html) in Java,
- * thus we are not allowed to use case-insensitive or uppercase-based lookup.
- */
- fun lowercaseValueOfOrNull(name: String): JavadocTag? = name2Value[name]
- }
-}
diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt
deleted file mode 100644
index c4c8cbb2..00000000
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-package org.jetbrains.dokka.base.translators.psi.parsers
-
-import com.intellij.psi.*
-import com.intellij.psi.javadoc.PsiDocComment
-import com.intellij.psi.javadoc.PsiDocTag
-import org.jetbrains.dokka.analysis.from
-import org.jetbrains.dokka.base.translators.psi.findSuperMethodsOrEmptyArray
-import org.jetbrains.dokka.links.DRI
-import org.jetbrains.dokka.utilities.DokkaLogger
-import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
-import org.jetbrains.kotlin.idea.kdoc.findKDoc
-import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
-import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
-import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
-import org.jetbrains.kotlin.psi.KtDeclaration
-import org.jetbrains.kotlin.psi.KtElement
-import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
-import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
-
-internal interface DocComment {
- fun hasTag(tag: JavadocTag): Boolean
- fun hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean
- fun tagsByName(tag: JavadocTag, param: String? = null): List<DocumentationContent>
-}
-
-internal data class JavaDocComment(val comment: PsiDocComment) : DocComment {
- override fun hasTag(tag: JavadocTag): Boolean = comment.hasTag(tag)
- override fun hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean =
- comment.hasTag(tag) && comment.tagsByName(tag).firstIsInstanceOrNull<PsiDocTag>()
- ?.resolveToElement()
- ?.getKotlinFqName()?.asString() == exceptionFqName
-
- override fun tagsByName(tag: JavadocTag, param: String?): List<DocumentationContent> =
- comment.tagsByName(tag).map { PsiDocumentationContent(it, tag) }
-}
-
-internal data class KotlinDocComment(val comment: KDocTag, val descriptor: DeclarationDescriptor) : DocComment {
- override fun hasTag(tag: JavadocTag): Boolean =
- when (tag) {
- JavadocTag.DESCRIPTION -> comment.getContent().isNotEmpty()
- else -> tagsWithContent.any { it.text.startsWith("@$tag") }
- }
-
- override fun hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean =
- tagsWithContent.any { it.hasExceptionWithName(tag, exceptionFqName) }
-
- override fun tagsByName(tag: JavadocTag, param: String?): List<DocumentationContent> =
- when (tag) {
- JavadocTag.DESCRIPTION -> listOf(DescriptorDocumentationContent(descriptor, comment, tag))
- else -> comment.children.mapNotNull { (it as? KDocTag) }
- .filter { it.name == "$tag" && param?.let { param -> it.hasExceptionWithName(param) } != false }
- .map { DescriptorDocumentationContent(descriptor, it, tag) }
- }
-
- private val tagsWithContent: List<KDocTag> = comment.children.mapNotNull { (it as? KDocTag) }
-
- private fun KDocTag.hasExceptionWithName(tag: JavadocTag, exceptionFqName: String) =
- text.startsWith("@$tag") && hasExceptionWithName(exceptionFqName)
-
- private fun KDocTag.hasExceptionWithName(exceptionFqName: String) =
- getSubjectName() == exceptionFqName
-}
-
-internal interface DocumentationContent {
- val tag: JavadocTag
-}
-
-internal data class PsiDocumentationContent(val psiElement: PsiElement, override val tag: JavadocTag) :
- DocumentationContent
-
-internal data class DescriptorDocumentationContent(
- val descriptor: DeclarationDescriptor,
- val element: KDocTag,
- override val tag: JavadocTag
-) : DocumentationContent
-
-internal fun PsiDocComment.hasTag(tag: JavadocTag): Boolean =
- when (tag) {
- JavadocTag.DESCRIPTION -> descriptionElements.isNotEmpty()
- else -> findTagByName(tag.toString()) != null
- }
-
-internal fun PsiDocComment.tagsByName(tag: JavadocTag): List<PsiElement> =
- when (tag) {
- JavadocTag.DESCRIPTION -> descriptionElements.toList()
- else -> findTagsByName(tag.toString()).toList()
- }
-
-internal fun findClosestDocComment(element: PsiNamedElement, logger: DokkaLogger): DocComment? {
- (element as? PsiDocCommentOwner)?.docComment?.run { return JavaDocComment(this) }
- element.toKdocComment()?.run { return this }
-
- if (element is PsiMethod) {
- val superMethods = element.findSuperMethodsOrEmptyArray(logger)
- if (superMethods.isEmpty()) return null
-
- if (superMethods.size == 1) {
- return findClosestDocComment(superMethods.single(), logger)
- }
-
- val superMethodDocumentation = superMethods.map { method -> findClosestDocComment(method, logger) }.distinct()
- if (superMethodDocumentation.size == 1) {
- return superMethodDocumentation.single()
- }
-
- logger.debug(
- "Conflicting documentation for ${DRI.from(element)}" +
- "${superMethods.map { DRI.from(it) }}"
- )
-
- /* Prioritize super class over interface */
- val indexOfSuperClass = superMethods.indexOfFirst { method ->
- val parent = method.parent
- if (parent is PsiClass) !parent.isInterface
- else false
- }
-
- return if (indexOfSuperClass >= 0) superMethodDocumentation[indexOfSuperClass]
- else superMethodDocumentation.first()
- }
- return element.children.firstIsInstanceOrNull<PsiDocComment>()?.let { JavaDocComment(it) }
-}
-
-internal fun PsiNamedElement.toKdocComment(): KotlinDocComment? =
- (navigationElement as? KtElement)?.findKDoc { DescriptorToSourceUtils.descriptorToDeclaration(it) }
- ?.run {
- (this@toKdocComment.navigationElement as? KtDeclaration)?.descriptor?.let {
- KotlinDocComment(
- this,
- it
- )
- }
- }
-
-internal fun PsiDocTag.contentElementsWithSiblingIfNeeded(): List<PsiElement> = if (dataElements.isNotEmpty()) {
- listOfNotNull(
- dataElements[0],
- dataElements[0].nextSibling?.takeIf { it.text != dataElements.drop(1).firstOrNull()?.text },
- *dataElements.drop(1).toTypedArray()
- )
-} else {
- emptyList()
-}
-
-internal fun PsiDocTag.resolveToElement(): PsiElement? =
- dataElements.firstOrNull()?.firstChild?.referenceElementOrSelf()?.resolveToGetDri()
diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt
deleted file mode 100644
index 3cc16251..00000000
--- a/plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.jetbrains.dokka.base.translators.psi.parsers
-
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiJavaCodeReferenceElement
-import com.intellij.psi.impl.source.tree.JavaDocElementType
-import com.intellij.psi.util.PsiTreeUtil
-
-internal fun PsiElement.referenceElementOrSelf(): PsiElement? =
- if (node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) {
- PsiTreeUtil.findChildOfType(this, PsiJavaCodeReferenceElement::class.java)
- } else this
-
-internal fun PsiElement.resolveToGetDri(): PsiElement? =
- reference?.resolve() \ No newline at end of file