cc.unitmesh.docs.kdoc.findKDoc.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of docs-builder Show documentation
Show all versions of docs-builder Show documentation
Chocolate Factory is a cutting-edge LLM toolkit designed to empower you in creating your very own AI assistant.
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package cc.unitmesh.docs.kdoc
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.psi.psiUtil.isPropertyParameter
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
data class KDocContent(
val contentTag: KDocTag,
val sections: List,
val element: KtElement? = null
)
internal fun DeclarationDescriptor.findKDoc(
descriptorToPsi: (DeclarationDescriptorWithSource) -> PsiElement? = {
DescriptorToSourceUtils.descriptorToDeclaration(
it
)
},
): KDocContent? {
if (this is DeclarationDescriptorWithSource) {
val psiDeclaration = descriptorToPsi(this)?.navigationElement
return (psiDeclaration as? KtElement)?.findKDoc(descriptorToPsi)
}
return null
}
private typealias DescriptorToPsi = (DeclarationDescriptorWithSource) -> PsiElement?
internal fun KtElement.findKDoc(descriptorToPsi: DescriptorToPsi): KDocContent? {
return findKDoc()
?: this.lookupInheritedKDoc(descriptorToPsi)
}
internal fun KtElement.findKDoc(): KDocContent? {
return this.lookupOwnedKDoc()
?: this.lookupKDocInContainer()
}
private fun KtElement.lookupOwnedKDoc(): KDocContent? {
// KDoc for primary constructor is located inside of its class KDoc
val psiDeclaration = when (this) {
is KtPrimaryConstructor -> getContainingClassOrObject()
else -> this
}
if (psiDeclaration is KtDeclaration) {
val kdoc = psiDeclaration.docComment
if (kdoc != null) {
if (this is KtConstructor<*>) {
// ConstructorDescriptor resolves to the same JetDeclaration
val constructorSection = kdoc.findSectionByTag(KDocKnownTag.CONSTRUCTOR)
if (constructorSection != null) {
// if annotated with @constructor tag and the caret is on constructor definition,
// then show @constructor description as the main content, and additional sections
// that contain @param tags (if any), as the most relatable ones
// practical example: val foo = Foo("argument") -- show @constructor and @param content
val paramSections = kdoc.findSectionsContainingTag(KDocKnownTag.PARAM)
return KDocContent(constructorSection, paramSections, this)
}
}
return KDocContent(kdoc.getDefaultSection(), kdoc.getAllSections(), this)
}
}
return null
}
/**
* Looks for sections that have a deeply nested [tag],
* as opposed to [KDoc.findSectionByTag], which only looks among the top level
*/
private fun KDoc.findSectionsContainingTag(tag: KDocKnownTag): List {
return getChildrenOfType()
.filter { it.findTagByName(tag.name.toLowerCaseAsciiOnly()) != null }
}
private fun KtElement.lookupKDocInContainer(): KDocContent? {
val subjectName = name
val containingDeclaration =
PsiTreeUtil.findFirstParent(this, true) {
it is KtDeclarationWithBody && it !is KtPrimaryConstructor
|| it is KtClassOrObject
}
val containerKDoc = containingDeclaration?.getChildOfType()
if (containerKDoc == null || subjectName == null) return null
val propertySection = containerKDoc.findSectionByTag(KDocKnownTag.PROPERTY, subjectName)
val paramTag =
containerKDoc.findDescendantOfType { it.knownTag == KDocKnownTag.PARAM && it.getSubjectName() == subjectName }
val primaryContent = when {
// class Foo(val s: String)
this is KtParameter && this.isPropertyParameter() -> propertySection ?: paramTag
// fun some(f: String) || class Some<T: Base> || Foo(s = "argument")
this is KtParameter || this is KtTypeParameter -> paramTag
// if this property is declared separately (outside primary constructor), but it's for some reason
// annotated as @property in class's description, instead of having its own KDoc
this is KtProperty && containingDeclaration is KtClassOrObject -> propertySection
else -> null
}
return primaryContent?.let {
// makes little sense to include any other sections, since we found
// documentation for a very specific element, like a property/param
KDocContent(it, sections = emptyList(), this)
}
}
private fun KtElement.lookupInheritedKDoc(descriptorToPsi: DescriptorToPsi): KDocContent? {
return null
}