diff options
author | Ignat Beresnev <ignat.beresnev@jetbrains.com> | 2023-11-10 11:46:54 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-10 11:46:54 +0100 |
commit | 8e5c63d035ef44a269b8c43430f43f5c8eebfb63 (patch) | |
tree | 1b915207b2b9f61951ddbf0ff2e687efd053d555 /subprojects/analysis-java-psi/src/main | |
parent | a44efd4ba0c2e4ab921ff75e0f53fc9335aa79db (diff) | |
download | dokka-8e5c63d035ef44a269b8c43430f43f5c8eebfb63.tar.gz dokka-8e5c63d035ef44a269b8c43430f43f5c8eebfb63.tar.bz2 dokka-8e5c63d035ef44a269b8c43430f43f5c8eebfb63.zip |
Restructure the project to utilize included builds (#3174)
* Refactor and simplify artifact publishing
* Update Gradle to 8.4
* Refactor and simplify convention plugins and build scripts
Fixes #3132
---------
Co-authored-by: Adam <897017+aSemy@users.noreply.github.com>
Co-authored-by: Oleg Yukhnevich <whyoleg@gmail.com>
Diffstat (limited to 'subprojects/analysis-java-psi/src/main')
32 files changed, 0 insertions, 2728 deletions
diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt deleted file mode 100644 index 3b8ff25b..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java - -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiJavaFile -import com.intellij.psi.PsiKeyword -import com.intellij.psi.PsiManager -import com.intellij.psi.PsiModifierListOwner -import kotlinx.coroutines.coroutineScope -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.java.parsers.DokkaPsiParser -import org.jetbrains.dokka.analysis.java.parsers.JavaPsiDocCommentParser -import org.jetbrains.dokka.analysis.java.parsers.JavadocParser -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.model.JavaVisibility -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.query -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator -import org.jetbrains.dokka.utilities.parallelMap -import org.jetbrains.dokka.utilities.parallelMapNotNull - -internal class DefaultPsiToDocumentableTranslator : AsyncSourceToDocumentableTranslator { - - override suspend fun invokeSuspending(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { - return coroutineScope { - val projectProvider = context.plugin<JavaAnalysisPlugin>().querySingle { projectProvider } - val project = projectProvider.getProject(sourceSet, context) - - val sourceRootsExtractor = context.plugin<JavaAnalysisPlugin>().querySingle { sourceRootsExtractor } - val sourceRoots = sourceRootsExtractor.extract(sourceSet, context) - - val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") - - val psiFiles = sourceRoots.parallelMap { sourceRoot -> - sourceRoot.absoluteFile.walkTopDown().mapNotNull { - localFileSystem.findFileByPath(it.path)?.let { vFile -> - PsiManager.getInstance(project).findFile(vFile) as? PsiJavaFile - } - }.toList() - }.flatten() - - val docParser = createPsiParser(sourceSet, context) - - DModule( - name = context.configuration.moduleName, - packages = psiFiles.parallelMapNotNull { it }.groupBy { it.packageName }.toList() - .parallelMap { (packageName: String, psiFiles: List<PsiJavaFile>) -> - docParser.parsePackage(packageName, psiFiles) - }, - documentation = emptyMap(), - expectPresentInSet = null, - sourceSets = setOf(sourceSet) - ) - } - } - - private fun createPsiParser(sourceSet: DokkaSourceSet, context: DokkaContext): DokkaPsiParser { - val projectProvider = context.plugin<JavaAnalysisPlugin>().querySingle { projectProvider } - val docCommentParsers = context.plugin<JavaAnalysisPlugin>().query { docCommentParsers } - return DokkaPsiParser( - sourceSetData = sourceSet, - project = projectProvider.getProject(sourceSet, context), - logger = context.logger, - javadocParser = JavadocParser( - docCommentParsers = docCommentParsers, - docCommentFinder = context.plugin<JavaAnalysisPlugin>().docCommentFinder - ), - javaPsiDocCommentParser = docCommentParsers.single { it is JavaPsiDocCommentParser } as JavaPsiDocCommentParser, - lightMethodChecker = context.plugin<JavaAnalysisPlugin>().querySingle { kotlinLightMethodChecker } - ) - } -} - -internal fun PsiModifierListOwner.getVisibility() = modifierList?.let { - val ml = it.children.toList() - when { - ml.any { it.text == PsiKeyword.PUBLIC } || it.hasModifierProperty("public") -> JavaVisibility.Public - ml.any { it.text == PsiKeyword.PROTECTED } || it.hasModifierProperty("protected") -> JavaVisibility.Protected - ml.any { it.text == PsiKeyword.PRIVATE } || it.hasModifierProperty("private") -> JavaVisibility.Private - else -> JavaVisibility.Default - } -} ?: JavaVisibility.Default diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt deleted file mode 100644 index 9426adf1..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java - -import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute -import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiAnnotation -import org.jetbrains.dokka.CoreExtensions -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.doccomment.DocCommentCreator -import org.jetbrains.dokka.analysis.java.doccomment.DocCommentFactory -import org.jetbrains.dokka.analysis.java.doccomment.DocCommentFinder -import org.jetbrains.dokka.analysis.java.doccomment.JavaDocCommentCreator -import org.jetbrains.dokka.analysis.java.parsers.DocCommentParser -import org.jetbrains.dokka.analysis.java.parsers.doctag.InheritDocTagContentProvider -import org.jetbrains.dokka.analysis.java.parsers.JavaPsiDocCommentParser -import org.jetbrains.dokka.analysis.java.parsers.doctag.InheritDocTagResolver -import org.jetbrains.dokka.analysis.java.parsers.doctag.PsiDocTagParser -import org.jetbrains.dokka.analysis.java.util.NoopIntellijLoggerFactory -import org.jetbrains.dokka.plugability.* -import java.io.File - - -@InternalDokkaApi -public interface ProjectProvider { - public fun getProject(sourceSet: DokkaSourceSet, context: DokkaContext): Project -} - -@InternalDokkaApi -public interface SourceRootsExtractor { - public fun extract(sourceSet: DokkaSourceSet, context: DokkaContext): List<File> -} - -@InternalDokkaApi -public interface BreakingAbstractionKotlinLightMethodChecker { - // TODO [beresnev] not even sure it's needed, but left for compatibility and to preserve behaviour - public fun isLightAnnotation(annotation: PsiAnnotation): Boolean - public fun isLightAnnotationAttribute(attribute: JvmAnnotationAttribute): Boolean -} - -@InternalDokkaApi -public class JavaAnalysisPlugin : DokkaPlugin() { - - // single - public val projectProvider: ExtensionPoint<ProjectProvider> by extensionPoint() - - // single - public val sourceRootsExtractor: ExtensionPoint<SourceRootsExtractor> by extensionPoint() - - // multiple - public val docCommentCreators: ExtensionPoint<DocCommentCreator> by extensionPoint() - - // multiple - public val docCommentParsers: ExtensionPoint<DocCommentParser> by extensionPoint() - - // none or more - public val inheritDocTagContentProviders: ExtensionPoint<InheritDocTagContentProvider> by extensionPoint() - - // TODO [beresnev] figure out a better way depending on what it's used for - public val kotlinLightMethodChecker: ExtensionPoint<BreakingAbstractionKotlinLightMethodChecker> by extensionPoint() - - private val docCommentFactory by lazy { - DocCommentFactory(query { docCommentCreators }.reversed()) - } - - public val docCommentFinder: DocCommentFinder by lazy { - DocCommentFinder(logger, docCommentFactory) - } - - internal val javaDocCommentCreator by extending { - docCommentCreators providing { JavaDocCommentCreator() } - } - - private val psiDocTagParser by lazy { - PsiDocTagParser( - inheritDocTagResolver = InheritDocTagResolver( - docCommentFactory = docCommentFactory, - docCommentFinder = docCommentFinder, - contentProviders = query { inheritDocTagContentProviders } - ) - ) - } - - internal val javaDocCommentParser by extending { - docCommentParsers providing { - JavaPsiDocCommentParser( - psiDocTagParser - ) - } - } - - internal val psiToDocumentableTranslator by extending { - CoreExtensions.sourceToDocumentableTranslator providing { DefaultPsiToDocumentableTranslator() } - } - - @OptIn(DokkaPluginApiPreview::class) - override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement - - private companion object { - init { - // Suppress messages emitted by the IntelliJ logger since - // there's not much the end user can do about it - Logger.setFactory(NoopIntellijLoggerFactory()) - } - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt deleted file mode 100644 index 23aee764..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java - -import com.intellij.psi.PsiMethod -import org.jetbrains.dokka.InternalDokkaApi - -@InternalDokkaApi -public sealed class JavadocTag( - public val name: String -) - -public object AuthorJavadocTag : JavadocTag("author") -public object DeprecatedJavadocTag : JavadocTag("deprecated") -public object DescriptionJavadocTag : JavadocTag("description") -public object ReturnJavadocTag : JavadocTag("return") -public object SinceJavadocTag : JavadocTag("since") - -public class ParamJavadocTag( - public val method: PsiMethod, - public val paramName: String, - public val paramIndex: Int -) : JavadocTag(name) { - public companion object { - public const val name: String = "param" - } -} - -public class SeeJavadocTag( - public val qualifiedReference: String -) : JavadocTag(name) { - public companion object { - public const val name: String = "see" - } -} - -public sealed class ThrowingExceptionJavadocTag( - name: String, - public val exceptionQualifiedName: String? -) : JavadocTag(name) - -public class ThrowsJavadocTag(exceptionQualifiedName: String?) : ThrowingExceptionJavadocTag(name, exceptionQualifiedName) { - public companion object { - public const val name: String = "throws" - } -} - -public class ExceptionJavadocTag(exceptionQualifiedName: String?) : ThrowingExceptionJavadocTag(name, exceptionQualifiedName) { - public companion object { - public const val name: String = "exception" - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt deleted file mode 100644 index 5b19a42d..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java - -import com.intellij.openapi.project.Project -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.psi.SyntheticElement -import com.intellij.psi.javadoc.PsiDocComment -import org.jetbrains.dokka.analysis.java.parsers.JavaPsiDocCommentParser -import org.jetbrains.dokka.model.doc.DocumentationNode - -private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/javadoc/EnumValueOf.java.template" -private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/javadoc/EnumValues.java.template" - -internal class SyntheticElementDocumentationProvider( - private val javadocParser: JavaPsiDocCommentParser, - private val project: Project -) { - fun isDocumented(psiElement: PsiElement): Boolean = psiElement is PsiMethod - && (psiElement.isSyntheticEnumValuesMethod() || psiElement.isSyntheticEnumValueOfMethod()) - - fun getDocumentation(psiElement: PsiElement): DocumentationNode? { - val psiMethod = psiElement as? PsiMethod ?: return null - val templatePath = when { - psiMethod.isSyntheticEnumValuesMethod() -> ENUM_VALUES_TEMPLATE_PATH - psiMethod.isSyntheticEnumValueOfMethod() -> ENUM_VALUEOF_TEMPLATE_PATH - else -> return null - } - val docComment = loadSyntheticDoc(templatePath) ?: return null - return javadocParser.parsePsiDocComment(docComment, psiElement) - } - - private fun loadSyntheticDoc(path: String): PsiDocComment? { - val text = javaClass.getResource(path)?.readText() ?: return null - return JavaPsiFacade.getElementFactory(project).createDocCommentFromText(text) - } -} - -private fun PsiMethod.isSyntheticEnumValuesMethod() = this.isSyntheticEnumFunction() && this.name == "values" -private fun PsiMethod.isSyntheticEnumValueOfMethod() = this.isSyntheticEnumFunction() && this.name == "valueOf" -private fun PsiMethod.isSyntheticEnumFunction() = this is SyntheticElement && this.containingClass?.isEnum == true - diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt deleted file mode 100644 index be5f7eaa..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.JavadocTag - -/** - * MUST override equals and hashcode - */ -@InternalDokkaApi -public interface DocComment { - public fun hasTag(tag: JavadocTag): Boolean - - public fun resolveTag(tag: JavadocTag): List<DocumentationContent> -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt deleted file mode 100644 index 2e22c77c..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.InternalDokkaApi - -@InternalDokkaApi -public interface DocCommentCreator { - public fun create(element: PsiNamedElement): DocComment? -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt deleted file mode 100644 index 4647d4d9..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.InternalDokkaApi - -@InternalDokkaApi -public class DocCommentFactory( - private val docCommentCreators: List<DocCommentCreator> -) { - public fun fromElement(element: PsiNamedElement): DocComment? { - docCommentCreators.forEach { creator -> - val comment = creator.create(element) - if (comment != null) { - return comment - } - } - return null - } -} - diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt deleted file mode 100644 index 18463e0e..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.javadoc.PsiDocComment -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.util.from -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.utilities.DokkaLogger - -@InternalDokkaApi -public class DocCommentFinder( - private val logger: DokkaLogger, - private val docCommentFactory: DocCommentFactory, -) { - public fun findClosestToElement(element: PsiNamedElement): DocComment? { - val docComment = docCommentFactory.fromElement(element) - if (docComment != null) { - return docComment - } - - return if (element is PsiMethod) { - findClosestToMethod(element) - } else { - element.children - .filterIsInstance<PsiDocComment>() - .firstOrNull() - ?.let { JavaDocComment(it) } - } - } - - private fun findClosestToMethod(method: PsiMethod): DocComment? { - val superMethods = method.findSuperMethods() - if (superMethods.isEmpty()) return null - - if (superMethods.size == 1) { - return findClosestToElement(superMethods.single()) - } - - val superMethodDocumentation = superMethods.map { superMethod -> findClosestToElement(superMethod) }.distinct() - if (superMethodDocumentation.size == 1) { - return superMethodDocumentation.single() - } - - logger.debug( - "Conflicting documentation for ${DRI.from(method)}" + - "${superMethods.map { DRI.from(it) }}" - ) - - /* Prioritize super class over interface */ - val indexOfSuperClass = superMethods.indexOfFirst { superMethod -> - val parent = superMethod.parent - if (parent is PsiClass) !parent.isInterface - else false - } - - return if (indexOfSuperClass >= 0) { - superMethodDocumentation[indexOfSuperClass] - } else { - superMethodDocumentation.first() - } - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt deleted file mode 100644 index 64e9ceb8..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.JavadocTag - -@InternalDokkaApi -public interface DocumentationContent { - public val tag: JavadocTag - - public fun resolveSiblings(): List<DocumentationContent> -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt deleted file mode 100644 index 066b5162..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import com.intellij.psi.PsiElement -import com.intellij.psi.javadoc.PsiDocComment -import com.intellij.psi.javadoc.PsiDocTag -import org.jetbrains.dokka.analysis.java.* -import org.jetbrains.dokka.analysis.java.util.contentElementsWithSiblingIfNeeded -import org.jetbrains.dokka.analysis.java.util.getKotlinFqName -import org.jetbrains.dokka.analysis.java.util.hasTag -import org.jetbrains.dokka.analysis.java.util.resolveToElement -import org.jetbrains.dokka.utilities.firstIsInstanceOrNull - -internal class JavaDocComment(val comment: PsiDocComment) : DocComment { - override fun hasTag(tag: JavadocTag): Boolean { - return when (tag) { - is ThrowingExceptionJavadocTag -> hasTag(tag) - else -> comment.hasTag(tag) - } - } - - private fun hasTag(tag: ThrowingExceptionJavadocTag): Boolean = - comment.hasTag(tag) && comment.resolveTag(tag).firstIsInstanceOrNull<PsiDocTag>() - ?.resolveToElement() - ?.getKotlinFqName() == tag.exceptionQualifiedName - - override fun resolveTag(tag: JavadocTag): List<DocumentationContent> { - return when (tag) { - is ParamJavadocTag -> resolveParamTag(tag) - is ThrowingExceptionJavadocTag -> resolveThrowingTag(tag) - else -> comment.resolveTag(tag).map { PsiDocumentationContent(it, tag) } - } - } - - private fun resolveParamTag(tag: ParamJavadocTag): List<DocumentationContent> { - val resolvedParamElements = comment.resolveTag(tag) - .filterIsInstance<PsiDocTag>() - .map { it.contentElementsWithSiblingIfNeeded() } - .firstOrNull { - it.firstOrNull()?.text == tag.method.parameterList.parameters[tag.paramIndex].name - }.orEmpty() - - return resolvedParamElements - .withoutReferenceLink() - .map { PsiDocumentationContent(it, tag) } - } - - private fun resolveThrowingTag(tag: ThrowingExceptionJavadocTag): List<DocumentationContent> { - val resolvedElements = comment.resolveTag(tag) - .flatMap { - when (it) { - is PsiDocTag -> it.contentElementsWithSiblingIfNeeded() - else -> listOf(it) - } - } - - return resolvedElements - .withoutReferenceLink() - .map { PsiDocumentationContent(it, tag) } - } - - private fun PsiDocComment.resolveTag(tag: JavadocTag): List<PsiElement> { - return when (tag) { - DescriptionJavadocTag -> this.descriptionElements.toList() - else -> this.findTagsByName(tag.name).toList() - } - } - - private fun List<PsiElement>.withoutReferenceLink(): List<PsiElement> = drop(1) - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as JavaDocComment - - if (comment != other.comment) return false - - return true - } - - override fun hashCode(): Int { - return comment.hashCode() - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt deleted file mode 100644 index 79da0ee2..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import com.intellij.psi.PsiDocCommentOwner -import com.intellij.psi.PsiNamedElement - -internal class JavaDocCommentCreator : DocCommentCreator { - override fun create(element: PsiNamedElement): DocComment? { - val psiDocComment = (element as? PsiDocCommentOwner)?.docComment ?: return null - return JavaDocComment(psiDocComment) - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt deleted file mode 100644 index aa1fe120..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.doccomment - -import com.intellij.psi.PsiElement -import com.intellij.psi.javadoc.PsiDocTag -import org.jetbrains.dokka.analysis.java.JavadocTag -import org.jetbrains.dokka.analysis.java.util.contentElementsWithSiblingIfNeeded - -internal data class PsiDocumentationContent( - val psiElement: PsiElement, - override val tag: JavadocTag -) : DocumentationContent { - - override fun resolveSiblings(): List<DocumentationContent> { - return if (psiElement is PsiDocTag) { - psiElement.contentElementsWithSiblingIfNeeded() - .map { content -> PsiDocumentationContent(content, tag) } - } else { - listOf(this) - } - } - -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt deleted file mode 100644 index 1cd7d604..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers - -import com.intellij.psi.javadoc.PsiDocComment -import org.jetbrains.dokka.analysis.java.JavadocTag - -internal data class CommentResolutionContext( - val comment: PsiDocComment, - val tag: JavadocTag? -) diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DocCommentParser.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DocCommentParser.kt deleted file mode 100644 index 4a381e80..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DocCommentParser.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers - -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.doccomment.DocComment -import org.jetbrains.dokka.model.doc.DocumentationNode - -@InternalDokkaApi -public interface DocCommentParser { - public fun canParse(docComment: DocComment): Boolean - public fun parse(docComment: DocComment, context: PsiNamedElement): DocumentationNode -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DokkaPsiParser.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DokkaPsiParser.kt deleted file mode 100644 index b8dabe42..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DokkaPsiParser.kt +++ /dev/null @@ -1,803 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers - -import com.intellij.lang.jvm.JvmModifier -import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute -import com.intellij.lang.jvm.annotation.JvmAnnotationAttributeValue -import com.intellij.lang.jvm.annotation.JvmAnnotationConstantValue -import com.intellij.lang.jvm.annotation.JvmAnnotationEnumFieldValue -import com.intellij.lang.jvm.types.JvmReferenceType -import com.intellij.openapi.project.Project -import com.intellij.psi.* -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.analysis.java.BreakingAbstractionKotlinLightMethodChecker -import org.jetbrains.dokka.analysis.java.SyntheticElementDocumentationProvider -import org.jetbrains.dokka.analysis.java.getVisibility -import org.jetbrains.dokka.analysis.java.util.* -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.nextTarget -import org.jetbrains.dokka.links.withClass -import org.jetbrains.dokka.links.withEnumEntryExtra -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.AnnotationTarget -import org.jetbrains.dokka.model.doc.DocumentationNode -import org.jetbrains.dokka.model.doc.Param -import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.utilities.parallelForEach -import org.jetbrains.dokka.utilities.parallelMap -import org.jetbrains.dokka.utilities.parallelMapNotNull - -internal class DokkaPsiParser( - private val sourceSetData: DokkaConfiguration.DokkaSourceSet, - private val project: Project, - private val logger: DokkaLogger, - private val javadocParser: JavadocParser, - private val javaPsiDocCommentParser: JavaPsiDocCommentParser, - private val lightMethodChecker: BreakingAbstractionKotlinLightMethodChecker, -) { - private val syntheticDocProvider = SyntheticElementDocumentationProvider(javaPsiDocCommentParser, project) - - private val cachedBounds = hashMapOf<String, Bound>() - - private val PsiMethod.hash: Int - get() = "$returnType $name$parameterList".hashCode() - - private val PsiField.hash: Int - get() = "$type $name".hashCode() - - private val PsiClassType.shouldBeIgnored: Boolean - get() = isClass("java.lang.Enum") || isClass("java.lang.Object") - - private fun PsiClassType.isClass(qName: String): Boolean { - val shortName = qName.substringAfterLast('.') - if (className == shortName) { - val psiClass = resolve() - return psiClass?.qualifiedName == qName - } - return false - } - - private fun <T> T.toSourceSetDependent() = mapOf(sourceSetData to this) - - suspend fun parsePackage(packageName: String, psiFiles: List<PsiJavaFile>): DPackage = coroutineScope { - val dri = DRI(packageName = packageName) - val packageInfo = psiFiles.singleOrNull { it.name == "package-info.java" } - val documentation = packageInfo?.let { - javadocParser.parseDocumentation(it).toSourceSetDependent() - }.orEmpty() - val annotations = packageInfo?.packageStatement?.annotationList?.annotations - - DPackage( - dri = dri, - functions = emptyList(), - properties = emptyList(), - classlikes = psiFiles.parallelMap { psiFile -> - coroutineScope { - psiFile.classes.asIterable().parallelMap { parseClasslike(it, dri) } - } - }.flatten(), - typealiases = emptyList(), - documentation = documentation, - expectPresentInSet = null, - sourceSets = setOf(sourceSetData), - extra = PropertyContainer.withAll( - annotations?.toList().orEmpty().toListOfAnnotations().toSourceSetDependent().toAnnotations() - ) - ) - } - - private suspend fun parseClasslike(psi: PsiClass, parent: DRI): DClasslike = coroutineScope { - with(psi) { - val dri = parent.withClass(name.toString()) - val superMethodsKeys = hashSetOf<Int>() - val superMethods = mutableListOf<Pair<PsiMethod, DRI>>() - val superFieldsKeys = hashSetOf<Int>() - val superFields = mutableListOf<Pair<PsiField, DRI>>() - methods.asIterable().parallelForEach { superMethodsKeys.add(it.hash) } - - /** - * Caution! This method mutates - * - superMethodsKeys - * - superMethods - * - superFieldsKeys - * - superKeys - */ - /** - * Caution! This method mutates - * - superMethodsKeys - * - superMethods - * - superFieldsKeys - * - superKeys - */ - fun Array<PsiClassType>.getSuperTypesPsiClasses(): List<Pair<PsiClass, JavaClassKindTypes>> { - forEach { type -> - (type as? PsiClassType)?.resolve()?.let { - val definedAt = DRI.from(it) - it.methods.forEach { method -> - val hash = method.hash - if (!method.isConstructor && !superMethodsKeys.contains(hash) && - method.getVisibility() != JavaVisibility.Private - ) { - superMethodsKeys.add(hash) - superMethods.add(Pair(method, definedAt)) - } - } - it.fields.forEach { field -> - val hash = field.hash - if (!superFieldsKeys.contains(hash)) { - superFieldsKeys.add(hash) - superFields.add(Pair(field, definedAt)) - } - } - } - } - return filter { !it.shouldBeIgnored }.mapNotNull { supertypePsi -> - supertypePsi.resolve()?.let { supertypePsiClass -> - val javaClassKind = when { - supertypePsiClass.isInterface -> JavaClassKindTypes.INTERFACE - else -> JavaClassKindTypes.CLASS - } - supertypePsiClass to javaClassKind - } - } - } - - fun traversePsiClassForAncestorsAndInheritedMembers(psiClass: PsiClass): AncestryNode { - val (classes, interfaces) = psiClass.superTypes.getSuperTypesPsiClasses() - .partition { it.second == JavaClassKindTypes.CLASS } - - return AncestryNode( - typeConstructor = GenericTypeConstructor( - DRI.from(psiClass), - psiClass.typeParameters.map { typeParameter -> - TypeParameter( - dri = DRI.from(typeParameter), - name = typeParameter.name.orEmpty(), - extra = typeParameter.annotations() - ) - } - ), - superclass = classes.singleOrNull()?.first?.let(::traversePsiClassForAncestorsAndInheritedMembers), - interfaces = interfaces.map { traversePsiClassForAncestorsAndInheritedMembers(it.first) } - ) - } - - val ancestry: AncestryNode = traversePsiClassForAncestorsAndInheritedMembers(this) - - val (regularFunctions, accessors) = splitFunctionsAndAccessors(psi.fields, psi.methods) - val (regularSuperFunctions, superAccessors) = splitFunctionsAndAccessors( - fields = superFields.map { it.first }.toTypedArray(), - methods = superMethods.map { it.first }.toTypedArray() - ) - - val regularSuperFunctionsKeys = regularSuperFunctions.map { it.hash }.toSet() - val regularSuperFunctionsWithDRI = superMethods.filter { it.first.hash in regularSuperFunctionsKeys } - - val superAccessorsWithDRI = superAccessors.mapValues { (field, methods) -> - val containsJvmField = field.annotations.mapNotNull { it.toAnnotation() }.any { it.isJvmField() } - if (containsJvmField) { - emptyList() - } else { - methods.mapNotNull { method -> superMethods.find { it.first.hash == method.hash } } - } - } - - val overridden = regularFunctions.flatMap { it.findSuperMethods().toList() } - val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent() - val allFunctions = async { - val parsedRegularFunctions = regularFunctions.parallelMapNotNull { - if (!it.isConstructor) parseFunction( - it, - parentDRI = dri - ) else null - } - val parsedSuperFunctions = regularSuperFunctionsWithDRI - .filter { it.first !in overridden } - .parallelMap { parseFunction(it.first, inheritedFrom = it.second) } - - parsedRegularFunctions + parsedSuperFunctions - } - val allFields = async { - val parsedFields = fields.toList().parallelMapNotNull { - parseField(it, accessors[it].orEmpty()) - } - val parsedSuperFields = superFields.parallelMapNotNull { (field, dri) -> - parseFieldWithInheritingAccessors( - field, - superAccessorsWithDRI[field].orEmpty(), - inheritedFrom = dri - ) - } - parsedFields + parsedSuperFields - } - val source = parseSources() - val classlikes = async { innerClasses.asIterable().parallelMap { parseClasslike(it, dri) } } - val visibility = getVisibility().toSourceSetDependent() - val ancestors = (listOfNotNull(ancestry.superclass?.let { - it.typeConstructor.let { typeConstructor -> - TypeConstructorWithKind( - typeConstructor, - JavaClassKindTypes.CLASS - ) - } - }) + ancestry.interfaces.map { - TypeConstructorWithKind( - it.typeConstructor, - JavaClassKindTypes.INTERFACE - ) - }).toSourceSetDependent() - val modifiers = getModifier().toSourceSetDependent() - val implementedInterfacesExtra = - ImplementedInterfaces(ancestry.allImplementedInterfaces().toSourceSetDependent()) - - when { - isAnnotationType -> - DAnnotation( - name = name.orEmpty(), - dri = dri, - documentation = documentation, - expectPresentInSet = null, - sources = source, - functions = allFunctions.await(), - properties = allFields.await(), - classlikes = classlikes.await(), - visibility = visibility, - companion = null, - constructors = parseConstructors(dri), - generics = mapTypeParameters(dri), - sourceSets = setOf(sourceSetData), - isExpectActual = false, - extra = PropertyContainer.withAll( - implementedInterfacesExtra, - annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - - isEnum -> DEnum( - dri = dri, - name = name.orEmpty(), - entries = fields.filterIsInstance<PsiEnumConstant>().map { entry -> - DEnumEntry( - dri = dri.withClass(entry.name).withEnumEntryExtra(), - name = entry.name, - documentation = javadocParser.parseDocumentation(entry).toSourceSetDependent(), - expectPresentInSet = null, - functions = emptyList(), - properties = emptyList(), - classlikes = emptyList(), - sourceSets = setOf(sourceSetData), - extra = PropertyContainer.withAll( - implementedInterfacesExtra, - annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - }, - documentation = documentation, - expectPresentInSet = null, - sources = source, - functions = allFunctions.await(), - properties = fields.filter { it !is PsiEnumConstant } - .map { parseField(it, accessors[it].orEmpty()) }, - classlikes = classlikes.await(), - visibility = visibility, - companion = null, - constructors = parseConstructors(dri), - supertypes = ancestors, - sourceSets = setOf(sourceSetData), - isExpectActual = false, - extra = PropertyContainer.withAll( - implementedInterfacesExtra, - annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - - isInterface -> DInterface( - dri = dri, - name = name.orEmpty(), - documentation = documentation, - expectPresentInSet = null, - sources = source, - functions = allFunctions.await(), - properties = allFields.await(), - classlikes = classlikes.await(), - visibility = visibility, - companion = null, - generics = mapTypeParameters(dri), - supertypes = ancestors, - sourceSets = setOf(sourceSetData), - isExpectActual = false, - extra = PropertyContainer.withAll( - implementedInterfacesExtra, - annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - - else -> DClass( - dri = dri, - name = name.orEmpty(), - constructors = parseConstructors(dri), - functions = allFunctions.await(), - properties = allFields.await(), - classlikes = classlikes.await(), - sources = source, - visibility = visibility, - companion = null, - generics = mapTypeParameters(dri), - supertypes = ancestors, - documentation = documentation, - expectPresentInSet = null, - modifier = modifiers, - sourceSets = setOf(sourceSetData), - isExpectActual = false, - extra = PropertyContainer.withAll( - implementedInterfacesExtra, - annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations(), - ancestry.exceptionInSupertypesOrNull() - ) - ) - } - } - } - - /* - * Parameter `parentDRI` required for substitute package name: - * in the case of synthetic constructor, it will return empty from [DRI.Companion.from]. - */ - private fun PsiClass.parseConstructors(parentDRI: DRI): List<DFunction> { - val constructors = when { - isAnnotationType || isInterface -> emptyArray() - isEnum -> this.constructors - else -> this.constructors.takeIf { it.isNotEmpty() } ?: arrayOf(createDefaultConstructor()) - } - return constructors.map { parseFunction(psi = it, isConstructor = true, parentDRI = parentDRI) } - } - - /** - * PSI doesn't return a default constructor if class doesn't contain an explicit one. - * This method create synthetic constructor - * Visibility modifier is preserved from the class. - */ - private fun PsiClass.createDefaultConstructor(): PsiMethod { - val psiElementFactory = JavaPsiFacade.getElementFactory(project) - val signature = when (val classVisibility = getVisibility()) { - JavaVisibility.Default -> name.orEmpty() - else -> "${classVisibility.name} $name" - } - return psiElementFactory.createConstructor(signature, this) - } - - private fun AncestryNode.exceptionInSupertypesOrNull(): ExceptionInSupertypes? = - typeConstructorsBeingExceptions().takeIf { it.isNotEmpty() } - ?.let { ExceptionInSupertypes(it.toSourceSetDependent()) } - - private fun parseFunction( - psi: PsiMethod, - isConstructor: Boolean = false, - inheritedFrom: DRI? = null, - parentDRI: DRI? = null, - ): DFunction { - val dri = parentDRI?.let { dri -> - DRI.from(psi).copy(packageName = dri.packageName, classNames = dri.classNames) - } ?: DRI.from(psi) - val docs = psi.getDocumentation() - return DFunction( - dri = dri, - name = psi.name, - isConstructor = isConstructor, - parameters = psi.parameterList.parameters.map { psiParameter -> - DParameter( - dri = dri.copy(target = dri.target.nextTarget()), - name = psiParameter.name, - documentation = DocumentationNode( - listOfNotNull(docs.firstChildOfTypeOrNull<Param> { - it.name == psiParameter.name - }) - ).toSourceSetDependent(), - expectPresentInSet = null, - type = getBound(psiParameter.type), - sourceSets = setOf(sourceSetData), - extra = PropertyContainer.withAll( - psiParameter.annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - }, - documentation = docs.toSourceSetDependent(), - expectPresentInSet = null, - sources = psi.parseSources(), - visibility = psi.getVisibility().toSourceSetDependent(), - type = psi.returnType?.let { getBound(type = it) } ?: Void, - generics = psi.mapTypeParameters(dri), - receiver = null, - modifier = psi.getModifier().toSourceSetDependent(), - sourceSets = setOf(sourceSetData), - isExpectActual = false, - extra = psi.additionalExtras().let { - PropertyContainer.withAll( - inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) }, - it.toSourceSetDependent().toAdditionalModifiers(), - (psi.annotations.toList() - .toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() - .toAnnotations(), - ObviousMember.takeIf { psi.isObvious(inheritedFrom) }, - psi.throwsList.toDriList().takeIf { it.isNotEmpty() } - ?.let { CheckedExceptions(it.toSourceSetDependent()) } - ) - } - ) - } - - private fun PsiNamedElement.parseSources(): SourceSetDependent<DocumentableSource> { - return when { - // `isPhysical` detects the virtual declarations without real sources. - // Otherwise, `PsiDocumentableSource` initialization will fail: non-physical declarations doesn't have `virtualFile`. - // This check protects from accidentally requesting sources for synthetic / virtual declarations. - isPhysical -> PsiDocumentableSource(this).toSourceSetDependent() - else -> emptyMap() - } - } - - private fun PsiMethod.getDocumentation(): DocumentationNode = - this.takeIf { it is SyntheticElement }?.let { syntheticDocProvider.getDocumentation(it) } - ?: javadocParser.parseDocumentation(this) - - private fun PsiMethod.isObvious(inheritedFrom: DRI? = null): Boolean { - return (this is SyntheticElement && !syntheticDocProvider.isDocumented(this)) - || inheritedFrom?.isObvious() == true - } - - private fun DRI.isObvious(): Boolean { - return packageName == "java.lang" && (classNames == "Object" || classNames == "Enum") - } - - private fun PsiReferenceList.toDriList() = referenceElements.mapNotNull { it?.resolve()?.let { DRI.from(it) } } - - private fun PsiModifierListOwner.additionalExtras() = listOfNotNull( - ExtraModifiers.JavaOnlyModifiers.Static.takeIf { hasModifier(JvmModifier.STATIC) }, - ExtraModifiers.JavaOnlyModifiers.Native.takeIf { hasModifier(JvmModifier.NATIVE) }, - ExtraModifiers.JavaOnlyModifiers.Synchronized.takeIf { hasModifier(JvmModifier.SYNCHRONIZED) }, - ExtraModifiers.JavaOnlyModifiers.StrictFP.takeIf { hasModifier(JvmModifier.STRICTFP) }, - ExtraModifiers.JavaOnlyModifiers.Transient.takeIf { hasModifier(JvmModifier.TRANSIENT) }, - ExtraModifiers.JavaOnlyModifiers.Volatile.takeIf { hasModifier(JvmModifier.VOLATILE) }, - ExtraModifiers.JavaOnlyModifiers.Transitive.takeIf { hasModifier(JvmModifier.TRANSITIVE) } - ).toSet() - - private fun Set<ExtraModifiers>.toListOfAnnotations() = map { - if (it !is ExtraModifiers.JavaOnlyModifiers.Static) - Annotations.Annotation(DRI("kotlin.jvm", it.name.toLowerCase().capitalize()), emptyMap()) - else - Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap()) - } - - /** - * Workaround for getting JvmField Kotlin annotation in PSIs - */ - private fun Collection<PsiAnnotation>.findJvmFieldAnnotation(): Annotations.Annotation? { - val anyJvmFieldAnnotation = this.any { - it.qualifiedName == "$JVM_FIELD_PACKAGE_NAME.$JVM_FIELD_CLASS_NAMES" - } - return if (anyJvmFieldAnnotation) { - Annotations.Annotation(DRI(JVM_FIELD_PACKAGE_NAME, JVM_FIELD_CLASS_NAMES), emptyMap()) - } else { - null - } - } - - private fun <T : AnnotationTarget> PsiTypeParameter.annotations(): PropertyContainer<T> = this.annotations.toList().toListOfAnnotations().annotations() - private fun <T : AnnotationTarget> PsiType.annotations(): PropertyContainer<T> = this.annotations.toList().toListOfAnnotations().annotations() - - private fun <T : AnnotationTarget> List<Annotations.Annotation>.annotations(): PropertyContainer<T> = - this.takeIf { it.isNotEmpty() }?.let { annotations -> - PropertyContainer.withAll(annotations.toSourceSetDependent().toAnnotations()) - } ?: PropertyContainer.empty() - - private fun getBound(type: PsiType): Bound { - //We would like to cache most of the bounds since it is not common to annotate them, - //but if this is the case, we treat them as 'one of' - fun PsiType.cacheBoundIfHasNoAnnotation(f: (List<Annotations.Annotation>) -> Bound): Bound { - val annotations = this.annotations.toList().toListOfAnnotations() - return if (annotations.isNotEmpty()) f(annotations) - else cachedBounds.getOrPut(canonicalText) { - f(annotations) - } - } - - return when (type) { - is PsiClassType -> - type.resolve()?.let { resolved -> - when { - resolved.qualifiedName == "java.lang.Object" -> type.cacheBoundIfHasNoAnnotation { annotations -> JavaObject(annotations.annotations()) } - resolved is PsiTypeParameter -> { - TypeParameter( - dri = DRI.from(resolved), - name = resolved.name.orEmpty(), - extra = type.annotations() - ) - } - - Regex("kotlin\\.jvm\\.functions\\.Function.*").matches(resolved.qualifiedName ?: "") || - Regex("java\\.util\\.function\\.Function.*").matches( - resolved.qualifiedName ?: "" - ) -> FunctionalTypeConstructor( - DRI.from(resolved), - type.parameters.map { getProjection(it) }, - extra = type.annotations() - ) - - else -> { - // cache types that have no annotation and no type parameter - // since we cache only by name and type parameters depend on context - val typeParameters = type.parameters.map { getProjection(it) } - if (typeParameters.isEmpty()) - type.cacheBoundIfHasNoAnnotation { annotations -> - GenericTypeConstructor( - DRI.from(resolved), - typeParameters, - extra = annotations.annotations() - ) - } - else - GenericTypeConstructor( - DRI.from(resolved), - typeParameters, - extra = type.annotations() - ) - } - } - } ?: UnresolvedBound(type.presentableText, type.annotations()) - - is PsiArrayType -> GenericTypeConstructor( - DRI("kotlin", "Array"), - listOf(getProjection(type.componentType)), - extra = type.annotations() - ) - - is PsiPrimitiveType -> if (type.name == "void") Void - else type.cacheBoundIfHasNoAnnotation { annotations -> PrimitiveJavaType(type.name, annotations.annotations()) } - else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser") - } - } - - - private fun getVariance(type: PsiWildcardType): Projection = when { - type.isExtends -> Covariance(getBound(type.extendsBound)) - type.isSuper -> Contravariance(getBound(type.superBound)) - // If the type isn't explicitly bounded, it still has an implicit `extends Object` bound - type.extendsBound != PsiType.NULL -> Covariance(getBound(type.extendsBound)) - else -> throw IllegalStateException("${type.presentableText} has incorrect bounds") - } - - private fun getProjection(type: PsiType): Projection = when (type) { - is PsiEllipsisType -> Star - is PsiWildcardType -> getVariance(type) - else -> getBound(type) - } - - private fun PsiModifierListOwner.getModifier() = when { - hasModifier(JvmModifier.ABSTRACT) -> JavaModifier.Abstract - hasModifier(JvmModifier.FINAL) -> JavaModifier.Final - else -> JavaModifier.Empty - } - - private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List<DTypeParameter> { - fun mapBounds(bounds: Array<JvmReferenceType>): List<Bound> = - if (bounds.isEmpty()) emptyList() else bounds.mapNotNull { - (it as? PsiClassType)?.let { classType -> Nullable(getBound(classType)) } - } - return typeParameters.map { type -> - DTypeParameter( - dri = dri.copy(target = dri.target.nextTarget()), - name = type.name.orEmpty(), - presentableName = null, - documentation = javadocParser.parseDocumentation(type).toSourceSetDependent(), - expectPresentInSet = null, - bounds = mapBounds(type.bounds), - sourceSets = setOf(sourceSetData), - extra = PropertyContainer.withAll( - type.annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations() - ) - ) - } - } - - private fun parseFieldWithInheritingAccessors( - psi: PsiField, - accessors: List<Pair<PsiMethod, DRI>>, - inheritedFrom: DRI - ): DProperty { - val getter = accessors - .firstOrNull { (method, _) -> method.isGetterFor(psi) } - ?.let { (method, dri) -> parseFunction(method, inheritedFrom = dri) } - - val setter = accessors - .firstOrNull { (method, _) -> method.isSetterFor(psi) } - ?.let { (method, dri) -> parseFunction(method, inheritedFrom = dri) } - - return parseField( - psi = psi, - getter = getter, - setter = setter, - inheritedFrom = inheritedFrom - ) - } - - private fun parseField(psi: PsiField, accessors: List<PsiMethod>, inheritedFrom: DRI? = null): DProperty { - val getter = accessors.firstOrNull { it.isGetterFor(psi) }?.let { parseFunction(it) } - val setter = accessors.firstOrNull { it.isSetterFor(psi) }?.let { parseFunction(it) } - return parseField( - psi = psi, - getter = getter, - setter = setter, - inheritedFrom = inheritedFrom - ) - } - - private fun parseField(psi: PsiField, getter: DFunction?, setter: DFunction?, inheritedFrom: DRI? = null): DProperty { - val dri = DRI.from(psi) - - // non-final java field without accessors should be a var - // setter should be not null when inheriting kotlin vars - val isMutable = !psi.hasModifierProperty("final") - val isVar = (isMutable && getter == null && setter == null) || (getter != null && setter != null) - - return DProperty( - dri = dri, - name = psi.name, - documentation = javadocParser.parseDocumentation(psi).toSourceSetDependent(), - expectPresentInSet = null, - sources = psi.parseSources(), - visibility = psi.getVisibility(getter).toSourceSetDependent(), - type = getBound(psi.type), - receiver = null, - setter = setter, - getter = getter, - modifier = psi.getModifier().toSourceSetDependent(), - sourceSets = setOf(sourceSetData), - generics = emptyList(), - isExpectActual = false, - extra = psi.additionalExtras().let { - val psiAnnotations = psi.annotations.toList() - val parsedAnnotations = psiAnnotations.toListOfAnnotations() - val extraModifierAnnotations = it.toListOfAnnotations() - val jvmFieldAnnotation = psiAnnotations.findJvmFieldAnnotation() - val annotations = parsedAnnotations + extraModifierAnnotations + listOfNotNull(jvmFieldAnnotation) - - PropertyContainer.withAll( - inheritedFrom?.let { inheritedFrom -> InheritedMember(inheritedFrom.toSourceSetDependent()) }, - it.toSourceSetDependent().toAdditionalModifiers(), - annotations.toSourceSetDependent().toAnnotations(), - psi.getConstantExpression()?.let { DefaultValue(it.toSourceSetDependent()) }, - takeIf { isVar }?.let { IsVar } - ) - } - ) - } - - private fun PsiField.getVisibility(getter: DFunction?): Visibility { - return getter?.visibility?.get(sourceSetData) ?: this.getVisibility() - } - - private fun Collection<PsiAnnotation>.toListOfAnnotations() = - filter { !lightMethodChecker.isLightAnnotation(it) }.mapNotNull { it.toAnnotation() } - - private fun PsiField.getConstantExpression(): Expression? { - val constantValue = this.computeConstantValue() ?: return null - return when (constantValue) { - is Byte -> IntegerConstant(constantValue.toLong()) - is Short -> IntegerConstant(constantValue.toLong()) - is Int -> IntegerConstant(constantValue.toLong()) - is Long -> IntegerConstant(constantValue) - is Char -> StringConstant(constantValue.toString()) - is String -> StringConstant(constantValue) - is Double -> DoubleConstant(constantValue) - is Float -> FloatConstant(constantValue) - is Boolean -> BooleanConstant(constantValue) - else -> ComplexExpression(constantValue.toString()) - } - } - - private fun JvmAnnotationAttribute.toValue(): AnnotationParameterValue = when (this) { - is PsiNameValuePair -> value?.toValue() ?: attributeValue?.toValue() ?: StringValue("") - else -> StringValue(this.attributeName) - }.let { annotationValue -> - if (annotationValue is StringValue) annotationValue.copy(annotationValue.value.removeSurrounding("\"")) - else annotationValue - } - - /** - * This is a workaround for static imports from JDK like RetentionPolicy - * For some reason they are not represented in the same way than using normal import - */ - private fun JvmAnnotationAttributeValue.toValue(): AnnotationParameterValue? { - return when (this) { - is JvmAnnotationEnumFieldValue -> (field as? PsiElement)?.let { EnumValue(fieldName ?: "", DRI.from(it)) } - // static import of a constant is resolved to constant value instead of a field/link - is JvmAnnotationConstantValue -> this.constantValue?.toAnnotationLiteralValue() - else -> null - } - } - - private fun Any.toAnnotationLiteralValue() = when (this) { - is Byte -> IntValue(this.toInt()) - is Short -> IntValue(this.toInt()) - is Char -> StringValue(this.toString()) - is Int -> IntValue(this) - is Long -> LongValue(this) - is Boolean -> BooleanValue(this) - is Float -> FloatValue(this) - is Double -> DoubleValue(this) - else -> StringValue(this.toString()) - } - - private fun PsiAnnotationMemberValue.toValue(): AnnotationParameterValue? = when (this) { - is PsiAnnotation -> toAnnotation()?.let { AnnotationValue(it) } - is PsiArrayInitializerMemberValue -> ArrayValue(initializers.mapNotNull { it.toValue() }) - is PsiReferenceExpression -> psiReference?.let { EnumValue(text ?: "", DRI.from(it)) } - is PsiClassObjectAccessExpression -> { - val parameterType = (type as? PsiClassType)?.parameters?.firstOrNull() - val classType = when (parameterType) { - is PsiClassType -> parameterType.resolve() - // Notice: Array<String>::class will be passed down as String::class - // should probably be Array::class instead but this reflects behaviour for Kotlin sources - is PsiArrayType -> (parameterType.componentType as? PsiClassType)?.resolve() - else -> null - } - classType?.let { ClassValue(it.name ?: "", DRI.from(it)) } - } - is PsiLiteralExpression -> toValue() - else -> StringValue(text ?: "") - } - - private fun PsiLiteralExpression.toValue(): AnnotationParameterValue? = when (type) { - PsiType.INT -> (value as? Int)?.let { IntValue(it) } - PsiType.LONG -> (value as? Long)?.let { LongValue(it) } - PsiType.FLOAT -> (value as? Float)?.let { FloatValue(it) } - PsiType.DOUBLE -> (value as? Double)?.let { DoubleValue(it) } - PsiType.BOOLEAN -> (value as? Boolean)?.let { BooleanValue(it) } - PsiType.NULL -> NullValue - else -> StringValue(text ?: "") - } - - private fun PsiAnnotation.toAnnotation(): Annotations.Annotation? { - // TODO Mitigating workaround for issue https://github.com/Kotlin/dokka/issues/1341 - // Tracking https://youtrack.jetbrains.com/issue/KT-41234 - // Needs to be removed once this issue is fixed in light classes - fun PsiElement.getAnnotationsOrNull(): Array<PsiAnnotation>? { - this as PsiClass - return try { - this.annotations - } catch (e: Exception) { - logger.warn("Failed to get annotations from ${this.qualifiedName}") - null - } - } - - return psiReference?.let { psiElement -> - Annotations.Annotation( - dri = DRI.from(psiElement), - params = attributes - .filter { !lightMethodChecker.isLightAnnotationAttribute(it) } - .mapNotNull { it.attributeName to it.toValue() } - .toMap(), - mustBeDocumented = psiElement.getAnnotationsOrNull().orEmpty().any { annotation -> - annotation.hasQualifiedName("java.lang.annotation.Documented") - } - ) - } - } - - private val PsiElement.psiReference - get() = getChildOfType<PsiJavaCodeReferenceElement>()?.resolve() -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavaDocCommentParser.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavaDocCommentParser.kt deleted file mode 100644 index 1e4c9f2d..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavaDocCommentParser.kt +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.impl.source.tree.LazyParseablePsiElement -import com.intellij.psi.javadoc.PsiDocComment -import com.intellij.psi.javadoc.PsiDocTag - -import org.jetbrains.dokka.analysis.java.* -import org.jetbrains.dokka.analysis.java.doccomment.DocComment -import org.jetbrains.dokka.analysis.java.doccomment.JavaDocComment -import org.jetbrains.dokka.analysis.java.parsers.doctag.PsiDocTagParser -import org.jetbrains.dokka.analysis.java.util.* -import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.doc.Deprecated - -internal class JavaPsiDocCommentParser( - private val psiDocTagParser: PsiDocTagParser, -) : DocCommentParser { - - override fun canParse(docComment: DocComment): Boolean { - return docComment is JavaDocComment - } - - override fun parse(docComment: DocComment, context: PsiNamedElement): DocumentationNode { - val javaDocComment = docComment as JavaDocComment - return parsePsiDocComment(javaDocComment.comment, context) - } - - internal fun parsePsiDocComment(docComment: PsiDocComment, context: PsiNamedElement): DocumentationNode { - val description = listOfNotNull(docComment.getDescription()) - val tags = docComment.tags.mapNotNull { tag -> - parseDocTag(tag, docComment, context) - } - return DocumentationNode(description + tags) - } - - private fun PsiDocComment.getDescription(): Description? { - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = descriptionElements.asIterable(), - commentResolutionContext = CommentResolutionContext(this, DescriptionJavadocTag), - ) - return docTags.takeIf { it.isNotEmpty() }?.let { - Description(wrapTagIfNecessary(it)) - } - } - - private fun parseDocTag(tag: PsiDocTag, docComment: PsiDocComment, analysedElement: PsiNamedElement): TagWrapper? { - return when (tag.name) { - ParamJavadocTag.name -> parseParamTag(tag, docComment, analysedElement) - ThrowsJavadocTag.name, ExceptionJavadocTag.name -> parseThrowsTag(tag, docComment) - ReturnJavadocTag.name -> parseReturnTag(tag, docComment) - SinceJavadocTag.name -> parseSinceTag(tag, docComment) - AuthorJavadocTag.name -> parseAuthorTag(tag, docComment) - SeeJavadocTag.name -> parseSeeTag(tag, docComment) - DeprecatedJavadocTag.name -> parseDeprecatedTag(tag, docComment) - else -> emptyTagWrapper(tag, docComment) - } - } - - private fun parseParamTag( - tag: PsiDocTag, - docComment: PsiDocComment, - analysedElement: PsiNamedElement - ): TagWrapper? { - val paramName = tag.dataElements.firstOrNull()?.text.orEmpty() - - // can be a PsiClass if @param is referencing class generics, like here: - // https://github.com/biojava/biojava/blob/2417c230be36e4ba73c62bb3631b60f876265623/biojava-core/src/main/java/org/biojava/nbio/core/alignment/SimpleProfilePair.java#L43 - // not supported at the moment - val method = analysedElement as? PsiMethod ?: return null - val paramIndex = method.parameterList.parameters.map { it.name }.indexOf(paramName) - - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.contentElementsWithSiblingIfNeeded().drop(1), - commentResolutionContext = CommentResolutionContext( - comment = docComment, - tag = ParamJavadocTag(method, paramName, paramIndex) - ) - ) - return Param(root = wrapTagIfNecessary(docTags), name = paramName) - } - - private fun parseThrowsTag( - tag: PsiDocTag, - docComment: PsiDocComment - ): Throws { - val resolvedElement = tag.resolveToElement() - val exceptionAddress = resolvedElement?.let { DRI.from(it) } - - /* 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) - */ - val fullyQualifiedExceptionName = - resolvedElement?.getKotlinFqName() ?: tag.dataElements.firstOrNull()?.text.orEmpty() - - val javadocTag = when (tag.name) { - ThrowsJavadocTag.name -> ThrowsJavadocTag(fullyQualifiedExceptionName) - ExceptionJavadocTag.name -> ExceptionJavadocTag(fullyQualifiedExceptionName) - else -> throw IllegalArgumentException("Expected @throws or @exception") - } - - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.dataElements.drop(1), - commentResolutionContext = CommentResolutionContext( - comment = docComment, - tag = javadocTag - ), - ) - return Throws( - root = wrapTagIfNecessary(docTags), - name = fullyQualifiedExceptionName, - exceptionAddress = exceptionAddress - ) - } - - private fun parseReturnTag( - tag: PsiDocTag, - docComment: PsiDocComment - ): Return { - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.contentElementsWithSiblingIfNeeded(), - commentResolutionContext = CommentResolutionContext(comment = docComment, tag = ReturnJavadocTag), - ) - return Return(root = wrapTagIfNecessary(docTags)) - } - - private fun parseSinceTag( - tag: PsiDocTag, - docComment: PsiDocComment - ): Since { - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.contentElementsWithSiblingIfNeeded(), - commentResolutionContext = CommentResolutionContext(comment = docComment, tag = ReturnJavadocTag), - ) - return Since(root = wrapTagIfNecessary(docTags)) - } - - private fun parseAuthorTag( - tag: PsiDocTag, - docComment: PsiDocComment - ): Author { - // TODO [beresnev] see what the hell this is - // 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 - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.contentElementsWithSiblingIfNeeded(), - commentResolutionContext = CommentResolutionContext(comment = docComment, tag = AuthorJavadocTag), - ) - return Author(root = wrapTagIfNecessary(docTags)) - } - - private fun parseSeeTag( - tag: PsiDocTag, - docComment: PsiDocComment - ): See { - val referenceElement = tag.referenceElement() - val fullyQualifiedSeeReference = tag.resolveToElement()?.getKotlinFqName() - ?: referenceElement?.text.orEmpty().removePrefix("#") - - val context = CommentResolutionContext(comment = docComment, tag = SeeJavadocTag(fullyQualifiedSeeReference)) - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.dataElements.dropWhile { - it is PsiWhiteSpace || it.isDocReferenceHolder() || it == referenceElement - }, - commentResolutionContext = context, - ) - - return See( - root = wrapTagIfNecessary(docTags), - name = fullyQualifiedSeeReference, - address = referenceElement?.toDocumentationLink(context = context)?.dri - ) - } - - private fun PsiElement.isDocReferenceHolder(): Boolean { - return (this as? LazyParseablePsiElement)?.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER - } - - private fun parseDeprecatedTag( - tag: PsiDocTag, - docComment: PsiDocComment - ): Deprecated { - val docTags = psiDocTagParser.parseAsParagraph( - tag.contentElementsWithSiblingIfNeeded(), - CommentResolutionContext(comment = docComment, tag = DeprecatedJavadocTag), - ) - return Deprecated(root = wrapTagIfNecessary(docTags)) - } - - private fun wrapTagIfNecessary(tags: List<DocTag>): CustomDocTag { - val isFile = (tags.singleOrNull() as? CustomDocTag)?.name == MARKDOWN_ELEMENT_FILE_NAME - return if (isFile) { - tags.first() as CustomDocTag - } else { - CustomDocTag(tags, name = MARKDOWN_ELEMENT_FILE_NAME) - } - } - - // Wrapper for unsupported tags https://github.com/Kotlin/dokka/issues/1618 - private fun emptyTagWrapper( - tag: PsiDocTag, - docComment: PsiDocComment, - ): CustomTagWrapper { - val docTags = psiDocTagParser.parseAsParagraph( - psiElements = tag.contentElementsWithSiblingIfNeeded(), - commentResolutionContext = CommentResolutionContext(docComment, null), - ) - return CustomTagWrapper( - root = wrapTagIfNecessary(docTags), - name = tag.name - ) - } - - private fun PsiElement.toDocumentationLink(labelElement: PsiElement? = null, context: CommentResolutionContext): DocumentationLink? { - val resolvedElement = this.resolveToGetDri() ?: return null - val label = labelElement ?: defaultLabel() - val docTags = psiDocTagParser.parse(listOfNotNull(label), context) - return DocumentationLink(dri = DRI.from(resolvedElement), children = docTags) - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavadocParser.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavadocParser.kt deleted file mode 100644 index 140e64c9..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavadocParser.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers - -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.doccomment.DocCommentFinder -import org.jetbrains.dokka.model.doc.DocumentationNode - -internal fun interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): DocumentationNode -} - -@InternalDokkaApi -public class JavadocParser( - private val docCommentParsers: List<DocCommentParser>, - private val docCommentFinder: DocCommentFinder -) : JavaDocumentationParser { - - override fun parseDocumentation(element: PsiNamedElement): DocumentationNode { - val comment = docCommentFinder.findClosestToElement(element) ?: return DocumentationNode(emptyList()) - return docCommentParsers - .first { it.canParse(comment) } - .parse(comment, element) - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext.kt deleted file mode 100644 index e8df804b..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers.doctag - -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.DocumentationNode -import java.util.* - -@InternalDokkaApi -public class DocTagParserContext { - /** - * exists for resolving `@link element` links, where the referenced - * PSI element is mapped as DRI - * - * only used in the context of parsing to html and then from html to doctag - */ - private val driMap = mutableMapOf<String, DRI>() - - /** - * 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 val inheritDocSections = mutableMapOf<String, DocumentationNode>() - - /** - * @return key of the stored DRI - */ - public fun store(dri: DRI): String { - val id = dri.toString() - driMap[id] = dri - return id - } - - /** - * @return key of the stored documentation node - */ - public fun store(documentationNode: DocumentationNode): String { - val id = UUID.randomUUID().toString() - inheritDocSections[id] = documentationNode - return id - } - - public fun getDri(id: String): DRI? = driMap[id] - - public fun getDocumentationNode(id: String): DocumentationNode? = inheritDocSections[id] -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/HtmlToDocTagConverter.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/HtmlToDocTagConverter.kt deleted file mode 100644 index 195ce79a..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/HtmlToDocTagConverter.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers.doctag - -import org.jetbrains.dokka.analysis.markdown.jb.parseHtmlEncodedWithNormalisedSpaces -import org.jetbrains.dokka.model.doc.* -import org.jsoup.Jsoup -import org.jsoup.nodes.Comment -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode - -internal class HtmlToDocTagConverter( - private val docTagParserContext: DocTagParserContext -) { - fun convertToDocTag(html: String): List<DocTag> { - return Jsoup.parseBodyFragment(html) - .body() - .childNodes() - .flatMap { convertHtmlNode(it) } - } - - 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() - } - - 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" -> { - // TODO [beresnev] describe how it works - val id = element.attr("id") - val section = docTagParserContext.getDocumentationNode(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 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") && docTagParserContext.getDri(element.attr("data-dri")) != null -> { - val referencedDriId = element.attr("data-dri") - DocumentationLink( - dri = docTagParserContext.getDri(referencedDriId) - ?: error("docTagParserContext.getDri is null, TODO"), // TODO [beresnev] handle - children = children - ) - } - else -> Text(body = children.filterIsInstance<Text>().joinToString { it.body }) - } - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagContentProvider.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagContentProvider.kt deleted file mode 100644 index a09f85c4..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagContentProvider.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers.doctag - -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent - -@InternalDokkaApi -public interface InheritDocTagContentProvider { - public fun canConvert(content: DocumentationContent): Boolean - public fun convertToHtml(content: DocumentationContent, docTagParserContext: DocTagParserContext): String -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagResolver.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagResolver.kt deleted file mode 100644 index 344f69b1..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagResolver.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers.doctag - -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiMethod -import com.intellij.psi.javadoc.PsiDocComment -import org.jetbrains.dokka.analysis.java.* -import org.jetbrains.dokka.analysis.java.doccomment.* -import org.jetbrains.dokka.analysis.java.doccomment.JavaDocComment -import org.jetbrains.dokka.analysis.java.parsers.CommentResolutionContext - -internal class InheritDocTagResolver( - private val docCommentFactory: DocCommentFactory, - private val docCommentFinder: DocCommentFinder, - private val contentProviders: List<InheritDocTagContentProvider> -) { - internal fun convertToHtml(content: DocumentationContent, docTagParserContext: DocTagParserContext): String? { - return contentProviders - .firstOrNull { it.canConvert(content) } - ?.convertToHtml(content, docTagParserContext) - } - - internal fun resolveContent(context: CommentResolutionContext): List<DocumentationContent>? { - val javadocTag = context.tag ?: return null - - return when (javadocTag) { - is ThrowingExceptionJavadocTag -> { - javadocTag.exceptionQualifiedName?.let { _ -> - resolveThrowsTag( - javadocTag, - context.comment, - ) - } ?: return null - } - is ParamJavadocTag -> resolveParamTag(context.comment, javadocTag) - is DeprecatedJavadocTag -> resolveGenericTag(context.comment, DescriptionJavadocTag) - is SeeJavadocTag -> emptyList() - else -> resolveGenericTag(context.comment, javadocTag) - } - } - - private fun resolveGenericTag(currentElement: PsiDocComment, tag: JavadocTag): List<DocumentationContent> { - val docComment = when (val owner = currentElement.owner) { - is PsiClass -> lowestClassWithTag(owner, tag) - is PsiMethod -> lowestMethodWithTag(owner, tag) - else -> null - } - return docComment?.resolveTag(tag)?.flatMap { - it.resolveSiblings() - }.orEmpty() - } - - /** - * Main resolution point for exception like tags - * - * This should be used only with [ThrowsJavadocTag] or [ExceptionJavadocTag] as their resolution path should be the same - */ - private fun resolveThrowsTag( - tag: ThrowingExceptionJavadocTag, - currentElement: PsiDocComment, - ): List<DocumentationContent> { - val closestDocsWithThrows = - (currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, tag) } - .orEmpty().firstOrNull { - docCommentFinder.findClosestToElement(it)?.hasTag(tag) == true - } ?: return emptyList() - - return docCommentFactory.fromElement(closestDocsWithThrows) - ?.resolveTag(tag) - ?: emptyList() - } - - private fun resolveParamTag( - currentElement: PsiDocComment, - paramTag: ParamJavadocTag, - ): List<DocumentationContent> { - val parameterIndex = paramTag.paramIndex - - val methods = (currentElement.owner as? PsiMethod) - ?.let { method -> lowestMethodsWithTag(method, paramTag) } - .orEmpty() - - return methods.flatMap { - if (parameterIndex >= it.parameterList.parametersCount || parameterIndex < 0) { - return@flatMap emptyList() - } - - val closestTag = docCommentFinder.findClosestToElement(it) - val hasTag = closestTag?.hasTag(paramTag) ?: false - closestTag?.takeIf { hasTag }?.resolveTag(ParamJavadocTag(it, "", parameterIndex)) ?: emptyList() - } - } - - //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 { - docCommentFinder.findClosestToElement(it)?.takeIf { tag -> tag.hasTag(javadocTag) } ?: lowestClassWithTag( - it, - javadocTag - ) - } - - private fun lowestMethodWithTag( - baseMethod: PsiMethod, - javadocTag: JavadocTag, - ): DocComment? { - val methodsWithTag = lowestMethodsWithTag(baseMethod, javadocTag).firstOrNull() - return methodsWithTag?.let { - it.docComment?.let { JavaDocComment(it) } ?: docCommentFinder.findClosestToElement(it) - } - } - - private fun lowestMethodsWithTag(baseMethod: PsiMethod, javadocTag: JavadocTag): List<PsiMethod> = - baseMethod.findSuperMethods().filter { docCommentFinder.findClosestToElement(it)?.hasTag(javadocTag) == true } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiDocTagParser.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiDocTagParser.kt deleted file mode 100644 index 18c506f6..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiDocTagParser.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers.doctag - -import com.intellij.psi.PsiElement -import com.intellij.psi.javadoc.PsiDocTag -import org.jetbrains.dokka.analysis.java.parsers.CommentResolutionContext -import org.jetbrains.dokka.model.doc.* - -/** - * Parses [PsiElement] of [PsiDocTag] into Dokka's [DocTag] - */ -internal class PsiDocTagParser( - private val inheritDocTagResolver: InheritDocTagResolver -) { - fun parse( - psiElements: Iterable<PsiElement>, - commentResolutionContext: CommentResolutionContext - ): List<DocTag> = parse(asParagraph = false, psiElements, commentResolutionContext) - - fun parseAsParagraph( - psiElements: Iterable<PsiElement>, - commentResolutionContext: CommentResolutionContext - ): List<DocTag> = parse(asParagraph = true, psiElements, commentResolutionContext) - - private fun parse( - asParagraph: Boolean, - psiElements: Iterable<PsiElement>, - commentResolutionContext: CommentResolutionContext - ): List<DocTag> { - val docTagParserContext = DocTagParserContext() - - val psiToHtmlConverter = PsiElementToHtmlConverter(inheritDocTagResolver) - val elementsHtml = psiToHtmlConverter.convert(psiElements, docTagParserContext, commentResolutionContext) - ?: return emptyList() - - val htmlToDocTagConverter = HtmlToDocTagConverter(docTagParserContext) - val html = if (asParagraph) "<p>$elementsHtml</p>" else elementsHtml - return htmlToDocTagConverter.convertToDocTag(html) - } -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiElementToHtmlConverter.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiElementToHtmlConverter.kt deleted file mode 100644 index 07aaacb9..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiElementToHtmlConverter.kt +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.parsers.doctag - -import com.intellij.lexer.JavaDocTokenTypes -import com.intellij.psi.* -import com.intellij.psi.impl.source.javadoc.PsiDocParamRef -import com.intellij.psi.impl.source.tree.LeafPsiElement -import com.intellij.psi.javadoc.PsiDocTagValue -import com.intellij.psi.javadoc.PsiDocToken -import com.intellij.psi.javadoc.PsiInlineDocTag -import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent -import org.jetbrains.dokka.analysis.java.JavadocTag -import org.jetbrains.dokka.analysis.java.doccomment.PsiDocumentationContent -import org.jetbrains.dokka.analysis.java.parsers.CommentResolutionContext -import org.jetbrains.dokka.analysis.java.util.* -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.utilities.htmlEscape - -private const val UNRESOLVED_PSI_ELEMENT = "UNRESOLVED_PSI_ELEMENT" - -private data class HtmlParserState( - val currentJavadocTag: JavadocTag?, - val previousElement: PsiElement? = null, - val openPreTags: Int = 0, - val closedPreTags: Int = 0 -) - -private data class HtmlParsingResult(val newState: HtmlParserState, val parsedLine: String? = null) { - constructor(tag: JavadocTag?) : this(HtmlParserState(tag)) - - operator fun plus(other: HtmlParsingResult): HtmlParsingResult { - return HtmlParsingResult( - newState = other.newState, - parsedLine = listOfNotNull(parsedLine, other.parsedLine).joinToString(separator = "") - ) - } -} - -internal class PsiElementToHtmlConverter( - private val inheritDocTagResolver: InheritDocTagResolver -) { - private val preOpeningTagRegex = "<pre(\\s+.*)?>".toRegex() - private val preClosingTagRegex = "</pre>".toRegex() - - fun convert( - psiElements: Iterable<PsiElement>, - docTagParserContext: DocTagParserContext, - commentResolutionContext: CommentResolutionContext - ): String? { - return WithContext(docTagParserContext, commentResolutionContext) - .convert(psiElements) - } - - private inner class WithContext( - private val docTagParserContext: DocTagParserContext, - private val commentResolutionContext: CommentResolutionContext - ) { - fun convert(psiElements: Iterable<PsiElement>): String? { - val parsingResult = - psiElements.fold(HtmlParsingResult(commentResolutionContext.tag)) { resultAccumulator, psiElement -> - resultAccumulator + parseHtml(psiElement, resultAccumulator.newState) - } - return parsingResult.parsedLine?.trim() - } - - private fun parseHtml(psiElement: PsiElement, state: HtmlParserState): HtmlParsingResult = - when (psiElement) { - is PsiReference -> psiElement.children.fold(HtmlParsingResult(state)) { acc, e -> - acc + parseHtml(e, acc.newState) - } - else -> parseHtmlOfSimpleElement(psiElement, state) - } - - private fun parseHtmlOfSimpleElement(psiElement: PsiElement, state: HtmlParserState): HtmlParsingResult { - val text = psiElement.text - - val openPre = state.openPreTags + preOpeningTagRegex.findAll(text).count() - val closedPre = state.closedPreTags + preClosingTagRegex.findAll(text).count() - val isInsidePre = openPre > closedPre - - val parsed = when (psiElement) { - is PsiInlineDocTag -> psiElement.toHtml(state.currentJavadocTag) - is PsiDocParamRef -> psiElement.toDocumentationLinkString() - is PsiDocTagValue, is LeafPsiElement -> { - psiElement.stringifyElementAsText(isInsidePre, state.previousElement) - } - else -> null - } - val previousElement = if (text.trim() == "") state.previousElement else psiElement - return HtmlParsingResult( - state.copy( - previousElement = previousElement, - closedPreTags = closedPre, - openPreTags = openPre - ), parsed - ) - } - - /** - * Inline tags can be met in the middle of some text. Example of an inline tag usage: - * - * ```java - * Use the {@link #getComponentAt(int, int) getComponentAt} method. - * ``` - */ - private fun PsiInlineDocTag.toHtml(javadocTag: JavadocTag?): String? = - when (this.name) { - "link", "linkplain" -> this.referenceElement() - ?.toDocumentationLinkString(this.dataElements.filterIsInstance<PsiDocToken>().joinToString(" ") { - it.stringifyElementAsText(keepFormatting = false).orEmpty() - }) - - "code" -> "<code data-inline>${dataElementsAsText(this)}</code>" - "literal" -> "<literal>${dataElementsAsText(this)}</literal>" - "index" -> "<index>${this.children.filterIsInstance<PsiDocTagValue>().joinToString { it.text }}</index>" - "inheritDoc" -> { - val inheritDocContent = inheritDocTagResolver.resolveContent(commentResolutionContext) - val html = inheritDocContent?.fold(HtmlParsingResult(javadocTag)) { result, content -> - result + content.toInheritDocHtml(result.newState, docTagParserContext) - }?.parsedLine.orEmpty() - html - } - - else -> this.text - } - - private fun DocumentationContent.toInheritDocHtml( - parserState: HtmlParserState, - docTagParserContext: DocTagParserContext - ): HtmlParsingResult { - // TODO [beresnev] comment - return if (this is PsiDocumentationContent) { - parseHtml(this.psiElement, parserState) - } else { - HtmlParsingResult(parserState, inheritDocTagResolver.convertToHtml(this, docTagParserContext)) - } - } - - private fun dataElementsAsText(tag: PsiInlineDocTag): String { - return tag.dataElements.joinToString("") { - it.stringifyElementAsText(keepFormatting = true).orEmpty() - }.htmlEscape() - } - - private fun PsiElement.toDocumentationLinkString(label: String = ""): String { - val driId = reference?.resolve()?.takeIf { it !is PsiParameter }?.let { - val dri = DRI.from(it) - val id = docTagParserContext.store(dri) - id - } ?: UNRESOLVED_PSI_ELEMENT // TODO [beresnev] log this somewhere maybe? - - // TODO [beresnev] data-dri into a constant - return """<a data-dri="${driId.htmlEscape()}">${label.ifBlank { defaultLabel().text }}</a>""" - } - } -} - -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 - } - } - -private fun PsiDocToken.isLeadingAsterisk() = tokenType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS - -private fun PsiDocToken.isTagName() = tokenType == JavaDocTokenType.DOC_TAG_NAME - -/** - * 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 -} - - diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/CoreCopyPaste.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/CoreCopyPaste.kt deleted file mode 100644 index e875fb82..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/CoreCopyPaste.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.AncestryNode -import org.jetbrains.dokka.model.TypeConstructor - -// TODO [beresnev] copy-pasted -internal fun AncestryNode.typeConstructorsBeingExceptions(): List<TypeConstructor> { - fun traverseSupertypes(ancestry: AncestryNode): List<TypeConstructor> = - listOf(ancestry.typeConstructor) + (ancestry.superclass?.let(::traverseSupertypes) ?: emptyList()) - - return superclass?.let(::traverseSupertypes)?.filter { type -> type.dri.isDirectlyAnException() } ?: emptyList() -} - -// TODO [beresnev] copy-pasted -internal fun DRI.isDirectlyAnException(): Boolean = - toString().let { stringed -> - stringed == "kotlin/Exception///PointingToDeclaration/" || - stringed == "java.lang/Exception///PointingToDeclaration/" - } diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/NoopIntellijLogger.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/NoopIntellijLogger.kt deleted file mode 100644 index e5cb6ef0..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/NoopIntellijLogger.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import com.intellij.openapi.diagnostic.Attachment -import com.intellij.openapi.diagnostic.DefaultLogger -import com.intellij.openapi.diagnostic.Logger - -internal class NoopIntellijLoggerFactory : Logger.Factory { - override fun getLoggerInstance(p0: String): Logger = NoopIntellijLogger -} - -/** - * Ignores all messages passed to it - */ -internal object NoopIntellijLogger : DefaultLogger(null) { - override fun isDebugEnabled(): Boolean = false - override fun isTraceEnabled(): Boolean = false - - override fun debug(message: String?) {} - override fun debug(t: Throwable?) {} - override fun debug(message: String?, t: Throwable?) {} - override fun debug(message: String, vararg details: Any?) {} - override fun debugValues(header: String, values: MutableCollection<*>) {} - - override fun trace(message: String?) {} - override fun trace(t: Throwable?) {} - - override fun info(message: String?) {} - override fun info(message: String?, t: Throwable?) {} - override fun info(t: Throwable) {} - - override fun warn(message: String?, t: Throwable?) {} - override fun warn(message: String?) {} - override fun warn(t: Throwable) {} - - override fun error(message: String?, t: Throwable?, vararg details: String?) {} - override fun error(message: String?) {} - override fun error(message: Any?) {} - override fun error(message: String?, vararg attachments: Attachment?) {} - override fun error(message: String?, t: Throwable?, vararg attachments: Attachment?) {} - override fun error(message: String?, vararg details: String?) {} - override fun error(message: String?, t: Throwable?) {} - override fun error(t: Throwable) {} -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PropertiesConventionUtil.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PropertiesConventionUtil.kt deleted file mode 100644 index de24552e..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PropertiesConventionUtil.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -// TODO [beresnev] copy-paste - -internal fun propertyNamesBySetMethodName(methodName: String): List<String> = - listOfNotNull(propertyNameBySetMethodName(methodName, false), propertyNameBySetMethodName(methodName, true)) - -internal fun propertyNameByGetMethodName(methodName: String): String? = - propertyNameFromAccessorMethodName(methodName, "get") ?: propertyNameFromAccessorMethodName(methodName, "is", removePrefix = false) - -private fun propertyNameBySetMethodName(methodName: String, withIsPrefix: Boolean): String? = - propertyNameFromAccessorMethodName(methodName, "set", addPrefix = if (withIsPrefix) "is" else null) - -private fun propertyNameFromAccessorMethodName( - methodName: String, - prefix: String, - removePrefix: Boolean = true, - addPrefix: String? = null -): String? { - val isSpecial = methodName.startsWith("<") // see special in org.jetbrains.kotlin.Name - if (isSpecial) return null - if (!methodName.startsWith(prefix)) return null - if (methodName.length == prefix.length) return null - if (methodName[prefix.length] in 'a'..'z') return null - - if (addPrefix != null) { - assert(removePrefix) - return addPrefix + methodName.removePrefix(prefix) - } - - if (!removePrefix) return methodName - val name = methodName.removePrefix(prefix).decapitalizeSmartForCompiler(asciiOnly = true) - if (!isValidIdentifier(name)) return null - return name -} - -/** - * "FooBar" -> "fooBar" - * "FOOBar" -> "fooBar" - * "FOO" -> "foo" - * "FOO_BAR" -> "foO_BAR" - */ -private fun String.decapitalizeSmartForCompiler(asciiOnly: Boolean = false): String { - if (isEmpty() || !isUpperCaseCharAt(0, asciiOnly)) return this - - if (length == 1 || !isUpperCaseCharAt(1, asciiOnly)) { - return if (asciiOnly) decapitalizeAsciiOnly() else replaceFirstChar(Char::lowercaseChar) - } - - val secondWordStart = (indices.firstOrNull { !isUpperCaseCharAt(it, asciiOnly) } ?: return toLowerCase(this, asciiOnly)) - 1 - - return toLowerCase(substring(0, secondWordStart), asciiOnly) + substring(secondWordStart) -} - -private fun String.isUpperCaseCharAt(index: Int, asciiOnly: Boolean): Boolean { - val c = this[index] - return if (asciiOnly) c in 'A'..'Z' else c.isUpperCase() -} - -private fun toLowerCase(string: String, asciiOnly: Boolean): String { - return if (asciiOnly) string.toLowerCaseAsciiOnly() else string.lowercase() -} - -private fun toUpperCase(string: String, asciiOnly: Boolean): String { - return if (asciiOnly) string.toUpperCaseAsciiOnly() else string.uppercase() -} - -private fun String.decapitalizeAsciiOnly(): String { - if (isEmpty()) return this - val c = this[0] - return if (c in 'A'..'Z') - c.lowercaseChar() + substring(1) - else - this -} - -private fun String.toLowerCaseAsciiOnly(): String { - val builder = StringBuilder(length) - for (c in this) { - builder.append(if (c in 'A'..'Z') c.lowercaseChar() else c) - } - return builder.toString() -} - -private fun String.toUpperCaseAsciiOnly(): String { - val builder = StringBuilder(length) - for (c in this) { - builder.append(if (c in 'a'..'z') c.uppercaseChar() else c) - } - return builder.toString() -} - -private fun isValidIdentifier(name: String): Boolean { - if (name.isEmpty() || name.startsWith("<")) return false - for (element in name) { - if (element == '.' || element == '/' || element == '\\') { - return false - } - } - return true -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiAccessorConventionUtil.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiAccessorConventionUtil.kt deleted file mode 100644 index 190fc5bd..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiAccessorConventionUtil.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import com.intellij.psi.PsiField -import com.intellij.psi.PsiMethod -import org.jetbrains.dokka.analysis.java.getVisibility -import org.jetbrains.dokka.model.JavaVisibility -import org.jetbrains.dokka.model.KotlinVisibility -import org.jetbrains.dokka.model.Visibility - - -internal data class PsiFunctionsHolder( - val regularFunctions: List<PsiMethod>, - val accessors: Map<PsiField, List<PsiMethod>> -) - -internal fun splitFunctionsAndAccessors(fields: Array<PsiField>, methods: Array<PsiMethod>): PsiFunctionsHolder { - val fieldsByName = fields.associateBy { it.name } - val regularFunctions = mutableListOf<PsiMethod>() - val accessors = mutableMapOf<PsiField, MutableList<PsiMethod>>() - methods.forEach { method -> - val possiblePropertyNamesForFunction = method.getPossiblePropertyNamesForFunction() - val field = possiblePropertyNamesForFunction.firstNotNullOfOrNull { fieldsByName[it] } - if (field != null && method.isAccessorFor(field)) { - accessors.getOrPut(field, ::mutableListOf).add(method) - } else { - regularFunctions.add(method) - } - } - - val accessorLookalikes = removeNonAccessorsReturning(accessors) - regularFunctions.addAll(accessorLookalikes) - - return PsiFunctionsHolder(regularFunctions, accessors) -} - -private fun PsiMethod.getPossiblePropertyNamesForFunction(): List<String> { - val jvmName = getAnnotation("kotlin.jvm.JvmName")?.findAttributeValue("name")?.text - if (jvmName != null) return listOf(jvmName) - - return when { - isGetterName(name) -> listOfNotNull( - propertyNameByGetMethodName(name) - ) - isSetterName(name) -> { - propertyNamesBySetMethodName(name) - } - else -> listOf() - } -} - -private fun isGetterName(name: String): Boolean { - return name.startsWith("get") || name.startsWith("is") -} - -private fun isSetterName(name: String): Boolean { - return name.startsWith("set") -} - -/** - * If a field has no getter, it's not accessible as a property from Kotlin's perspective, - * but it still might have a setter. In this case, this "setter" should be just a regular function - */ -private fun removeNonAccessorsReturning( - fieldAccessors: MutableMap<PsiField, MutableList<PsiMethod>> -): List<PsiMethod> { - val nonAccessors = mutableListOf<PsiMethod>() - fieldAccessors.entries.removeIf { (field, methods) -> - if (methods.size == 1 && methods[0].isSetterFor(field)) { - nonAccessors.add(methods[0]) - true - } else { - false - } - } - return nonAccessors -} - -internal fun PsiMethod.isAccessorFor(field: PsiField): Boolean { - return (this.isGetterFor(field) || this.isSetterFor(field)) - && !field.getVisibility().isPublicAPI() - && this.getVisibility().isPublicAPI() -} - -internal fun PsiMethod.isGetterFor(field: PsiField): Boolean { - return this.returnType == field.type && !this.hasParameters() -} - -internal fun PsiMethod.isSetterFor(field: PsiField): Boolean { - return parameterList.getParameter(0)?.type == field.type && parameterList.getParametersCount() == 1 -} - -private fun Visibility.isPublicAPI() = when(this) { - KotlinVisibility.Public, - KotlinVisibility.Protected, - JavaVisibility.Public, - JavaVisibility.Protected -> true - else -> false -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiCommentsUtils.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiCommentsUtils.kt deleted file mode 100644 index 573f479e..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiCommentsUtils.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import com.intellij.psi.JavaDocTokenType -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiJavaCodeReferenceElement -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.javadoc.PsiDocComment -import com.intellij.psi.javadoc.PsiDocTag -import com.intellij.psi.javadoc.PsiDocToken -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.dokka.analysis.java.DescriptionJavadocTag -import org.jetbrains.dokka.analysis.java.JavadocTag - -internal fun PsiDocComment.hasTag(tag: JavadocTag): Boolean = - when (tag) { - DescriptionJavadocTag -> descriptionElements.isNotEmpty() - else -> findTagByName(tag.name) != null - } - -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() - -internal fun PsiDocTag.referenceElement(): PsiElement? = - linkElement()?.referenceElementOrSelf() - -internal fun PsiElement.referenceElementOrSelf(): PsiElement? = - if (node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { - PsiTreeUtil.findChildOfType(this, PsiJavaCodeReferenceElement::class.java) - } else this - -internal fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - -internal fun PsiElement.defaultLabel() = children.firstOrNull { - it is PsiDocToken && it.text.isNotBlank() && !it.isSharpToken() -} ?: this - -internal fun PsiDocToken.isSharpToken() = tokenType == JavaDocTokenType.DOC_TAG_VALUE_SHARP_TOKEN diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt deleted file mode 100644 index 162f940e..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import com.intellij.psi.* -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.dokka.InternalDokkaApi -import org.jetbrains.dokka.links.* -import org.jetbrains.dokka.model.DocumentableSource -import org.jetbrains.dokka.utilities.firstIsInstanceOrNull - -// TODO [beresnev] copy-paste - -internal val PsiElement.parentsWithSelf: Sequence<PsiElement> - get() = generateSequence(this) { if (it is PsiFile) null else it.parent } - -@InternalDokkaApi -public fun DRI.Companion.from(psi: PsiElement): DRI = psi.parentsWithSelf.run { - val psiMethod = firstIsInstanceOrNull<PsiMethod>() - val psiField = firstIsInstanceOrNull<PsiField>() - val classes = filterIsInstance<PsiClass>().filterNot { it is PsiTypeParameter } - .toList() // We only want exact PsiClass types, not PsiTypeParameter subtype - val additionalClasses = if (psi is PsiEnumConstant) listOfNotNull(psiField?.name) else emptyList() - DRI( - packageName = classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', "") ?: "", - classNames = (additionalClasses + classes.mapNotNull { it.name }).takeIf { it.isNotEmpty() } - ?.asReversed()?.joinToString("."), - // The fallback strategy test whether psi is not `PsiEnumConstant`. The reason behind this is that - // we need unified DRI for both Java and Kotlin enums, so we can link them properly and treat them alike. - // To achieve that, we append enum name to classNames list and leave the callable part set to null. For Kotlin enums - // it is by default, while for Java enums we have to explicitly test for that in this `takeUnless` condition. - callable = psiMethod?.let { Callable.from(it) } ?: psiField?.takeUnless { psi is PsiEnumConstant }?.let { Callable.from(it) }, - target = DriTarget.from(psi), - extra = if (psi is PsiEnumConstant) - DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() - else null - ) -} - -internal fun Callable.Companion.from(psi: PsiMethod) = with(psi) { - Callable( - name, - null, - parameterList.parameters.map { param -> JavaClassReference(param.type.canonicalText) }) -} - -internal fun Callable.Companion.from(psi: PsiField): Callable { - return Callable( - name = psi.name, - receiver = null, - params = emptyList() - ) -} - -internal fun DriTarget.Companion.from(psi: PsiElement): DriTarget = psi.parentsWithSelf.run { - return when (psi) { - is PsiTypeParameter -> PointingToGenericParameters(psi.index) - else -> firstIsInstanceOrNull<PsiParameter>()?.let { - val callable = firstIsInstanceOrNull<PsiMethod>() - val params = (callable?.parameterList?.parameters).orEmpty() - PointingToCallableParameters(params.indexOf(it)) - } ?: PointingToDeclaration - } -} - -// TODO [beresnev] copy-paste -internal fun PsiElement.siblings(forward: Boolean = true, withItself: Boolean = true): Sequence<PsiElement> { - return object : Sequence<PsiElement> { - override fun iterator(): Iterator<PsiElement> { - var next: PsiElement? = this@siblings - return object : Iterator<PsiElement> { - init { - if (!withItself) next() - } - - override fun hasNext(): Boolean = next != null - override fun next(): PsiElement { - val result = next ?: throw NoSuchElementException() - next = if (forward) result.nextSibling else result.prevSibling - return result - } - } - } - } -} - -// TODO [beresnev] copy-paste -internal fun PsiElement.getNextSiblingIgnoringWhitespace(withItself: Boolean = false): PsiElement? { - return siblings(withItself = withItself).filter { it !is PsiWhiteSpace }.firstOrNull() -} - -@InternalDokkaApi -public class PsiDocumentableSource( - public val psi: PsiNamedElement -) : DocumentableSource { - override val path: String = psi.containingFile.virtualFile.path - - override fun computeLineNumber(): Int? { - val range = psi.getChildOfType<PsiIdentifier>()?.textRange ?: psi.textRange - val doc = PsiDocumentManager.getInstance(psi.project).getDocument(psi.containingFile) - // IJ uses 0-based line-numbers; external source browsers use 1-based - return doc?.getLineNumber(range.startOffset)?.plus(1) - } -} - -public inline fun <reified T : PsiElement> PsiElement.getChildOfType(): T? { - return PsiTreeUtil.getChildOfType(this, T::class.java) -} - -internal fun PsiElement.getKotlinFqName(): String? = this.kotlinFqNameProp - -//// from import org.jetbrains.kotlin.idea.base.psi.kotlinFqName -internal val PsiElement.kotlinFqNameProp: String? - get() = when (val element = this) { - is PsiPackage -> element.qualifiedName - is PsiClass -> element.qualifiedName - is PsiMember -> element.name?.let { name -> - val prefix = element.containingClass?.qualifiedName - if (prefix != null) "$prefix.$name" else name - } -// is KtNamedDeclaration -> element.fqName TODO [beresnev] decide what to do with it - is PsiQualifiedNamedElement -> element.qualifiedName - else -> null - } diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/StdlibUtil.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/StdlibUtil.kt deleted file mode 100644 index 00658ce9..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/StdlibUtil.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import java.util.* - -// TODO [beresnev] copy-paste - -// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 -internal fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R? { - for (element in this) { - val result = transform(element) - if (result != null) { - return result - } - } - return null -} - -// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 -internal fun Char.uppercaseChar(): Char = Character.toUpperCase(this) - -// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 -internal fun Char.lowercaseChar(): Char = Character.toLowerCase(this) - -// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 -internal fun String.lowercase(): String = this.toLowerCase(Locale.ROOT) - -// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 -internal fun String.uppercase(): String = this.toUpperCase(Locale.ROOT) - -// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 -internal fun String.replaceFirstChar(transform: (Char) -> Char): String { - return if (isNotEmpty()) transform(this[0]) + substring(1) else this -} diff --git a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/resolveToGetDri.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/resolveToGetDri.kt deleted file mode 100644 index aabe57c0..00000000 --- a/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/resolveToGetDri.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.analysis.java.util - -import com.intellij.psi.PsiElement - -// TODO [beresnev] get rid of -internal fun PsiElement.resolveToGetDri(): PsiElement? = - reference?.resolve() diff --git a/subprojects/analysis-java-psi/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-java-psi/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin deleted file mode 100644 index 9fa991c2..00000000 --- a/subprojects/analysis-java-psi/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin +++ /dev/null @@ -1,5 +0,0 @@ -# -# Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. -# - -org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin |