Skip to content

Commit

Permalink
Improve JavadocParser and fix case-sensitivity (#2905)
Browse files Browse the repository at this point in the history
* Get rid of safeEnumValueOf that was an unnecessary public API burden and constantly allocating
* Restructure JavadocParser.parseDocTag, so it has one lever of nesting less
* Make tag parsing case-sensitive

Fixes #2907
  • Loading branch information
qwwdfsad authored Mar 21, 2023
1 parent e66f9d8 commit ff5aa67
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 102 deletions.
4 changes: 0 additions & 4 deletions core/src/main/kotlin/utilities/safeEnumValueOf.kt

This file was deleted.

164 changes: 88 additions & 76 deletions plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ 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.enumValueOrNull
import org.jetbrains.dokka.utilities.htmlEscape
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
Expand Down Expand Up @@ -84,93 +83,106 @@ class JavadocParser(
parseWithChildren = parseWithChildren
)

private fun parseDocTag(tag: PsiDocTag, docComment: PsiDocComment, analysedElement: PsiNamedElement): TagWrapper =
enumValueOrNull<JavadocTag>(tag.name)?.let { javadocTag ->
val resolutionContext = CommentResolutionContext(comment = docComment, tag = javadocTag)
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,
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
name = name,
exceptionAddress = dri
)
}

JavadocTag.RETURN -> Return(
wrapTagIfNecessary(
convertJavadocElements(
tag.contentElementsWithSiblingIfNeeded(),
context = resolutionContext
)
}
JavadocTag.RETURN -> Return(
wrapTagIfNecessary(
convertJavadocElements(
tag.contentElementsWithSiblingIfNeeded(),
context = resolutionContext
)
)
)

JavadocTag.AUTHOR -> Author(
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
)
) // 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
)
)
)
else -> null
//TODO https://github.com/Kotlin/dokka/issues/1618
}
} ?: CustomTagWrapper(
wrapTagIfNecessary(
convertJavadocElements(
tag.contentElementsWithSiblingIfNeeded(),
context = CommentResolutionContext(docComment, null)

JavadocTag.DEPRECATED -> Deprecated(
wrapTagIfNecessary(
convertJavadocElements(
tag.contentElementsWithSiblingIfNeeded(),
context = resolutionContext
)
)
),
tag.name
)
)

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,16 @@ internal enum class JavadocTag {
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]
}
}
Loading

0 comments on commit ff5aa67

Please sign in to comment.