diff options
author | Ignat Beresnev <ignat.beresnev@jetbrains.com> | 2023-07-05 10:04:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-05 10:04:55 +0200 |
commit | 9559158bfeeb274e9ccf1b4563f1b23b42afc493 (patch) | |
tree | 3ece0887623cfe2b7148af23001867a1dd5e6597 | |
parent | cbd9733d3dd2f52992e98e7cebd072091a572529 (diff) | |
download | dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.gz dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.tar.bz2 dokka-9559158bfeeb274e9ccf1b4563f1b23b42afc493.zip |
Decompose Kotlin/Java analysis (#3034)
* Extract analysis into separate modules
341 files changed, 6268 insertions, 4313 deletions
diff --git a/build.gradle.kts b/build.gradle.kts index 689e9215..d3b965bd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,10 +41,6 @@ apiValidation { // NAME PATH "frontend", // :plugins:base:frontend - "kotlin-analysis", // :kotlin-analysis - "compiler-dependency", // :kotlin-analysis:compiler-dependency - "intellij-dependency", // :kotlin-analysis:intellij-dependency - "integration-tests", // :integration-tests "gradle", // :integration-tests:gradle "cli", // :integration-tests:cli diff --git a/core/api/core.api b/core/api/core.api index ac48cfd5..9530899d 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -1459,6 +1459,7 @@ public final class org/jetbrains/dokka/model/DocumentableKt { } public abstract interface class org/jetbrains/dokka/model/DocumentableSource { + public abstract fun computeLineNumber ()Ljava/lang/Integer; public abstract fun getPath ()Ljava/lang/String; } @@ -1782,6 +1783,24 @@ public final class org/jetbrains/dokka/model/Invariance : org/jetbrains/dokka/mo public fun toString ()Ljava/lang/String; } +public final class org/jetbrains/dokka/model/IsAlsoParameter : org/jetbrains/dokka/model/properties/ExtraProperty { + public static final field Companion Lorg/jetbrains/dokka/model/IsAlsoParameter$Companion; + public fun <init> (Ljava/util/List;)V + public final fun component1 ()Ljava/util/List; + public final fun copy (Ljava/util/List;)Lorg/jetbrains/dokka/model/IsAlsoParameter; + public static synthetic fun copy$default (Lorg/jetbrains/dokka/model/IsAlsoParameter;Ljava/util/List;ILjava/lang/Object;)Lorg/jetbrains/dokka/model/IsAlsoParameter; + public fun equals (Ljava/lang/Object;)Z + public final fun getInSourceSets ()Ljava/util/List; + public fun getKey ()Lorg/jetbrains/dokka/model/properties/ExtraProperty$Key; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/model/IsAlsoParameter$Companion : org/jetbrains/dokka/model/properties/ExtraProperty$Key { + public synthetic fun mergeStrategyFor (Ljava/lang/Object;Ljava/lang/Object;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; + public fun mergeStrategyFor (Lorg/jetbrains/dokka/model/IsAlsoParameter;Lorg/jetbrains/dokka/model/IsAlsoParameter;)Lorg/jetbrains/dokka/model/properties/MergeStrategy; +} + public final class org/jetbrains/dokka/model/IsVar : org/jetbrains/dokka/model/properties/ExtraProperty, org/jetbrains/dokka/model/properties/ExtraProperty$Key { public static final field INSTANCE Lorg/jetbrains/dokka/model/IsVar; public fun getKey ()Lorg/jetbrains/dokka/model/properties/ExtraProperty$Key; @@ -4400,6 +4419,7 @@ public abstract class org/jetbrains/dokka/plugability/DokkaPlugin { protected final fun extending (Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/dokka/plugability/DokkaPlugin$ExtensionProvider; protected final fun extensionPoint ()Lkotlin/properties/ReadOnlyProperty; public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; + protected final fun getLogger ()Lorg/jetbrains/dokka/utilities/DokkaLogger; protected abstract fun pluginApiPreviewAcknowledgement ()Lorg/jetbrains/dokka/plugability/PluginApiPreviewAcknowledgement; public final fun setContext (Lorg/jetbrains/dokka/plugability/DokkaContext;)V protected final fun unsafeInstall (Lkotlin/Lazy;)V diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1afed43d..2a5b668c 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -7,11 +7,8 @@ plugins { } dependencies { - api(libs.jetbrains.markdown) implementation(kotlin("reflect")) - - implementation(libs.jsoup) - + implementation(libs.kotlinx.coroutines.core) implementation(libs.jackson.kotlin) implementation(libs.jackson.xml) constraints { @@ -20,8 +17,6 @@ dependencies { } } - implementation(libs.kotlinx.coroutines.core) - testImplementation(projects.core.testApi) testImplementation(kotlin("test-junit")) } diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt index ddfa0c69..8b5196c3 100644 --- a/core/src/main/kotlin/CoreExtensions.kt +++ b/core/src/main/kotlin/CoreExtensions.kt @@ -1,7 +1,7 @@ package org.jetbrains.dokka import org.jetbrains.dokka.generation.Generation -import org.jetbrains.dokka.plugability.* +import org.jetbrains.dokka.plugability.ExtensionPoint import org.jetbrains.dokka.renderers.PostAction import org.jetbrains.dokka.renderers.Renderer import org.jetbrains.dokka.transformers.documentation.DocumentableMerger @@ -28,4 +28,4 @@ object CoreExtensions { operator fun provideDelegate(thisRef: CoreExtensions, property: KProperty<*>): Lazy<ExtensionPoint<T>> = lazy { ExtensionPoint(thisRef::class.qualifiedName!!, property.name) } } -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/DokkaBootstrap.kt b/core/src/main/kotlin/DokkaBootstrap.kt index f4533d5b..10a91c5e 100644 --- a/core/src/main/kotlin/DokkaBootstrap.kt +++ b/core/src/main/kotlin/DokkaBootstrap.kt @@ -1,7 +1,6 @@ package org.jetbrains.dokka import java.util.function.BiConsumer -import kotlin.jvm.Throws interface DokkaBootstrap { @Throws(Throwable::class) diff --git a/core/src/main/kotlin/InternalDokkaApi.kt b/core/src/main/kotlin/InternalDokkaApi.kt index 4389bf7b..0582d350 100644 --- a/core/src/main/kotlin/InternalDokkaApi.kt +++ b/core/src/main/kotlin/InternalDokkaApi.kt @@ -16,6 +16,9 @@ package org.jetbrains.dokka level = RequiresOptIn.Level.ERROR, message = "This is an internal Dokka API not intended for public use" ) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.FIELD) +@Target( + AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.FIELD, + AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS +) @Retention(AnnotationRetention.BINARY) public annotation class InternalDokkaApi() diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt index c7b2290e..64c3e14c 100644 --- a/core/src/main/kotlin/model/Documentable.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -518,6 +518,15 @@ fun <T> SourceSetDependent<T>?.orEmpty(): SourceSetDependent<T> = this ?: emptyM interface DocumentableSource { val path: String + + /** + * Computes the first line number of the documentable's declaration/signature/identifier. + * + * Numbering is always 1-based. + * + * May return null if the sources could not be found - for example, for synthetic/generated declarations. + */ + fun computeLineNumber(): Int? } -data class TypeConstructorWithKind(val typeConstructor: TypeConstructor, val kind: ClassKind)
\ No newline at end of file +data class TypeConstructorWithKind(val typeConstructor: TypeConstructor, val kind: ClassKind) diff --git a/core/src/main/kotlin/model/WithChildren.kt b/core/src/main/kotlin/model/WithChildren.kt index 01a188c8..7412971a 100644 --- a/core/src/main/kotlin/model/WithChildren.kt +++ b/core/src/main/kotlin/model/WithChildren.kt @@ -91,4 +91,4 @@ fun <T : WithChildren<T>> T.asPrintableTree( } return buildString { append(this@asPrintableTree, "", "") } -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/model/documentableProperties.kt b/core/src/main/kotlin/model/documentableProperties.kt index 9fe8aa1d..4e743b0a 100644 --- a/core/src/main/kotlin/model/documentableProperties.kt +++ b/core/src/main/kotlin/model/documentableProperties.kt @@ -49,6 +49,15 @@ object IsVar : ExtraProperty<DProperty>, ExtraProperty.Key<DProperty, IsVar> { override val key: ExtraProperty.Key<DProperty, *> = this } +data class IsAlsoParameter(val inSourceSets: List<DokkaSourceSet>) : ExtraProperty<DProperty> { + companion object : ExtraProperty.Key<DProperty, IsAlsoParameter> { + override fun mergeStrategyFor(left: IsAlsoParameter, right: IsAlsoParameter): MergeStrategy<DProperty> = + MergeStrategy.Replace(IsAlsoParameter(left.inSourceSets + right.inSourceSets)) + } + + override val key: ExtraProperty.Key<DProperty, *> = IsAlsoParameter +} + data class CheckedExceptions(val exceptions: SourceSetDependent<List<DRI>>) : ExtraProperty<Documentable>, ExtraProperty.Key<Documentable, ObviousMember> { companion object : ExtraProperty.Key<Documentable, CheckedExceptions> { override fun mergeStrategyFor(left: CheckedExceptions, right: CheckedExceptions) = diff --git a/core/src/main/kotlin/plugability/DokkaPlugin.kt b/core/src/main/kotlin/plugability/DokkaPlugin.kt index 54733ac2..99fa4b95 100644 --- a/core/src/main/kotlin/plugability/DokkaPlugin.kt +++ b/core/src/main/kotlin/plugability/DokkaPlugin.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule import com.fasterxml.jackson.dataformat.xml.XmlMapper import com.fasterxml.jackson.module.kotlin.readValue import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.utilities.parseJson import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -33,6 +34,8 @@ abstract class DokkaPlugin { @PublishedApi internal var context: DokkaContext? = null + protected val logger: DokkaLogger get() = context?.logger ?: throw IllegalStateException("No logger found") + /** * @see PluginApiPreviewAcknowledgement */ diff --git a/core/src/main/kotlin/utilities/Collections.kt b/core/src/main/kotlin/utilities/Collections.kt new file mode 100644 index 00000000..b18752f7 --- /dev/null +++ b/core/src/main/kotlin/utilities/Collections.kt @@ -0,0 +1,25 @@ +package org.jetbrains.dokka.utilities + +import org.jetbrains.dokka.InternalDokkaApi + +/** + * This utility method was previously imported from `org.jetbrains.kotlin.utils.addToStdlib`, + * and there were a lot of usages. Since no replacement exists in stdlib, it was implemented + * locally for convenience. + */ +@InternalDokkaApi +inline fun <reified T : Any> Iterable<*>.firstIsInstanceOrNull(): T? { + for (element in this) if (element is T) return element + return null +} + +/** + * This utility method was previously imported from `org.jetbrains.kotlin.utils.addToStdlib`, + * and there were a lot of usages. Since no replacement exists in stdlib, it was implemented + * locally for convenience. + */ +@InternalDokkaApi +inline fun <reified T : Any> Sequence<*>.firstIsInstanceOrNull(): T? { + for (element in this) if (element is T) return element + return null +} diff --git a/core/src/main/kotlin/utilities/Html.kt b/core/src/main/kotlin/utilities/Html.kt index a1d8ecec..262dd0a0 100644 --- a/core/src/main/kotlin/utilities/Html.kt +++ b/core/src/main/kotlin/utilities/Html.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.InternalDokkaApi import java.net.URLEncoder diff --git a/core/src/main/kotlin/utilities/SelfRepresentingSingletonSet.kt b/core/src/main/kotlin/utilities/SelfRepresentingSingletonSet.kt index c29d1b2a..e1b42388 100644 --- a/core/src/main/kotlin/utilities/SelfRepresentingSingletonSet.kt +++ b/core/src/main/kotlin/utilities/SelfRepresentingSingletonSet.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.InternalDokkaApi @InternalDokkaApi interface SelfRepresentingSingletonSet<T : SelfRepresentingSingletonSet<T>> : Set<T> { diff --git a/core/src/main/kotlin/utilities/ServiceLocator.kt b/core/src/main/kotlin/utilities/ServiceLocator.kt index f86960ec..b5b19057 100644 --- a/core/src/main/kotlin/utilities/ServiceLocator.kt +++ b/core/src/main/kotlin/utilities/ServiceLocator.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.InternalDokkaApi import java.io.File import java.net.URISyntaxException import java.net.URL diff --git a/core/src/main/kotlin/utilities/Uri.kt b/core/src/main/kotlin/utilities/Uri.kt index 67c81d98..ef8549f7 100644 --- a/core/src/main/kotlin/utilities/Uri.kt +++ b/core/src/main/kotlin/utilities/Uri.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.InternalDokkaApi import java.net.URI @InternalDokkaApi diff --git a/core/src/main/kotlin/utilities/associateWithNotNull.kt b/core/src/main/kotlin/utilities/associateWithNotNull.kt index 6c0bf4d8..9ff55d2c 100644 --- a/core/src/main/kotlin/utilities/associateWithNotNull.kt +++ b/core/src/main/kotlin/utilities/associateWithNotNull.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.InternalDokkaApi @InternalDokkaApi inline fun <K, V : Any> Iterable<K>.associateWithNotNull(valueSelector: (K) -> V?): Map<K, V> { diff --git a/core/src/main/kotlin/utilities/cast.kt b/core/src/main/kotlin/utilities/cast.kt index 784b7e2a..9fe76ef6 100644 --- a/core/src/main/kotlin/utilities/cast.kt +++ b/core/src/main/kotlin/utilities/cast.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.InternalDokkaApi @InternalDokkaApi inline fun <reified T> Any.cast(): T { diff --git a/core/src/main/kotlin/utilities/parallelCollectionOperations.kt b/core/src/main/kotlin/utilities/parallelCollectionOperations.kt index d24aa7a6..cff8d735 100644 --- a/core/src/main/kotlin/utilities/parallelCollectionOperations.kt +++ b/core/src/main/kotlin/utilities/parallelCollectionOperations.kt @@ -1,7 +1,10 @@ package org.jetbrains.dokka.utilities -import kotlinx.coroutines.* -import org.jetbrains.dokka.* +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import org.jetbrains.dokka.InternalDokkaApi @InternalDokkaApi suspend inline fun <A, B> Iterable<A>.parallelMap(crossinline f: suspend (A) -> B): List<B> = coroutineScope { diff --git a/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt b/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt index 9ac5fea0..d93ea5df 100644 --- a/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt +++ b/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt @@ -1,6 +1,9 @@ package utilities -import org.jetbrains.dokka.* +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.DokkaSourceSetImpl +import org.jetbrains.dokka.toCompactJsonString import java.io.File import kotlin.test.Test import kotlin.test.assertEquals diff --git a/core/src/test/kotlin/utilities/JsonKtTest.kt b/core/src/test/kotlin/utilities/JsonKtTest.kt index ee78392c..301f4f5d 100644 --- a/core/src/test/kotlin/utilities/JsonKtTest.kt +++ b/core/src/test/kotlin/utilities/JsonKtTest.kt @@ -2,8 +2,8 @@ package utilities import org.jetbrains.dokka.utilities.serializeAsCompactJson import org.jetbrains.dokka.utilities.serializeAsPrettyJson -import kotlin.test.assertEquals import kotlin.test.Test +import kotlin.test.assertEquals class JsonTest { diff --git a/core/test-api/build.gradle.kts b/core/test-api/build.gradle.kts index 1eb8f00c..6b4b4d17 100644 --- a/core/test-api/build.gradle.kts +++ b/core/test-api/build.gradle.kts @@ -7,7 +7,7 @@ plugins { dependencies { api(projects.core) - implementation(projects.kotlinAnalysis) + implementation("junit:junit:4.13.2") // TODO: remove dependency to junit implementation(kotlin("reflect")) } diff --git a/core/test-api/src/main/kotlin/testApi/logger/TestLogger.kt b/core/test-api/src/main/kotlin/testApi/logger/TestLogger.kt index e70200a7..1e301735 100644 --- a/core/test-api/src/main/kotlin/testApi/logger/TestLogger.kt +++ b/core/test-api/src/main/kotlin/testApi/logger/TestLogger.kt @@ -1,7 +1,7 @@ package org.jetbrains.dokka.testApi.logger import org.jetbrains.dokka.utilities.DokkaLogger -import java.util.Collections +import java.util.* /* * Even in tests it be used in a concurrent environment, so needs to be thread safe diff --git a/core/test-api/src/main/kotlin/testApi/testRunner/TestDokkaConfigurationBuilder.kt b/core/test-api/src/main/kotlin/testApi/testRunner/TestDokkaConfigurationBuilder.kt index 50ab3bad..c63f5b2e 100644 --- a/core/test-api/src/main/kotlin/testApi/testRunner/TestDokkaConfigurationBuilder.kt +++ b/core/test-api/src/main/kotlin/testApi/testRunner/TestDokkaConfigurationBuilder.kt @@ -1,6 +1,5 @@ package testApi.testRunner -import org.intellij.markdown.MarkdownElementTypes import org.jetbrains.dokka.* import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* @@ -206,6 +205,5 @@ fun dPackage( fun documentationNode(vararg texts: String): DocumentationNode { return DocumentationNode( texts.toList() - .map { Description(CustomDocTag(listOf(Text(it)), name = MarkdownElementTypes.MARKDOWN_FILE.name)) }) + .map { Description(CustomDocTag(listOf(Text(it)), name = "MARKDOWN_FILE")) }) } - diff --git a/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt b/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt index 31443b2d..cfb809ea 100644 --- a/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt +++ b/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt @@ -1,7 +1,8 @@ package org.jetbrains.dokka.testApi.testRunner -import com.intellij.openapi.application.PathManager -import org.jetbrains.dokka.* +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.ExternalDocumentationLinkImpl import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext @@ -37,7 +38,7 @@ abstract class AbstractTest<M : TestMethods, T : TestBuilder<M>, D : DokkaTestGe cleanupOutput: Boolean = true, useOutputLocationFromConfig: Boolean = false, pluginOverrides: List<DokkaPlugin> = emptyList(), - block: T.() -> Unit + block: T.() -> Unit, ) { val testMethods = testBuilder().apply(block).build() val configurationToUse = @@ -65,7 +66,7 @@ abstract class AbstractTest<M : TestMethods, T : TestBuilder<M>, D : DokkaTestGe cleanupOutput: Boolean = true, pluginOverrides: List<DokkaPlugin> = emptyList(), loggerForTest: DokkaLogger = logger, - block: T.() -> Unit + block: T.() -> Unit, ) { val testMethods = testBuilder().apply(block).build() val testDirPath = getTempDir(cleanupOutput).root.toPath().toAbsolutePath() @@ -133,7 +134,7 @@ abstract class AbstractTest<M : TestMethods, T : TestBuilder<M>, D : DokkaTestGe private fun Map<String, String>.materializeFiles( root: Path = Paths.get("."), - charset: Charset = Charset.forName("utf-8") + charset: Charset = Charset.forName("utf-8"), ) = this.map { (path, content) -> val file = root.resolve(path) Files.createDirectories(file.parent) @@ -160,11 +161,17 @@ abstract class AbstractTest<M : TestMethods, T : TestBuilder<M>, D : DokkaTestGe protected val jvmStdlibPath: String? by lazy { - PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class") + ClassLoader.getSystemResource("kotlin/jvm/Strictfp.class") + ?.file + ?.replace("file:", "") + ?.replaceAfter(".jar", "") } protected val jsStdlibPath: String? by lazy { - PathManager.getResourceRoot(Any::class.java, "/kotlin/jquery") + ClassLoader.getSystemResource("kotlin/jquery") + ?.file + ?.replace("file:", "") + ?.replaceAfter(".jar", "") } protected val commonStdlibPath: String? by lazy { @@ -195,7 +202,7 @@ open class CoreTestMethods( open val documentablesTransformationStage: (DModule) -> Unit, open val pagesGenerationStage: (RootPageNode) -> Unit, open val pagesTransformationStage: (RootPageNode) -> Unit, - open val renderingStage: (RootPageNode, DokkaContext) -> Unit + open val renderingStage: (RootPageNode, DokkaContext) -> Unit, ) : TestMethods abstract class TestBuilder<M : TestMethods> { @@ -206,7 +213,7 @@ abstract class DokkaTestGenerator<T : TestMethods>( protected val configuration: DokkaConfiguration, protected val logger: DokkaLogger, protected val testMethods: T, - protected val additionalPlugins: List<DokkaPlugin> = emptyList() + protected val additionalPlugins: List<DokkaPlugin> = emptyList(), ) { abstract fun generate() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 77374d87..6194d0a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,13 @@ kotlinx-bcv = "0.12.1" ## Analysis kotlin-compiler = "1.8.10" kotlin-ide-plugin = "213-1.8.10-release-430-IJ6777.52" -intellij = "213.6777.52" +kotlin-jps-common = "213-1.6.21-release-334-IJ6777.52" # TODO [beresnev] not sure which version should be used exactly + +# MUST match the version of the intellij platform used in the kotlin compiler, +# otherwise this will lead to different versions of psi API and implementations +# on the classpath and will fail with hard to debug problems in runtime. +# See: https://github.com/JetBrains/kotlin/blob/e6633d3d9214402fcf3585ae8c24213a4761cc8b/gradle/versions.properties#L1 +intellij-platform = "203.8084.24" ## HTML jsoup = "1.15.3" @@ -65,10 +71,13 @@ kotlin-idePlugin-common = { module = "org.jetbrains.kotlin:common", version.ref kotlin-idePlugin-idea = { module = "org.jetbrains.kotlin:idea", version.ref = "kotlin-ide-plugin" } kotlin-idePlugin-core = { module = "org.jetbrains.kotlin:core", version.ref = "kotlin-ide-plugin" } kotlin-idePlugin-native = { module = "org.jetbrains.kotlin:native", version.ref = "kotlin-ide-plugin" } +kotlin-jps-common = { module = "org.jetbrains.kotlin:jps-common", version.ref = "kotlin-jps-common" } #### Java analysis #### -jetbrains-intellij-core = { module = "com.jetbrains.intellij.idea:intellij-core", version.ref = "intellij" } -jetbrains-intellij-jpsStandalone = { module = "com.jetbrains.intellij.idea:jps-standalone", version.ref = "intellij" } +intellij-java-psi-api = { module = "com.jetbrains.intellij.java:java-psi", version.ref = "intellij-platform" } +intellij-java-psi-impl = { module = "com.jetbrains.intellij.java:java-psi-impl", version.ref = "intellij-platform" } +intellij-platform-util-api = { module = "com.jetbrains.intellij.platform:util", version.ref = "intellij-platform" } +intellij-platform-util-rt = { module = "com.jetbrains.intellij.platform:util-rt", version.ref = "intellij-platform" } #### HTML #### jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } diff --git a/integration-tests/cli/build.gradle.kts b/integration-tests/cli/build.gradle.kts index 4e498df1..e20529a4 100644 --- a/integration-tests/cli/build.gradle.kts +++ b/integration-tests/cli/build.gradle.kts @@ -23,13 +23,19 @@ val basePluginShadow: Configuration by configurations.creating { dependencies { basePluginShadow(projects.plugins.base) - basePluginShadow(projects.kotlinAnalysis) // compileOnly in base plugin + + // TODO [beresnev] analysis switcher + basePluginShadow(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow")) } val basePluginShadowJar by tasks.register("basePluginShadowJar", ShadowJar::class) { configurations = listOf(basePluginShadow) archiveFileName.set("fat-base-plugin-$dokka_version.jar") archiveClassifier.set("") + + // service files are merged to make sure all Dokka plugins + // from the dependencies are loaded, and not just a single one. + mergeServiceFiles() } tasks.integrationTest { diff --git a/kotlin-analysis/build.gradle.kts b/kotlin-analysis/build.gradle.kts deleted file mode 100644 index a5908b4e..00000000 --- a/kotlin-analysis/build.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -import org.jetbrains.registerDokkaArtifactPublication - -plugins { - id("org.jetbrains.conventions.kotlin-jvm") - id("org.jetbrains.conventions.maven-publish") - id("com.github.johnrengelman.shadow") -} - -dependencies { - compileOnly(projects.core) - api(project("intellij-dependency", configuration = "shadow")) - api(project("compiler-dependency", configuration = "shadow")) -} - -registerDokkaArtifactPublication("dokkaAnalysis") { - artifactId = "dokka-analysis" -} diff --git a/kotlin-analysis/compiler-dependency/build.gradle.kts b/kotlin-analysis/compiler-dependency/build.gradle.kts deleted file mode 100644 index 5d0a01e0..00000000 --- a/kotlin-analysis/compiler-dependency/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -import org.jetbrains.DokkaPublicationBuilder.Component.Shadow -import org.jetbrains.registerDokkaArtifactPublication - -plugins { - id("org.jetbrains.conventions.kotlin-jvm") - id("org.jetbrains.conventions.maven-publish") - id("com.github.johnrengelman.shadow") -} - -dependencies { - api(libs.kotlin.compiler) -} - -tasks { - shadowJar { - val dokka_version: String by project - archiveFileName.set("dokka-kotlin-analysis-compiler-$dokka_version.jar") - archiveClassifier.set("") - exclude("com/intellij/") - } -} - -registerDokkaArtifactPublication("kotlinAnalysisCompiler") { - artifactId = "kotlin-analysis-compiler" - component = Shadow -} diff --git a/kotlin-analysis/intellij-dependency/build.gradle.kts b/kotlin-analysis/intellij-dependency/build.gradle.kts deleted file mode 100644 index af099902..00000000 --- a/kotlin-analysis/intellij-dependency/build.gradle.kts +++ /dev/null @@ -1,75 +0,0 @@ -import org.jetbrains.DokkaPublicationBuilder.Component.Shadow -import org.jetbrains.registerDokkaArtifactPublication - -plugins { - id("org.jetbrains.conventions.kotlin-jvm") - id("org.jetbrains.conventions.maven-publish") - id("com.github.johnrengelman.shadow") -} - -repositories { - // Override the shared repositories defined in the root settings.gradle.kts - // These repositories are very specific and are not needed in other projects - mavenCentral() - maven("https://www.jetbrains.com/intellij-repository/snapshots") { - mavenContent { snapshotsOnly() } - } - maven("https://www.jetbrains.com/intellij-repository/releases") - maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide") - maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide-plugin-dependencies") - maven("https://cache-redirector.jetbrains.com/intellij-dependencies") - maven("https://www.myget.org/F/rd-snapshots/maven/") -} - -val intellijCore: Configuration by configurations.creating - -fun intellijCoreAnalysis() = zipTree(intellijCore.singleFile).matching { - include("intellij-core.jar") -} - -val jpsStandalone: Configuration by configurations.creating - -fun jpsModel() = zipTree(jpsStandalone.singleFile).matching { - include("jps-model.jar") - include("aalto-xml-*.jar") -} - -dependencies { - api(libs.kotlin.idePlugin.common) - api(libs.kotlin.idePlugin.idea) { - isTransitive = false - } - api(libs.kotlin.idePlugin.core) - api(libs.kotlin.idePlugin.native) - - @Suppress("UnstableApiUsage") - intellijCore(libs.jetbrains.intellij.core) - implementation(intellijCoreAnalysis()) - - @Suppress("UnstableApiUsage") - jpsStandalone(libs.jetbrains.intellij.jpsStandalone) - implementation(jpsModel()) -} - -tasks { - shadowJar { - val dokka_version: String by project - archiveFileName.set("dokka-kotlin-analysis-intellij-$dokka_version.jar") - archiveClassifier.set("") - - exclude("colorScheme/**") - exclude("fileTemplates/**") - exclude("inspectionDescriptions/**") - exclude("intentionDescriptions/**") - exclude("tips/**") - exclude("messages/**") - exclude("src/**") - exclude("**/*.kotlin_metadata") - exclude("**/*.kotlin_builtins") - } -} - -registerDokkaArtifactPublication("kotlinAnalysisIntelliJ") { - artifactId = "kotlin-analysis-intellij" - component = Shadow -} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AbsolutePathString.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AbsolutePathString.kt deleted file mode 100644 index 7c8b6840..00000000 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AbsolutePathString.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.jetbrains.dokka.analysis - -internal typealias AbsolutePathString = String diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt deleted file mode 100644 index 0c55fed4..00000000 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.jetbrains.dokka.analysis - -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.model.DocumentableSource -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.load.kotlin.toSourceElement - -class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource { - override val path = descriptor.toSourceElement.containingFile.toString() -} - -class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource { - override val path = psi.containingFile.virtualFile.path -} diff --git a/plugins/all-modules-page/build.gradle.kts b/plugins/all-modules-page/build.gradle.kts index d0778dc5..191f2433 100644 --- a/plugins/all-modules-page/build.gradle.kts +++ b/plugins/all-modules-page/build.gradle.kts @@ -11,27 +11,20 @@ registerDokkaArtifactPublication("dokkaAllModulesPage") { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) + compileOnly(projects.subprojects.analysisKotlinApi) - compileOnly(projects.kotlinAnalysis) implementation(projects.plugins.base) implementation(projects.plugins.templating) + + implementation(projects.subprojects.analysisMarkdownJb) + + implementation(libs.kotlinx.html) + testImplementation(projects.plugins.base) testImplementation(projects.plugins.base.baseTestUtils) testImplementation(projects.plugins.gfm) testImplementation(projects.plugins.gfm.gfmTemplateProcessing) testImplementation(projects.core.contentMatcherTestUtils) - - implementation(libs.kotlinx.coroutines.core) - implementation(libs.jackson.kotlin) - constraints { - implementation(libs.jackson.databind) { - because("CVE-2022-42003") - } - } - implementation(libs.kotlinx.html) - implementation(libs.jsoup) - testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) diff --git a/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt b/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt index 2901f361..e0092fcd 100644 --- a/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt +++ b/plugins/all-modules-page/src/main/kotlin/MultimodulePageCreator.kt @@ -2,12 +2,8 @@ package org.jetbrains.dokka.allModulesPage import org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.parsers.MarkdownParser -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.Module -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentationParsingContext -import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentation -import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentationFragments import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder @@ -22,6 +18,7 @@ import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.pages.PageCreator import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin import java.io.File class MultimodulePageCreator( @@ -31,6 +28,7 @@ class MultimodulePageCreator( private val commentsConverter by lazy { context.plugin<DokkaBase>().querySingle { commentsToContentConverter } } private val signatureProvider by lazy { context.plugin<DokkaBase>().querySingle { signatureProvider } } + private val moduleDocumentationReader by lazy { context.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } } override fun invoke(creationContext: AllModulesPageGeneration.DefaultAllModulesContext): RootPageNode { val modules = context.configuration.modules @@ -88,15 +86,7 @@ class MultimodulePageCreator( files.map { MarkdownParser({ null }, it.absolutePath).parse(it.readText()) } private fun getDisplayedModuleDocumentation(module: DokkaModuleDescription): P? { - val parsingContext = ModuleAndPackageDocumentationParsingContext(logger) - - val documentationFragment = module.includes - .flatMap { include -> parseModuleAndPackageDocumentationFragments(include) } - .firstOrNull { fragment -> fragment.classifier == Module && fragment.name == module.name } - ?: return null - - val moduleDocumentation = parseModuleAndPackageDocumentation(parsingContext, documentationFragment) - return moduleDocumentation.documentation.firstParagraph() + return moduleDocumentationReader.read(module)?.firstParagraph() } private fun DocumentationNode.firstParagraph(): P? = diff --git a/plugins/android-documentation/build.gradle.kts b/plugins/android-documentation/build.gradle.kts index 5ef734b8..4dfc972d 100644 --- a/plugins/android-documentation/build.gradle.kts +++ b/plugins/android-documentation/build.gradle.kts @@ -8,9 +8,10 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) implementation(projects.plugins.base) + implementation(kotlin("reflect")) + testImplementation(projects.plugins.base) testImplementation(projects.plugins.base.baseTestUtils) testImplementation(projects.core.testApi) diff --git a/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt b/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt index 82375201..180268e4 100644 --- a/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt +++ b/plugins/android-documentation/src/test/kotlin/transformers/HideTagDocumentableFilterTest.kt @@ -2,8 +2,8 @@ package transformers import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.DClass -import kotlin.test.assertEquals import org.junit.jupiter.api.Test +import kotlin.test.assertEquals class HideTagDocumentableFilterTest : BaseAbstractTest() { private val configuration = dokkaConfiguration { @@ -68,4 +68,4 @@ class HideTagDocumentableFilterTest : BaseAbstractTest() { } -}
\ No newline at end of file +} diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index 0af1b46a..26787734 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -6,12 +6,7 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final fun getCommentsToContentConverter ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getCustomResourceInstaller ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getCustomTagContentProvider ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; - public final fun getDefaultExternalClasslikesTranslator ()Lorg/jetbrains/dokka/plugability/Extension; - public final fun getDefaultExternalDocumentablesProvider ()Lorg/jetbrains/dokka/plugability/Extension; - public final fun getDefaultKotlinAnalysis ()Lorg/jetbrains/dokka/plugability/Extension; - public final fun getDefaultSamplesTransformer ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDeprecatedDocumentableFilter ()Lorg/jetbrains/dokka/plugability/Extension; - public final fun getDescriptorToDocumentableTranslator ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDocTagToContentConverter ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDocumentableMerger ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getDocumentableToPageTranslator ()Lorg/jetbrains/dokka/plugability/Extension; @@ -20,8 +15,6 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final fun getEmptyModulesFilter ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getEmptyPackagesFilter ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getExtensionsExtractor ()Lorg/jetbrains/dokka/plugability/Extension; - public final fun getExternalClasslikesTranslator ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; - public final fun getExternalDocumentablesProvider ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getExternalLocationProviderFactory ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getFallbackMerger ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getFileWriter ()Lorg/jetbrains/dokka/plugability/Extension; @@ -31,7 +24,6 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final fun getInheritedEntriesVisbilityFilter ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getInheritorsExtractor ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getJavadocLocationProvider ()Lorg/jetbrains/dokka/plugability/Extension; - public final fun getKotlinAnalysis ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getKotlinArrayDocumentableReplacer ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getKotlinSignatureProvider ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getLocationProvider ()Lorg/jetbrains/dokka/plugability/Extension; @@ -45,7 +37,6 @@ public final class org/jetbrains/dokka/base/DokkaBase : org/jetbrains/dokka/plug public final fun getPageMergerStrategy ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getPathToRootConsumer ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getPreMergeDocumentableTransformer ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; - public final fun getPsiToDocumentableTranslator ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getReplaceVersionConsumer ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getResolveLinkConsumer ()Lorg/jetbrains/dokka/plugability/Extension; public final fun getRootCreator ()Lorg/jetbrains/dokka/plugability/Extension; @@ -120,104 +111,6 @@ public final class org/jetbrains/dokka/base/generation/SingleModuleGeneration : public final fun validityCheck (Lorg/jetbrains/dokka/plugability/DokkaContext;)V } -public class org/jetbrains/dokka/base/parsers/MarkdownParser : org/jetbrains/dokka/base/parsers/Parser { - public static final field Companion Lorg/jetbrains/dokka/base/parsers/MarkdownParser$Companion; - public fun <init> (Lkotlin/jvm/functions/Function1;Ljava/lang/String;)V - public fun parseStringToDocNode (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocTag; - public fun parseTagWithBody (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/TagWrapper; - public fun preparse (Ljava/lang/String;)Ljava/lang/String; -} - -public final class org/jetbrains/dokka/base/parsers/MarkdownParser$Companion { - public final fun fqName (Lorg/jetbrains/dokka/links/DRI;)Ljava/lang/String; - public final fun parseFromKDocTag (Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Z)Lorg/jetbrains/dokka/model/doc/DocumentationNode; - public static synthetic fun parseFromKDocTag$default (Lorg/jetbrains/dokka/base/parsers/MarkdownParser$Companion;Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ZILjava/lang/Object;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; -} - -public abstract class org/jetbrains/dokka/base/parsers/Parser { - public fun <init> ()V - public fun parse (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; - public abstract fun parseStringToDocNode (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocTag; - public fun parseTagWithBody (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/TagWrapper; - public abstract fun preparse (Ljava/lang/String;)Ljava/lang/String; -} - -public final class org/jetbrains/dokka/base/parsers/factories/DocTagsFromIElementFactory { - public static final field INSTANCE Lorg/jetbrains/dokka/base/parsers/factories/DocTagsFromIElementFactory; - public final fun getInstance (Lorg/intellij/markdown/IElementType;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;Z)Ljava/util/List; - public static synthetic fun getInstance$default (Lorg/jetbrains/dokka/base/parsers/factories/DocTagsFromIElementFactory;Lorg/intellij/markdown/IElementType;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;ZILjava/lang/Object;)Ljava/util/List; -} - -public final class org/jetbrains/dokka/base/parsers/factories/DocTagsFromStringFactory { - public static final field INSTANCE Lorg/jetbrains/dokka/base/parsers/factories/DocTagsFromStringFactory; - public final fun getInstance (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;)Lorg/jetbrains/dokka/model/doc/DocTag; - public static synthetic fun getInstance$default (Lorg/jetbrains/dokka/base/parsers/factories/DocTagsFromStringFactory;Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;ILjava/lang/Object;)Lorg/jetbrains/dokka/model/doc/DocTag; -} - -public final class org/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation { - public fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier;Lorg/jetbrains/dokka/model/doc/DocumentationNode;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public final fun component3 ()Lorg/jetbrains/dokka/model/doc/DocumentationNode; - public final fun copy (Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier;Lorg/jetbrains/dokka/model/doc/DocumentationNode;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation;Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier;Lorg/jetbrains/dokka/model/doc/DocumentationNode;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation; - public fun equals (Ljava/lang/Object;)Z - public final fun getClassifier ()Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public final fun getDocumentation ()Lorg/jetbrains/dokka/model/doc/DocumentationNode; - public final fun getName ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier : java/lang/Enum { - public static final field Module Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public static final field Package Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public static fun valueOf (Ljava/lang/String;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public static fun values ()[Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; -} - -public final class org/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment { - public fun <init> (Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier;Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource; - public final fun copy (Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier;Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment;Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier;Ljava/lang/String;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment; - public fun equals (Ljava/lang/Object;)Z - public final fun getClassifier ()Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation$Classifier; - public final fun getDocumentation ()Ljava/lang/String; - public final fun getName ()Ljava/lang/String; - public final fun getSource ()Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public abstract interface class org/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContext { - public abstract fun markdownParserFor (Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment;Ljava/lang/String;)Lorg/jetbrains/dokka/base/parsers/MarkdownParser; -} - -public final class org/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContextKt { - public static final fun ModuleAndPackageDocumentationParsingContext (Lorg/jetbrains/dokka/utilities/DokkaLogger;Lorg/jetbrains/dokka/analysis/DokkaResolutionFacade;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContext; - public static synthetic fun ModuleAndPackageDocumentationParsingContext$default (Lorg/jetbrains/dokka/utilities/DokkaLogger;Lorg/jetbrains/dokka/analysis/DokkaResolutionFacade;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContext; -} - -public abstract class org/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource { - public fun <init> ()V - public abstract fun getDocumentation ()Ljava/lang/String; - public abstract fun getSourceDescription ()Ljava/lang/String; - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/dokka/base/parsers/moduleAndPackage/ParseModuleAndPackageDocumentationFragmentsKt { - public static final fun parseModuleAndPackageDocumentationFragments (Ljava/io/File;)Ljava/util/List; - public static final fun parseModuleAndPackageDocumentationFragments (Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource;)Ljava/util/List; -} - -public final class org/jetbrains/dokka/base/parsers/moduleAndPackage/ParseModuleAndPackageDocumentationKt { - public static final fun parseModuleAndPackageDocumentation (Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContext;Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment;)Lorg/jetbrains/dokka/base/parsers/moduleAndPackage/ModuleAndPackageDocumentation; -} - public final class org/jetbrains/dokka/base/renderers/ContentTypeCheckingKt { public static final fun getURIExtension (Ljava/lang/String;)Ljava/lang/String; public static final fun isImage (Ljava/lang/String;)Z @@ -391,7 +284,7 @@ public final class org/jetbrains/dokka/base/renderers/html/HtmlRendererKt { } public abstract class org/jetbrains/dokka/base/renderers/html/NavigationDataProvider { - public fun <init> ()V + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun navigableChildren (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/base/renderers/html/NavigationNode; public fun visit (Lorg/jetbrains/dokka/pages/ContentPage;)Lorg/jetbrains/dokka/base/renderers/html/NavigationNode; } @@ -1276,11 +1169,6 @@ public final class org/jetbrains/dokka/base/transformers/documentables/UtilsKt { public static final fun isException (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;)Z } -public final class org/jetbrains/dokka/base/transformers/documentables/utils/FullClassHierarchyBuilder { - public fun <init> ()V - public final fun invoke (Lorg/jetbrains/dokka/model/DModule;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - public final class org/jetbrains/dokka/base/transformers/pages/annotations/SinceKotlinTransformer : org/jetbrains/dokka/transformers/documentation/DocumentableTransformer { public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; @@ -1332,21 +1220,6 @@ public final class org/jetbrains/dokka/base/transformers/pages/merger/SourceSetM public fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode; } -public final class org/jetbrains/dokka/base/transformers/pages/samples/DefaultSamplesTransformer : org/jetbrains/dokka/base/transformers/pages/samples/SamplesTransformer { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V - public fun processBody (Lcom/intellij/psi/PsiElement;)Ljava/lang/String; - public fun processImports (Lcom/intellij/psi/PsiElement;)Ljava/lang/String; -} - -public abstract class org/jetbrains/dokka/base/transformers/pages/samples/SamplesTransformer : org/jetbrains/dokka/transformers/pages/PageTransformer { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V - protected fun createSampleBody (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - public final fun getContext ()Lorg/jetbrains/dokka/plugability/DokkaContext; - public final fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode; - public abstract fun processBody (Lcom/intellij/psi/PsiElement;)Ljava/lang/String; - public abstract fun processImports (Lcom/intellij/psi/PsiElement;)Ljava/lang/String; -} - public final class org/jetbrains/dokka/base/transformers/pages/sourcelinks/SourceLink { public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)V public fun <init> (Lorg/jetbrains/dokka/DokkaConfiguration$SourceLinkDefinition;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)V @@ -1389,43 +1262,6 @@ public final class org/jetbrains/dokka/base/transformers/pages/tags/SinceKotlinT public fun isApplicable (Lorg/jetbrains/dokka/model/doc/CustomTagWrapper;)Z } -public final class org/jetbrains/dokka/base/translators/descriptors/DRIWithPlatformInfo { - public fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/util/Map;)V - public final fun component1 ()Lorg/jetbrains/dokka/links/DRI; - public final fun component2 ()Ljava/util/Map; - public final fun copy (Lorg/jetbrains/dokka/links/DRI;Ljava/util/Map;)Lorg/jetbrains/dokka/base/translators/descriptors/DRIWithPlatformInfo; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/base/translators/descriptors/DRIWithPlatformInfo;Lorg/jetbrains/dokka/links/DRI;Ljava/util/Map;ILjava/lang/Object;)Lorg/jetbrains/dokka/base/translators/descriptors/DRIWithPlatformInfo; - public fun equals (Ljava/lang/Object;)Z - public final fun getActual ()Ljava/util/Map; - public final fun getDri ()Lorg/jetbrains/dokka/links/DRI; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class org/jetbrains/dokka/base/translators/descriptors/DefaultDescriptorToDocumentableTranslator : org/jetbrains/dokka/base/translators/descriptors/ExternalClasslikesTranslator, org/jetbrains/dokka/transformers/sources/AsyncSourceToDocumentableTranslator { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V - public fun invoke (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DModule; - public fun invokeSuspending (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun translateClassDescriptor (Lorg/jetbrains/kotlin/descriptors/ClassDescriptor;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike; -} - -public final class org/jetbrains/dokka/base/translators/descriptors/DefaultDescriptorToDocumentableTranslatorKt { - public static final fun withEmptyInfo (Lorg/jetbrains/dokka/links/DRI;)Lorg/jetbrains/dokka/base/translators/descriptors/DRIWithPlatformInfo; -} - -public final class org/jetbrains/dokka/base/translators/descriptors/DefaultExternalDocumentablesProvider : org/jetbrains/dokka/base/translators/descriptors/ExternalDocumentablesProvider { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V - public fun findClasslike (Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike; -} - -public abstract interface class org/jetbrains/dokka/base/translators/descriptors/ExternalClasslikesTranslator { - public abstract fun translateClassDescriptor (Lorg/jetbrains/kotlin/descriptors/ClassDescriptor;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike; -} - -public abstract interface class org/jetbrains/dokka/base/translators/descriptors/ExternalDocumentablesProvider { - public abstract fun findClasslike (Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike; -} - public final class org/jetbrains/dokka/base/translators/documentables/BriefFromContentNodesKt { public static final fun firstParagraphBrief (Lorg/jetbrains/dokka/model/doc/DocTag;)Lorg/jetbrains/dokka/model/doc/DocTag; public static final fun firstSentenceBriefFromContentNodes (Ljava/util/List;)Ljava/util/List; @@ -1441,8 +1277,8 @@ public final class org/jetbrains/dokka/base/translators/documentables/DefaultDoc } public class org/jetbrains/dokka/base/translators/documentables/DefaultPageCreator { - public fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;)V - public synthetic fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser;)V + public synthetic fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser;ILkotlin/jvm/internal/DefaultConstructorMarker;)V protected fun contentForBrief (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/Documentable;)V protected fun contentForClasslikesAndEntries (Ljava/util/List;)Lorg/jetbrains/dokka/pages/ContentGroup; protected fun contentForConstructors (Ljava/util/List;Ljava/util/Set;Ljava/util/Set;)Lorg/jetbrains/dokka/pages/ContentGroup; @@ -1462,6 +1298,7 @@ public class org/jetbrains/dokka/base/translators/documentables/DefaultPageCreat public static synthetic fun divergentBlock$default (Lorg/jetbrains/dokka/base/translators/documentables/DefaultPageCreator;Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;Ljava/util/Collection;Lorg/jetbrains/dokka/pages/ContentKind;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)V protected fun getContentBuilder ()Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder; public final fun getCustomTagContentProviders ()Ljava/util/List; + public final fun getDocumentableAnalyzer ()Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser; public final fun getLogger ()Lorg/jetbrains/dokka/utilities/DokkaLogger; protected final fun getMergeImplicitExpectActualDeclarations ()Z protected final fun getSeparateInheritedMembers ()Z @@ -1622,27 +1459,3 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild public static synthetic fun row$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$TableBuilder;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V } -public final class org/jetbrains/dokka/base/translators/psi/DefaultPsiToDocumentableTranslator : org/jetbrains/dokka/transformers/sources/AsyncSourceToDocumentableTranslator { - public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V - public fun invoke (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DModule; - public fun invokeSuspending (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public final class org/jetbrains/dokka/base/translators/psi/DefaultPsiToDocumentableTranslator$DokkaPsiParser { - public fun <init> (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/analysis/DokkaResolutionFacade;Lorg/jetbrains/dokka/utilities/DokkaLogger;)V - public final fun parsePackage (Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public abstract interface class org/jetbrains/dokka/base/translators/psi/parsers/JavaDocumentationParser { - public abstract fun parseDocumentation (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; -} - -public final class org/jetbrains/dokka/base/translators/psi/parsers/JavadocParser : org/jetbrains/dokka/base/translators/psi/parsers/JavaDocumentationParser { - public static final field Companion Lorg/jetbrains/dokka/base/translators/psi/parsers/JavadocParser$Companion; - public fun <init> (Lorg/jetbrains/dokka/utilities/DokkaLogger;Lorg/jetbrains/dokka/analysis/DokkaResolutionFacade;)V - public fun parseDocumentation (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; -} - -public final class org/jetbrains/dokka/base/translators/psi/parsers/JavadocParser$Companion { -} - diff --git a/plugins/base/base-test-utils/build.gradle.kts b/plugins/base/base-test-utils/build.gradle.kts index 2645fbc3..ef4f9f7b 100644 --- a/plugins/base/base-test-utils/build.gradle.kts +++ b/plugins/base/base-test-utils/build.gradle.kts @@ -7,11 +7,15 @@ plugins { dependencies { compileOnly(projects.core) + compileOnly(projects.plugins.base) + + api(projects.subprojects.analysisKotlinApi) + // TODO [beresnev] analysis switcher + runtimeOnly(project(path = ":subprojects:analysis-kotlin-descriptors", configuration = "shadow")) implementation(kotlin("reflect")) - compileOnly(projects.plugins.base) implementation(projects.core.testApi) implementation(libs.jsoup) diff --git a/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt b/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt index a11ddb84..593a487c 100644 --- a/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt +++ b/plugins/base/base-test-utils/src/main/kotlin/testRunner/baseTestApi.kt @@ -9,7 +9,10 @@ import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.testApi.logger.TestLogger -import org.jetbrains.dokka.testApi.testRunner.* +import org.jetbrains.dokka.testApi.testRunner.AbstractTest +import org.jetbrains.dokka.testApi.testRunner.CoreTestMethods +import org.jetbrains.dokka.testApi.testRunner.DokkaTestGenerator +import org.jetbrains.dokka.testApi.testRunner.TestBuilder import org.jetbrains.dokka.utilities.DokkaConsoleLogger import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.utilities.LoggingLevel diff --git a/plugins/base/build.gradle.kts b/plugins/base/build.gradle.kts index b6ba2917..8bea63e8 100644 --- a/plugins/base/build.gradle.kts +++ b/plugins/base/build.gradle.kts @@ -8,14 +8,16 @@ plugins { dependencies { compileOnly(projects.core) + compileOnly(projects.subprojects.analysisKotlinApi) - implementation(kotlin("reflect")) + implementation(projects.subprojects.analysisMarkdownJb) + // Other + implementation(kotlin("reflect")) implementation(libs.kotlinx.coroutines.core) - - compileOnly(projects.kotlinAnalysis) implementation(libs.jsoup) - + implementation(libs.freemarker) + implementation(libs.kotlinx.html) implementation(libs.jackson.kotlin) constraints { implementation(libs.jackson.databind) { @@ -23,14 +25,9 @@ dependencies { } } - implementation(libs.freemarker) - + // Test only testImplementation(projects.plugins.base.baseTestUtils) testImplementation(projects.core.contentMatcherTestUtils) - - implementation(libs.kotlinx.html) - - testImplementation(projects.kotlinAnalysis) testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) diff --git a/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx b/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx index 1d0c8c10..d5150dd5 100644 --- a/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx +++ b/plugins/base/frontend/src/main/components/search/dokkaFuzzyFilter.tsx @@ -2,7 +2,7 @@ import Select from '@jetbrains/ring-ui/components/select/select'; import {Option, OptionWithHighlightComponent, OptionWithSearchResult} from "./types"; import fuzzyHighlight from '@jetbrains/ring-ui/components/global/fuzzy-highlight.js' import React from "react"; -import {SearchResultRow, signatureFromSearchResult} from "./searchResultRow"; +import {SearchResultRow} from "./searchResultRow"; import _ from "lodash"; const orderRecords = (records: OptionWithSearchResult[], searchPhrase: string): OptionWithSearchResult[] => { @@ -94,4 +94,4 @@ export class DokkaFuzzyFilterComponent extends Select { return highlightMatchedPhrases(orderRecords(matchedRecords, filterPhrase)) } -}
\ No newline at end of file +} diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx index d4e406bf..045f6365 100644 --- a/plugins/base/frontend/src/main/components/search/search.tsx +++ b/plugins/base/frontend/src/main/components/search/search.tsx @@ -1,12 +1,12 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, {useCallback, useEffect, useState} from 'react'; import List from '@jetbrains/ring-ui/components/list/list'; import Select from '@jetbrains/ring-ui/components/select/select'; import '@jetbrains/ring-ui/components/input-size/input-size.css'; import './search.scss'; import {CustomAnchorProps, IWindow, Option, Props} from "./types"; -import { DokkaSearchAnchor } from "./dokkaSearchAnchor"; -import { DokkaFuzzyFilterComponent } from "./dokkaFuzzyFilter"; -import { relativizeUrlForRequest } from '../utils/requests'; +import {DokkaSearchAnchor} from "./dokkaSearchAnchor"; +import {DokkaFuzzyFilterComponent} from "./dokkaFuzzyFilter"; +import {relativizeUrlForRequest} from '../utils/requests'; const WithFuzzySearchFilterComponent: React.FC<Props> = ({ data }: Props) => { const [selected, onSelected] = useState<Option>(data[0]); diff --git a/plugins/base/frontend/src/main/components/utils/requests.tsx b/plugins/base/frontend/src/main/components/utils/requests.tsx index 4a14e6f6..c95dda69 100644 --- a/plugins/base/frontend/src/main/components/utils/requests.tsx +++ b/plugins/base/frontend/src/main/components/utils/requests.tsx @@ -1,7 +1,7 @@ -import { IWindow } from "../search/types" +import {IWindow} from "../search/types" export const relativizeUrlForRequest = (filePath: string) : string => { const pathToRoot = (window as IWindow).pathToRoot const relativePath = pathToRoot == "" ? "." : pathToRoot return relativePath.endsWith('/') ? `${relativePath}${filePath}` : `${relativePath}/${filePath}` -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 8fc46870..6b000ac6 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -3,14 +3,14 @@ package org.jetbrains.dokka.base import org.jetbrains.dokka.CoreExtensions -import org.jetbrains.dokka.analysis.KotlinAnalysis -import org.jetbrains.dokka.analysis.ProjectKotlinAnalysis +import org.jetbrains.dokka.base.generation.SingleModuleGeneration import org.jetbrains.dokka.base.renderers.* import org.jetbrains.dokka.base.renderers.html.* import org.jetbrains.dokka.base.renderers.html.command.consumers.PathToRootConsumer +import org.jetbrains.dokka.base.renderers.html.command.consumers.ReplaceVersionsConsumer import org.jetbrains.dokka.base.renderers.html.command.consumers.ResolveLinkConsumer -import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProviderFactory import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProviderFactory import org.jetbrains.dokka.base.resolvers.external.javadoc.JavadocExternalLocationProviderFactory import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProviderFactory import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory @@ -23,24 +23,13 @@ import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransf import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter import org.jetbrains.dokka.base.transformers.pages.merger.* -import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer -import org.jetbrains.dokka.base.translators.descriptors.DefaultDescriptorToDocumentableTranslator -import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToPageTranslator -import org.jetbrains.dokka.base.translators.psi.DefaultPsiToDocumentableTranslator -import org.jetbrains.dokka.base.generation.SingleModuleGeneration -import org.jetbrains.dokka.base.renderers.html.command.consumers.ReplaceVersionsConsumer import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider import org.jetbrains.dokka.base.transformers.pages.tags.SinceKotlinTagContentProvider -import org.jetbrains.dokka.base.translators.descriptors.DefaultExternalDocumentablesProvider -import org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTranslator -import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider -import org.jetbrains.dokka.base.utils.NoopIntellijLoggerFactory +import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToPageTranslator import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.plugability.DokkaPluginApiPreview import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.renderers.PostAction import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -55,25 +44,16 @@ class DokkaBase : DokkaPlugin() { val externalLocationProviderFactory by extensionPoint<ExternalLocationProviderFactory>() val outputWriter by extensionPoint<OutputWriter>() val htmlPreprocessors by extensionPoint<PageTransformer>() - val kotlinAnalysis by extensionPoint<KotlinAnalysis>() + @Deprecated("It is not used anymore") val tabSortingStrategy by extensionPoint<TabSortingStrategy>() val immediateHtmlCommandConsumer by extensionPoint<ImmediateHtmlCommandConsumer>() - val externalDocumentablesProvider by extensionPoint<ExternalDocumentablesProvider>() - val externalClasslikesTranslator by extensionPoint<ExternalClasslikesTranslator>() + val singleGeneration by extending { CoreExtensions.generation providing ::SingleModuleGeneration } - val descriptorToDocumentableTranslator by extending { - CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator - } - - val psiToDocumentableTranslator by extending { - CoreExtensions.sourceToDocumentableTranslator providing ::DefaultPsiToDocumentableTranslator - } - val documentableMerger by extending { CoreExtensions.documentableMerger providing ::DefaultDocumentableMerger } @@ -168,7 +148,9 @@ class DokkaBase : DokkaPlugin() { } val pageMerger by extending { - CoreExtensions.pageTransformer providing ::PageMerger + CoreExtensions.pageTransformer providing ::PageMerger order { + // TODO [beresnev] make last() or at least after samples transformer + } } val sourceSetMerger by extending { @@ -189,15 +171,6 @@ class DokkaBase : DokkaPlugin() { CoreExtensions.renderer providing ::HtmlRenderer } - val defaultKotlinAnalysis by extending { - kotlinAnalysis providing { ctx -> - ProjectKotlinAnalysis( - sourceSets = ctx.configuration.sourceSets, - logger = ctx.logger - ) - } - } - val locationProvider by extending { locationProviderFactory providing ::DokkaLocationProviderFactory } @@ -218,12 +191,6 @@ class DokkaBase : DokkaPlugin() { htmlPreprocessors with RootCreator applyIf { !delayTemplateSubstitution } } - val defaultSamplesTransformer by extending { - CoreExtensions.pageTransformer providing ::DefaultSamplesTransformer order { - before(pageMerger) - } - } - val sourceLinksTransformer by extending { htmlPreprocessors providing ::SourceLinksTransformer order { after(rootCreator) } } @@ -275,26 +242,6 @@ class DokkaBase : DokkaPlugin() { htmlPreprocessors providing ::SearchbarDataInstaller order { after(sourceLinksTransformer) } } - val defaultExternalDocumentablesProvider by extending { - externalDocumentablesProvider providing ::DefaultExternalDocumentablesProvider - } - - val defaultExternalClasslikesTranslator by extending { - externalClasslikesTranslator providing ::DefaultDescriptorToDocumentableTranslator - } - - internal val disposeKotlinAnalysisPostAction by extending { - CoreExtensions.postActions with PostAction { this@DokkaBase.querySingle { kotlinAnalysis }.close() } - } - - private companion object { - init { - // Suppress messages emitted by the IntelliJ logger since - // there's not much the end user can do about it - com.intellij.openapi.diagnostic.Logger.setFactory(NoopIntellijLoggerFactory()) - } - } - @OptIn(DokkaPluginApiPreview::class) override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement diff --git a/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromStringFactory.kt b/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromStringFactory.kt deleted file mode 100644 index 1af4e719..00000000 --- a/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromStringFactory.kt +++ /dev/null @@ -1,77 +0,0 @@ -package org.jetbrains.dokka.base.parsers.factories - -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.links.DRI -import java.lang.NullPointerException - -object DocTagsFromStringFactory { - fun getInstance(name: String, children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap(), body: String? = null, dri: DRI? = null) = - when(name) { - "a" -> A(children, params) - "big" -> Big(children, params) - "b" -> B(children, params) - "blockquote" -> BlockQuote(children, params) - "br" -> Br - "cite" -> Cite(children, params) - "code" -> if(params.isEmpty()) CodeInline(children, params) else CodeBlock(children, params) - "dd" -> Dd(children, params) - "dfn" -> Dfn(children, params) - "dir" -> Dir(children, params) - "div" -> Div(children, params) - "dl" -> Dl(children, params) - "dt" -> Dt(children, params) - "Em" -> Em(children, params) - "font" -> Font(children, params) - "footer" -> Footer(children, params) - "frame" -> Frame(children, params) - "frameset" -> FrameSet(children, params) - "h1" -> H1(children, params) - "h2" -> H2(children, params) - "h3" -> H3(children, params) - "h4" -> H4(children, params) - "h5" -> H5(children, params) - "h6" -> H6(children, params) - "head" -> Head(children, params) - "header" -> Header(children, params) - "html" -> Html(children, params) - "i" -> I(children, params) - "iframe" -> IFrame(children, params) - "img" -> Img(children, params) - "input" -> Input(children, params) - "li" -> Li(children, params) - "link" -> Link(children, params) - "listing" -> Listing(children, params) - "main" -> Main(children, params) - "menu" -> Menu(children, params) - "meta" -> Meta(children, params) - "nav" -> Nav(children, params) - "noframes" -> NoFrames(children, params) - "noscript" -> NoScript(children, params) - "ol" -> Ol(children, params) - "p" -> P(children, params) - "pre" -> Pre(children, params) - "script" -> Script(children, params) - "section" -> Section(children, params) - "small" -> Small(children, params) - "span" -> Span(children, params) - "strong" -> Strong(children, params) - "sub" -> Sub(children, params) - "sup" -> Sup(children, params) - "table" -> Table(children, params) - "#text" -> Text(body ?: throw NullPointerException("Text body should be at least empty string passed to DocNodes factory!"), children, params) - "tBody" -> TBody(children, params) - "td" -> Td(children, params) - "tFoot" -> TFoot(children, params) - "th" -> Th(children, params) - "tHead" -> THead(children, params) - "title" -> Title(children, params) - "tr" -> Tr(children, params) - "tt" -> Tt(children, params) - "u" -> U(children, params) - "ul" -> Ul(children, params) - "var" -> Var(children, params) - "documentationlink" -> DocumentationLink(dri ?: throw NullPointerException("DRI cannot be passed null while constructing documentation link!"), children, params) - "hr" -> HorizontalRule - else -> CustomDocTag(children, params, name) - } -} diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment.kt b/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment.kt deleted file mode 100644 index 06fef72c..00000000 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationFragment.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage - -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.* - -data class ModuleAndPackageDocumentationFragment( - val name: String, - val classifier: Classifier, - val documentation: String, - val source: ModuleAndPackageDocumentationSource -) diff --git a/plugins/base/src/main/kotlin/renderers/PackageListService.kt b/plugins/base/src/main/kotlin/renderers/PackageListService.kt index 2bf66ebf..79391a1c 100644 --- a/plugins/base/src/main/kotlin/renderers/PackageListService.kt +++ b/plugins/base/src/main/kotlin/renderers/PackageListService.kt @@ -3,14 +3,13 @@ package org.jetbrains.dokka.base.renderers import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.resolvers.shared.LinkFormat import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.DOKKA_PARAM_PREFIX -import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.SINGLE_MODULE_NAME import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.MODULE_DELIMITER +import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.SINGLE_MODULE_NAME import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.kotlin.utils.addToStdlib.safeAs class PackageListService(val context: DokkaContext, val rootPage: RootPageNode) { @@ -29,7 +28,7 @@ class PackageListService(val context: DokkaContext, val rootPage: RootPageNode) ?.let { packages.add(it) } } - val contentPage = node.safeAs<ContentPage>() + val contentPage = node as? ContentPage contentPage?.dri?.forEach { dri -> val nodeLocation = locationProvider.resolve(node, context = module, skipExtension = true) ?: run { context.logger.error("Cannot resolve path for ${node.name}!"); null } diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 94bd0aeb..fa343eec 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -23,7 +23,6 @@ import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.pages.HtmlContent import org.jetbrains.dokka.plugability.* import org.jetbrains.dokka.utilities.htmlEscape -import org.jetbrains.kotlin.utils.addIfNotNull internal const val TEMPLATE_REPLACEMENT: String = "###" internal const val TOGGLEABLE_CONTENT_TYPE_ATTR = "data-togglable" @@ -355,12 +354,10 @@ open class HtmlRenderer( val contentOfSourceSet = mutableListOf<ContentNode>() distinct.onEachIndexed{ index, (_, distinctInstances) -> - contentOfSourceSet.addIfNotNull(distinctInstances.firstOrNull()?.before) + distinctInstances.firstOrNull()?.before?.let { contentOfSourceSet.add(it) } contentOfSourceSet.addAll(distinctInstances.map { it.divergent }) - contentOfSourceSet.addIfNotNull( - distinctInstances.firstOrNull()?.after - ?: if (index != distinct.size - 1) ContentBreakLine(it.key) else null - ) + (distinctInstances.firstOrNull()?.after ?: if (index != distinct.size - 1) ContentBreakLine(it.key) else null) + ?.let { contentOfSourceSet.add(it) } // content kind main is important for declarations list to avoid double line breaks if (node.dci.kind == ContentKind.Main && index != distinct.size - 1) { diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt index be1b0fcf..c864295c 100644 --- a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt +++ b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt @@ -4,13 +4,20 @@ import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations import org.jetbrains.dokka.base.transformers.documentables.isDeprecated import org.jetbrains.dokka.base.transformers.documentables.isException -import org.jetbrains.dokka.base.translators.documentables.DocumentableLanguage -import org.jetbrains.dokka.base.translators.documentables.documentableLanguage import org.jetbrains.dokka.base.utils.canonicalAlphabeticalOrder import org.jetbrains.dokka.model.* import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.kotlin.analysis.kotlin.internal.DocumentableLanguage +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin + +abstract class NavigationDataProvider( + dokkaContext: DokkaContext +) { + private val documentableSourceLanguageParser = dokkaContext.plugin<InternalKotlinAnalysisPlugin>().querySingle { documentableSourceLanguageParser } -abstract class NavigationDataProvider { open fun navigableChildren(input: RootPageNode): NavigationNode = input.withDescendants() .first { it is ModulePage || it is MultimoduleRootPage }.let { visit(it as ContentPage) } @@ -68,8 +75,9 @@ abstract class NavigationDataProvider { } private fun Documentable.hasAnyJavaSources(): Boolean { - val withSources = this as? WithSources ?: return false - return this.sourceSets.any { withSources.documentableLanguage(it) == DocumentableLanguage.JAVA } + return this.sourceSets.any { sourceSet -> + documentableSourceLanguageParser.getLanguage(this, sourceSet) == DocumentableLanguage.JAVA + } } private fun DClass.isAbstract() = diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt index 9543c388..4def7088 100644 --- a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt +++ b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt @@ -2,6 +2,8 @@ package org.jetbrains.dokka.base.renderers.html import kotlinx.html.* import kotlinx.html.stream.createHTML +import org.jetbrains.dokka.base.renderers.html.NavigationNodeIcon.CLASS +import org.jetbrains.dokka.base.renderers.html.NavigationNodeIcon.CLASS_KT import org.jetbrains.dokka.base.renderers.pageId import org.jetbrains.dokka.base.templating.AddToNavigationCommand import org.jetbrains.dokka.links.DRI diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index a213bce9..557205d7 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -11,7 +11,7 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.configuration import org.jetbrains.dokka.transformers.pages.PageTransformer -open class NavigationPageInstaller(val context: DokkaContext) : NavigationDataProvider(), PageTransformer { +open class NavigationPageInstaller(val context: DokkaContext) : NavigationDataProvider(context), PageTransformer { override fun invoke(input: RootPageNode): RootPageNode = input.modified( children = input.children + NavigationPage( diff --git a/plugins/base/src/main/kotlin/renderers/preprocessors.kt b/plugins/base/src/main/kotlin/renderers/preprocessors.kt index 8a30bed1..2cfa2dcf 100644 --- a/plugins/base/src/main/kotlin/renderers/preprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/preprocessors.kt @@ -1,12 +1,7 @@ package org.jetbrains.dokka.base.renderers import org.jetbrains.dokka.base.resolvers.shared.LinkFormat -import org.jetbrains.dokka.pages.ModulePage -import org.jetbrains.dokka.pages.RendererSpecificPage -import org.jetbrains.dokka.pages.RendererSpecificResourcePage -import org.jetbrains.dokka.pages.RendererSpecificRootPage -import org.jetbrains.dokka.pages.RenderingStrategy -import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.pages.PageTransformer diff --git a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt index a1f1542d..3ac987f6 100644 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt +++ b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt @@ -2,7 +2,10 @@ package org.jetbrains.dokka.base.resolvers.external.javadoc import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation -import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DRIExtraContainer +import org.jetbrains.dokka.links.EnumEntryDRIExtra import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.htmlEscape diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt index f6c4f0db..e589da15 100644 --- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt +++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt @@ -1,13 +1,13 @@ package org.jetbrains.dokka.base.signatures +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.drisOfAllNestedBounds import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.AnnotationTarget import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.drisOfAllNestedBounds -import org.jetbrains.dokka.model.AnnotationTarget interface JvmSignatureUtils { diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt index dfb0d3f7..8f278545 100644 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -2,7 +2,6 @@ package org.jetbrains.dokka.base.signatures import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.dri import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.driOrNull @@ -18,15 +17,14 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi -import org.jetbrains.kotlin.psi.KtParameter import kotlin.text.Typography.nbsp class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider, JvmSignatureUtils by KotlinSignatureUtils { + constructor(context: DokkaContext) : this( context.plugin<DokkaBase>().querySingle { commentsToContentConverter }, - context.logger + context.logger, ) private val contentBuilder = PageContentBuilder(ctcc, this, logger) @@ -222,8 +220,12 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog * An example would be a primary constructor `class A(val s: String)`, * where `s` is both a function parameter and a property */ - private fun DProperty.isAlsoParameter(sourceSet: DokkaSourceSet) = - (this.sources[sourceSet] as? DescriptorDocumentableSource)?.descriptor?.findPsi() is KtParameter + private fun DProperty.isAlsoParameter(sourceSet: DokkaSourceSet): Boolean { + return this.extra[IsAlsoParameter] + ?.inSourceSets + ?.any { it.sourceSetID == sourceSet.sourceSetID } + ?: false + } private fun propertySignature(p: DProperty) = p.sourceSets.map { sourceSet -> diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt index ae5275a5..e47c85b9 100644 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt @@ -1,14 +1,14 @@ package org.jetbrains.dokka.base.signatures -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer -import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.DriOfAny import org.jetbrains.dokka.links.DriOfUnit import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.AnnotationTarget import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.ContentKind object KotlinSignatureUtils : JvmSignatureUtils { diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt index 17e3cbcd..d9c68981 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt @@ -1,13 +1,13 @@ package org.jetbrains.dokka.base.transformers.documentables import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.utils.firstNotNullOfOrNull import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.properties.ExtraProperty import org.jetbrains.dokka.model.properties.MergeStrategy import org.jetbrains.dokka.model.properties.mergeExtras import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.documentation.DocumentableMerger -import org.jetbrains.kotlin.util.firstNotNullResult internal class DefaultDocumentableMerger(val context: DokkaContext) : DocumentableMerger { private val dependencyInfo = context.getDependencyInfo() @@ -21,7 +21,7 @@ internal class DefaultDocumentableMerger(val context: DokkaContext) : Documentab list.flatMap { it.packages } ) { pck1, pck2 -> pck1.mergeWith(pck2) }, documentation = list.map { it.documentation }.flatMap { it.entries }.associate { (k, v) -> k to v }, - expectPresentInSet = list.firstNotNullResult { it.expectPresentInSet }, + expectPresentInSet = list.firstNotNullOfOrNull { it.expectPresentInSet }, sourceSets = list.flatMap { it.sourceSets }.toSet() ).mergeExtras(left, right) } diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt index 1112ac15..366690c7 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt @@ -1,7 +1,7 @@ package org.jetbrains.dokka.base.transformers.documentables -import org.jetbrains.dokka.DokkaConfiguration.PackageOptions import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.PackageOptions import org.jetbrains.dokka.model.Annotations import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.EnumValue diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt index 94688799..713166bb 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt @@ -1,11 +1,11 @@ package org.jetbrains.dokka.base.transformers.documentables import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.DokkaDefaults import org.jetbrains.dokka.model.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.DokkaDefaults class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt index 19af0564..79df844e 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt @@ -2,7 +2,6 @@ package org.jetbrains.dokka.base.transformers.documentables import kotlinx.coroutines.* import kotlinx.coroutines.channels.* -import org.jetbrains.dokka.base.transformers.documentables.utils.FullClassHierarchyBuilder import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.DriOfAny import org.jetbrains.dokka.model.* @@ -10,16 +9,19 @@ import org.jetbrains.dokka.model.properties.ExtraProperty import org.jetbrains.dokka.model.properties.MergeStrategy import org.jetbrains.dokka.model.properties.plus import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer import org.jetbrains.dokka.utilities.parallelForEach import org.jetbrains.dokka.utilities.parallelMap +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin class ExtensionExtractorTransformer : DocumentableTransformer { override fun invoke(original: DModule, context: DokkaContext): DModule = runBlocking(Dispatchers.Default) { val classGraph = async { if (!context.configuration.suppressInheritedMembers) - FullClassHierarchyBuilder()(original) + context.plugin<InternalKotlinAnalysisPlugin>().querySingle { fullClassHierarchyBuilder }.build(original) else emptyMap() } diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt index 2e4b29ff..01438432 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.base.transformers.documentables -import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.InheritedMember import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.plugability.DokkaContext @@ -14,4 +15,4 @@ class InheritedEntriesDocumentableFilterTransformer(context: DokkaContext) : return context.configuration.suppressInheritedMembers && containsInheritedFrom } -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt index 463ec419..6106466f 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt @@ -1,11 +1,11 @@ package org.jetbrains.dokka.base.transformers.documentables +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.properties.ExtraProperty import org.jetbrains.dokka.model.properties.MergeStrategy import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer class InheritorsExtractorTransformer : DocumentableTransformer { diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt index 99fba9f7..9f10873b 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt @@ -5,21 +5,27 @@ import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.SourceSetDependent import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin +import org.jetbrains.kotlin.analysis.kotlin.internal.ModuleAndPackageDocumentationReader internal class ModuleAndPackageDocumentationTransformer( private val moduleAndPackageDocumentationReader: ModuleAndPackageDocumentationReader ) : PreMergeDocumentableTransformer { - constructor(context: DokkaContext) : this(ModuleAndPackageDocumentationReader(context)) + constructor(context: DokkaContext) : this( + context.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } + ) override fun invoke(modules: List<DModule>): List<DModule> { return modules.map { module -> module.copy( - documentation = module.documentation + moduleAndPackageDocumentationReader[module], + documentation = module.documentation + moduleAndPackageDocumentationReader.read(module), packages = module.packages.map { pkg -> pkg.copy( - documentation = pkg.documentation + moduleAndPackageDocumentationReader[pkg] + documentation = pkg.documentation + moduleAndPackageDocumentationReader.read(pkg) ) } ) diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt index ad9e34df..3368ded1 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt @@ -2,14 +2,12 @@ package org.jetbrains.dokka.base.transformers.documentables import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource import org.jetbrains.dokka.model.* import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED -import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin internal class ReportUndocumentedTransformer : DocumentableTransformer { @@ -19,14 +17,14 @@ internal class ReportUndocumentedTransformer : DocumentableTransformer { private fun invoke(documentable: Documentable, context: DokkaContext) { documentable.sourceSets.forEach { sourceSet -> - if (shouldBeReportedIfNotDocumented(documentable, sourceSet)) { + if (shouldBeReportedIfNotDocumented(documentable, sourceSet, context)) { reportIfUndocumented(context, documentable, sourceSet) } } } private fun shouldBeReportedIfNotDocumented( - documentable: Documentable, sourceSet: DokkaSourceSet + documentable: Documentable, sourceSet: DokkaSourceSet, context: DokkaContext ): Boolean { val packageOptionsOrNull = packageOptionsOrNull(sourceSet, documentable) @@ -42,11 +40,8 @@ internal class ReportUndocumentedTransformer : DocumentableTransformer { return false } - if (isFakeOverride(documentable, sourceSet)) { - return false - } - - if (isSynthesized(documentable, sourceSet)) { + val syntheticDetector = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { syntheticDocumentableDetector } + if (syntheticDetector.isSynthetic(documentable, sourceSet)) { return false } @@ -118,28 +113,8 @@ internal class ReportUndocumentedTransformer : DocumentableTransformer { return documentable.isConstructor } - private fun isFakeOverride(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - return callableMemberDescriptorOrNull(documentable, sourceSet)?.kind == FAKE_OVERRIDE - } - - private fun isSynthesized(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - return callableMemberDescriptorOrNull(documentable, sourceSet)?.kind == SYNTHESIZED - } - - private fun callableMemberDescriptorOrNull( - documentable: Documentable, sourceSet: DokkaSourceSet - ): CallableMemberDescriptor? { - if (documentable is WithSources) { - return documentable.sources[sourceSet] - .safeAs<DescriptorDocumentableSource>()?.descriptor - .safeAs<CallableMemberDescriptor>() - } - - return null - } - private fun isPrivateOrInternalApi(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - return when (documentable.safeAs<WithVisibility>()?.visibility?.get(sourceSet)) { + return when ((documentable as? WithVisibility)?.visibility?.get(sourceSet)) { KotlinVisibility.Public -> false KotlinVisibility.Private -> true KotlinVisibility.Protected -> true diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt index a297908d..2d65e98b 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt @@ -2,11 +2,11 @@ package org.jetbrains.dokka.base.transformers.documentables import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.dfs -import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.model.doc.Suppress +import org.jetbrains.dokka.plugability.DokkaContext class SuppressTagDocumentableFilter(val dokkaContext: DokkaContext) : SuppressedByConditionDocumentableFilterTransformer(dokkaContext) { override fun shouldBeSuppressed(d: Documentable): Boolean = d.documentation.any { (_, docs) -> docs.dfs { it is Suppress } != null } -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt index 75f43324..23b3f625 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt @@ -1,9 +1,9 @@ package org.jetbrains.dokka.base.transformers.pages.annotations -import com.intellij.util.containers.ComparatorUtil.max -import org.intellij.markdown.MarkdownElementTypes + import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.CustomDocTag @@ -13,7 +13,6 @@ import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer import org.jetbrains.dokka.utilities.associateWithNotNull -import org.jetbrains.kotlin.utils.addToStdlib.safeAs class SinceKotlinVersion constructor(str: String) : Comparable<SinceKotlinVersion> { private val parts: List<Int> = str.split(".").map { it.toInt() } @@ -124,7 +123,7 @@ class SinceKotlinTransformer(val context: DokkaContext) : DocumentableTransforme val annotatedVersion = annotations()[sourceSet] ?.findSinceKotlinAnnotation() - ?.params?.get("version").safeAs<StringValue>()?.value + ?.params?.let { it["version"] as? StringValue }?.value ?.let { SinceKotlinVersion(it) } val minSinceKotlin = minSinceKotlinVersionOfPlatform[sourceSet.analysisPlatform] @@ -139,7 +138,7 @@ class SinceKotlinTransformer(val context: DokkaContext) : DocumentableTransforme val version = getVersion(sourceSet) val parentVersion = parent?.get(sourceSet) if (parentVersion != null) - max(version, parentVersion) + maxOf(version, parentVersion) else version } @@ -157,7 +156,7 @@ class SinceKotlinTransformer(val context: DokkaContext) : DocumentableTransforme version.toString() ) ), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ), "Since Kotlin" ) diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt index fa9ce37e..58e22103 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt @@ -3,7 +3,9 @@ package org.jetbrains.dokka.base.transformers.pages.comments import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.model.doc.DocTag import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.DCI +import org.jetbrains.dokka.pages.Style interface CommentsToContentConverter { fun buildContent( diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt index 2193283c..083a82cc 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt @@ -1,13 +1,14 @@ package org.jetbrains.dokka.base.transformers.pages.comments -import org.intellij.markdown.MarkdownElementTypes + import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.plus import org.jetbrains.dokka.model.toDisplaySourceSets import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.dokka.utilities.firstIsInstanceOrNull open class DocTagToContentConverter : CommentsToContentConverter { override fun buildContent( @@ -261,5 +262,5 @@ open class DocTagToContentConverter : CommentsToContentConverter { } } - private fun CustomDocTag.isNonemptyFile() = name == MarkdownElementTypes.MARKDOWN_FILE.name && children.size > 1 + private fun CustomDocTag.isNonemptyFile() = name == MARKDOWN_ELEMENT_FILE_NAME && children.size > 1 } diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt index 6cb7f603..f9da616c 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt @@ -3,7 +3,9 @@ package org.jetbrains.dokka.base.transformers.pages.merger import org.jetbrains.dokka.Platform import org.jetbrains.dokka.model.DisplaySourceSet import org.jetbrains.dokka.model.toDisplaySourceSets -import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.pages.ContentComposite +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.pages.PageTransformer diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt index 27428fc6..f2c3d3f0 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt @@ -1,26 +1,19 @@ package org.jetbrains.dokka.base.transformers.pages.sourcelinks -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiIdentifier import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource -import org.jetbrains.dokka.analysis.PsiDocumentableSource import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.DocumentableSource +import org.jetbrains.dokka.model.WithSources +import org.jetbrains.dokka.model.sourceSetIDs import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.pages.PageTransformer -import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.psiUtil.getChildOfType -import org.jetbrains.kotlin.resolve.source.getPsi -import org.jetbrains.kotlin.utils.addToStdlib.cast import java.io.File class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { @@ -80,31 +73,13 @@ class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { val sourcePath = File(this.path).invariantSeparatorsPath val sourceLinkPath = File(sourceLink.path).invariantSeparatorsPath - val lineNumber = when (this) { - is DescriptorDocumentableSource -> this.descriptor - .cast<DeclarationDescriptorWithSource>() - .source.getPsi() - ?.lineNumber() - is PsiDocumentableSource -> this.psi.lineNumber() - else -> null - } + val lineNumber = this.computeLineNumber() return sourceLink.url + sourcePath.split(sourceLinkPath)[1] + sourceLink.lineSuffix + "${lineNumber ?: 1}" } - private fun PsiElement.lineNumber(): Int? { - val ktIdentifierTextRange = this.node?.findChildByType(KtTokens.IDENTIFIER)?.textRange - val javaIdentifierTextRange = this.getChildOfType<PsiIdentifier>()?.textRange - // synthetic and some light methods might return null - val textRange = ktIdentifierTextRange ?: javaIdentifierTextRange ?: textRange ?: return null - - val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) - // IJ uses 0-based line-numbers; external source browsers use 1-based - return doc?.getLineNumber(textRange.startOffset)?.plus(1) - } - private fun ContentNode.signatureGroupOrNull() = (this as? ContentGroup)?.takeIf { it.dci.kind == ContentKind.Symbol } @@ -150,4 +125,4 @@ data class SourceLink(val path: String, val url: String, val lineSuffix: String? sourceLinkDefinition.remoteLineSuffix, sourceSetData ) -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/translators/annotationsValue.kt b/plugins/base/src/main/kotlin/translators/annotationsValue.kt deleted file mode 100644 index a840816a..00000000 --- a/plugins/base/src/main/kotlin/translators/annotationsValue.kt +++ /dev/null @@ -1,3 +0,0 @@ -package org.jetbrains.dokka.base.translators - -internal fun unquotedValue(value: String): String = value.removeSurrounding("\"")
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt index a385e0e4..d8582ec5 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt @@ -6,6 +6,7 @@ import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.pages.ModulePageNode import org.jetbrains.dokka.plugability.* import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin class DefaultDocumentableToPageTranslator( context: DokkaContext @@ -14,6 +15,7 @@ class DefaultDocumentableToPageTranslator( private val commentsToContentConverter = context.plugin<DokkaBase>().querySingle { commentsToContentConverter } private val signatureProvider = context.plugin<DokkaBase>().querySingle { signatureProvider } private val customTagContentProviders = context.plugin<DokkaBase>().query { customTagContentProvider } + private val documentableSourceLanguageParser = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { documentableSourceLanguageParser } private val logger = context.logger override fun invoke(module: DModule): ModulePageNode = @@ -22,6 +24,7 @@ class DefaultDocumentableToPageTranslator( commentsToContentConverter, signatureProvider, logger, - customTagContentProviders + customTagContentProviders, + documentableSourceLanguageParser ).pageForModule(module) -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index ffc7fd85..8df785c0 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -18,7 +18,8 @@ import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.DokkaLogger -import java.util.Comparator +import org.jetbrains.kotlin.analysis.kotlin.internal.DocumentableSourceLanguageParser +import org.jetbrains.kotlin.analysis.kotlin.internal.DocumentableLanguage import kotlin.reflect.KClass internal typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>> @@ -28,7 +29,8 @@ open class DefaultPageCreator( commentsToContentConverter: CommentsToContentConverter, signatureProvider: SignatureProvider, val logger: DokkaLogger, - val customTagContentProviders: List<CustomTagContentProvider> = emptyList() + val customTagContentProviders: List<CustomTagContentProvider> = emptyList(), + val documentableAnalyzer: DocumentableSourceLanguageParser ) { protected open val contentBuilder = PageContentBuilder(commentsToContentConverter, signatureProvider, logger) @@ -506,12 +508,12 @@ open class DefaultPageCreator( sourceSet: DokkaSourceSet, tag: TagWrapper ) { - (documentable as? WithSources)?.documentableLanguage(sourceSet)?.let { - when (it) { - DocumentableLanguage.KOTLIN -> firstParagraphComment(tag.root) - DocumentableLanguage.JAVA -> firstSentenceComment(tag.root) - } - } ?: firstParagraphComment(tag.root) + val language = documentableAnalyzer.getLanguage(documentable, sourceSet) + when(language) { + DocumentableLanguage.JAVA -> firstSentenceComment(tag.root) + DocumentableLanguage.KOTLIN -> firstParagraphComment(tag.root) + else -> firstParagraphComment(tag.root) + } } protected open fun contentForFunction(f: DFunction) = contentForMember(f) diff --git a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt index 73c36d8d..7e5926a0 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt @@ -3,11 +3,11 @@ package org.jetbrains.dokka.base.translators.documentables import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.annotations import org.jetbrains.dokka.base.transformers.documentables.isDeprecated +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder import org.jetbrains.dokka.model.* import org.jetbrains.dokka.pages.ContentKind import org.jetbrains.dokka.pages.ContentStyle import org.jetbrains.dokka.pages.TextStyle -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder /** * Main header for [Deprecated] section diff --git a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt b/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt index 35645a73..6c5d885d 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt @@ -16,7 +16,6 @@ import org.jetbrains.dokka.pages.ContentKind import org.jetbrains.dokka.pages.ContentStyle import org.jetbrains.dokka.pages.TextStyle import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.kotlin.utils.addToStdlib.safeAs import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf @@ -250,10 +249,15 @@ internal fun PageContentBuilder.DocumentableContentBuilder.inheritorsSectionCont multiplatformInheritorsSectionContent(documentable, inheritors, logger) } -private fun WithScope.inheritors() = safeAs<WithExtraProperties<Documentable>>() - ?.let { it.extra[InheritorsInfo] } - ?.let { inheritors -> inheritors.value.filter { it.value.isNotEmpty() } } - .orEmpty() +private fun WithScope.inheritors(): SourceSetDependent<List<DRI>> { + @Suppress("UNCHECKED_CAST") + val withExtra = this as? WithExtraProperties<Documentable> + + return withExtra + ?.let { it.extra[InheritorsInfo] } + ?.let { inheritors -> inheritors.value.filter { it.value.isNotEmpty() } } + .orEmpty() +} /** * Detect that documentable is located only in the shared code without expect-actuals diff --git a/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt b/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt index bd54eedd..092077d6 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt @@ -1,15 +1,17 @@ package org.jetbrains.dokka.base.translators.documentables -import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.base.utils.firstNotNullOfOrNull +import org.jetbrains.dokka.model.doc.CustomDocTag +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.util.firstNotNullResult -import org.jetbrains.kotlin.utils.addToStdlib.safeAs fun firstParagraphBrief(docTag: DocTag): DocTag? = when(docTag){ is P -> docTag - is CustomDocTag -> docTag.children.firstNotNullResult { firstParagraphBrief(it) } + is CustomDocTag -> docTag.children.firstNotNullOfOrNull { firstParagraphBrief(it) } is Text -> docTag else -> null } @@ -18,7 +20,7 @@ fun firstSentenceBriefFromContentNodes(description: List<ContentNode>): List<Con val firstSentenceRegex = """^((?:[^.?!]|[.!?](?!\s))*[.!?])""".toRegex() //Description that is entirely based on html content. In html it is hard to define a brief so we render all of it - if(description.all { it.withDescendants().all { it is ContentGroup || it.safeAs<ContentText>()?.isHtml == true } }){ + if(description.all { it.withDescendants().all { it is ContentGroup || (it as? ContentText)?.isHtml == true } }){ return description } diff --git a/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt b/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt deleted file mode 100644 index b3ce7c5c..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/documentableLanguage.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.analysis.PsiDocumentableSource -import org.jetbrains.dokka.model.WithSources - -internal enum class DocumentableLanguage { - JAVA, KOTLIN -} - -internal fun WithSources.documentableLanguage(sourceSet: DokkaConfiguration.DokkaSourceSet): DocumentableLanguage = - when (sources[sourceSet]) { - is PsiDocumentableSource -> DocumentableLanguage.JAVA - else -> DocumentableLanguage.KOTLIN - }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/parseWithNormalisedSpaces.kt b/plugins/base/src/main/kotlin/translators/parseWithNormalisedSpaces.kt deleted file mode 100644 index 7bda9d0b..00000000 --- a/plugins/base/src/main/kotlin/translators/parseWithNormalisedSpaces.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.jetbrains.dokka.base.translators - -import org.intellij.markdown.lexer.Compat.codePointToString -import org.intellij.markdown.lexer.Compat.forEachCodePoint -import org.jetbrains.dokka.model.doc.DocTag -import org.jetbrains.dokka.model.doc.DocTag.Companion.contentTypeParam -import org.jetbrains.dokka.model.doc.Text -import org.jsoup.Jsoup -import org.jsoup.internal.StringUtil -import org.jsoup.nodes.Entities - -internal fun String.parseHtmlEncodedWithNormalisedSpaces( - renderWhiteCharactersAsSpaces: Boolean -): List<DocTag> { - val accum = StringBuilder() - val tags = mutableListOf<DocTag>() - var lastWasWhite = false - - forEachCodePoint { c -> - if (renderWhiteCharactersAsSpaces && StringUtil.isWhitespace(c)) { - if (!lastWasWhite) { - accum.append(' ') - lastWasWhite = true - } - } else if (codePointToString(c).let { it != Entities.escape(it) }) { - accum.toString().takeIf { it.isNotBlank() }?.let { tags.add(Text(it)) } - accum.delete(0, accum.length) - - accum.appendCodePoint(c) - tags.add(Text(accum.toString(), params = contentTypeParam("html"))) - accum.delete(0, accum.length) - } else if (!StringUtil.isInvisibleChar(c)) { - accum.appendCodePoint(c) - lastWasWhite = false - } - } - accum.toString().takeIf { it.isNotBlank() }?.let { tags.add(Text(it)) } - return tags -} - -/** - * Parses string into [Text] doc tags that can have either value of the string or html-encoded value with content-type=html parameter. - * Content type is added when dealing with html entries like ` ` - */ -internal fun String.parseWithNormalisedSpaces( - renderWhiteCharactersAsSpaces: Boolean -): List<DocTag> { - if (!requiresHtmlEncoding()) { - return parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces) - } - // parsing it using jsoup is required to get codePoints, otherwise they are interpreted separately, as chars - // But we dont need to do it for java as it is already parsed with jsoup - return Jsoup.parseBodyFragment(this).body().wholeText().parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces) -} - -private fun String.requiresHtmlEncoding(): Boolean = indexOf('&') != -1 diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt deleted file mode 100644 index c410104c..00000000 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ /dev/null @@ -1,863 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi - -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.vfs.VirtualFileManager -import com.intellij.psi.* -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.KotlinAnalysis -import org.jetbrains.dokka.analysis.PsiDocumentableSource -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser -import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions -import org.jetbrains.dokka.base.translators.unquotedValue -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.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.utilities.parallelForEach -import org.jetbrains.dokka.utilities.parallelMap -import org.jetbrains.dokka.utilities.parallelMapNotNull -import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName -import org.jetbrains.kotlin.psi.psiUtil.getChildOfType -import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import java.io.File - -class DefaultPsiToDocumentableTranslator( - context: DokkaContext, -) : AsyncSourceToDocumentableTranslator { - - private val kotlinAnalysis: KotlinAnalysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } - - override suspend fun invokeSuspending(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { - return coroutineScope { - fun isFileInSourceRoots(file: File): Boolean = - sourceSet.sourceRoots.any { root -> file.startsWith(root) } - - - val (environment, facade) = kotlinAnalysis[sourceSet] - - val sourceRoots = environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<JavaSourceRoot>() - ?.mapNotNull { it.file.takeIf(::isFileInSourceRoots) } - ?: listOf() - val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") - - val psiFiles = sourceRoots.parallelMap { sourceRoot -> - sourceRoot.absoluteFile.walkTopDown().mapNotNull { - localFileSystem.findFileByPath(it.path)?.let { vFile -> - PsiManager.getInstance(environment.project).findFile(vFile) as? PsiJavaFile - } - }.toList() - }.flatten() - - val docParser = - DokkaPsiParser( - sourceSet, - facade, - context.logger - ) - - DModule( - name = context.configuration.moduleName, - packages = psiFiles.parallelMapNotNull { it.safeAs<PsiJavaFile>() }.groupBy { it.packageName }.toList() - .parallelMap { (packageName: String, psiFiles: List<PsiJavaFile>) -> - docParser.parsePackage(packageName, psiFiles) - }, - documentation = emptyMap(), - expectPresentInSet = null, - sourceSets = setOf(sourceSet) - ) - } - } - - class DokkaPsiParser( - private val sourceSetData: DokkaSourceSet, - private val facade: DokkaResolutionFacade, - private val logger: DokkaLogger, - ) { - private val javadocParser = JavadocParser(logger, facade) - private val syntheticDocProvider = SyntheticElementDocumentationProvider(javadocParser, facade) - - 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 - */ - 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(facade.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.extendsBound != PsiType.NULL -> Covariance(getBound(type.extendsBound)) - type.superBound != PsiType.NULL -> Contravariance(getBound(type.superBound)) - 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 { it !is KtLightAbstractAnnotation }.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(unquotedValue(annotationValue.value)) - 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: KotlinExceptionWithAttachments) { - logger.warn("Failed to get annotations from ${this.getKotlinFqName()}") - null - } - } - - return psiReference?.let { psiElement -> - Annotations.Annotation( - dri = DRI.from(psiElement), - params = attributes - .filter { it !is KtLightAbstractAnnotation } - .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() - } -} - -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/plugins/base/src/main/kotlin/translators/psi/PsiInheritance.kt b/plugins/base/src/main/kotlin/translators/psi/PsiInheritance.kt deleted file mode 100644 index 1eca489e..00000000 --- a/plugins/base/src/main/kotlin/translators/psi/PsiInheritance.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi - -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiMethod -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.utils.addToStdlib.safeAs - -internal fun PsiClass.implementsInterface(fqName: FqName): Boolean { - return allInterfaces().any { it.getKotlinFqName() == fqName } -} - -internal fun PsiClass.allInterfaces(): Sequence<PsiClass> { - return sequence { - this.yieldAll(interfaces.toList()) - interfaces.forEach { yieldAll(it.allInterfaces()) } - } -} - -/** - * Workaround for failing [PsiMethod.findSuperMethods]. - * This might be resolved once ultra light classes are enabled for dokka - * See [KT-39518](https://youtrack.jetbrains.com/issue/KT-39518) - */ -internal fun PsiMethod.findSuperMethodsOrEmptyArray(logger: DokkaLogger): Array<PsiMethod> { - return try { - /* - We are not even attempting to call "findSuperMethods" on all methods called "getGetter" or "getSetter" - on any object implementing "kotlin.reflect.KProperty", since we know that those methods will fail - (KT-39518). Just catching the exception is not good enough, since "findSuperMethods" will - print the whole exception to stderr internally and then spoil the console. - */ - val kPropertyFqName = FqName("kotlin.reflect.KProperty") - if ( - this.parent?.safeAs<PsiClass>()?.implementsInterface(kPropertyFqName) == true && - (this.name == "getSetter" || this.name == "getGetter") - ) { - logger.warn("Skipped lookup of super methods for ${getKotlinFqName()} (KT-39518)") - return emptyArray() - } - findSuperMethods() - } catch (exception: Throwable) { - logger.warn("Failed to lookup of super methods for ${getKotlinFqName()} (KT-39518)") - emptyArray() - } -}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt index e7f8c9ec..e69de29b 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/InheritDocResolver.kt @@ -1,129 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi.parsers - -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.psi.javadoc.PsiDocComment -import com.intellij.psi.javadoc.PsiDocTag -import org.jetbrains.dokka.utilities.DokkaLogger - -internal data class CommentResolutionContext( - val comment: PsiDocComment, - val tag: JavadocTag?, - val name: String? = null, - val parameterIndex: Int? = null, -) - -internal class InheritDocResolver( - private val logger: DokkaLogger -) { - internal fun resolveFromContext(context: CommentResolutionContext) = - when (context.tag) { - JavadocTag.THROWS, JavadocTag.EXCEPTION -> context.name?.let { name -> - resolveThrowsTag( - context.tag, - context.comment, - name - ) - } - JavadocTag.PARAM -> context.parameterIndex?.let { paramIndex -> - resolveParamTag( - context.comment, - paramIndex - ) - } - JavadocTag.DEPRECATED -> resolveGenericTag(context.comment, JavadocTag.DESCRIPTION) - JavadocTag.SEE -> emptyList() - else -> context.tag?.let { tag -> resolveGenericTag(context.comment, tag) } - } - - private fun resolveGenericTag(currentElement: PsiDocComment, tag: JavadocTag) = - when (val owner = currentElement.owner) { - is PsiClass -> lowestClassWithTag(owner, tag) - is PsiMethod -> lowestMethodWithTag(owner, tag) - else -> null - }?.tagsByName(tag)?.flatMap { - when { - it is PsiDocumentationContent && it.psiElement is PsiDocTag -> - it.psiElement.contentElementsWithSiblingIfNeeded() - .map { content -> PsiDocumentationContent(content, it.tag) } - else -> listOf(it) - } - }.orEmpty() - - /** - * Main resolution point for exception like tags - * - * This should be used only with [JavadocTag.EXCEPTION] or [JavadocTag.THROWS] as their resolution path should be the same - */ - private fun resolveThrowsTag( - tag: JavadocTag, - currentElement: PsiDocComment, - exceptionFqName: String - ): List<DocumentationContent> { - val closestDocs = (currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, tag) } - .orEmpty().firstOrNull { - findClosestDocComment(it, logger)?.hasTagWithExceptionOfType(tag, exceptionFqName) == true - } - - return when (closestDocs?.language?.id) { - "kotlin" -> closestDocs.toKdocComment()?.tagsByName(tag, exceptionFqName).orEmpty() - else -> closestDocs?.docComment?.tagsByName(tag)?.flatMap { - when (it) { - is PsiDocTag -> it.contentElementsWithSiblingIfNeeded() - else -> listOf(it) - } - }?.withoutReferenceLink().orEmpty().map { PsiDocumentationContent(it, tag) } - } - } - - private fun resolveParamTag( - currentElement: PsiDocComment, - parameterIndex: Int, - ): List<DocumentationContent> = - (currentElement.owner as? PsiMethod)?.let { method -> lowestMethodsWithTag(method, JavadocTag.PARAM) } - .orEmpty().flatMap { - if (parameterIndex >= it.parameterList.parametersCount || parameterIndex < 0) emptyList() - else { - val closestTag = findClosestDocComment(it, logger) - val hasTag = closestTag?.hasTag(JavadocTag.PARAM) - when { - hasTag != true -> emptyList() - closestTag is JavaDocComment -> resolveJavaParamTag(closestTag, parameterIndex, it) - .withoutReferenceLink().map { PsiDocumentationContent(it, JavadocTag.PARAM) } - closestTag is KotlinDocComment -> resolveKdocTag(closestTag, parameterIndex) - else -> emptyList() - } - } - } - - private fun resolveJavaParamTag(comment: JavaDocComment, parameterIndex: Int, method: PsiMethod) = - comment.comment.tagsByName(JavadocTag.PARAM) - .filterIsInstance<PsiDocTag>().map { it.contentElementsWithSiblingIfNeeded() }.firstOrNull { - it.firstOrNull()?.text == method.parameterList.parameters[parameterIndex].name - }.orEmpty() - - private fun resolveKdocTag(comment: KotlinDocComment, parameterIndex: Int): List<DocumentationContent> = - listOf(comment.tagsByName(JavadocTag.PARAM)[parameterIndex]) - - //if we are in psi class javadoc only inherits docs from classes and not from interfaces - private fun lowestClassWithTag(baseClass: PsiClass, javadocTag: JavadocTag): DocComment? = - baseClass.superClass?.let { - findClosestDocComment(it, logger)?.takeIf { tag -> tag.hasTag(javadocTag) } ?: lowestClassWithTag( - it, - javadocTag - ) - } - - private fun lowestMethodWithTag( - baseMethod: PsiMethod, - javadocTag: JavadocTag, - ): DocComment? = - lowestMethodsWithTag(baseMethod, javadocTag).firstOrNull() - ?.let { it.docComment?.let { JavaDocComment(it) } ?: it.toKdocComment() } - - private fun lowestMethodsWithTag(baseMethod: PsiMethod, javadocTag: JavadocTag) = - baseMethod.findSuperMethods().filter { findClosestDocComment(it, logger)?.hasTag(javadocTag) == true } - - private fun List<PsiElement>.withoutReferenceLink(): List<PsiElement> = drop(1) -} diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt index 59c6f702..e69de29b 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocParser.kt @@ -1,511 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi.parsers - -import com.intellij.lexer.JavaDocTokenTypes -import com.intellij.psi.* -import com.intellij.psi.impl.source.javadoc.PsiDocParamRef -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.impl.source.tree.LazyParseablePsiElement -import com.intellij.psi.impl.source.tree.LeafPsiElement -import com.intellij.psi.javadoc.* -import org.intellij.markdown.MarkdownElementTypes -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.parsers.MarkdownParser -import org.jetbrains.dokka.base.translators.parseHtmlEncodedWithNormalisedSpaces -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.doc.Deprecated -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.utilities.htmlEscape -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName -import org.jetbrains.kotlin.idea.util.CommentSaver.Companion.tokenType -import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace -import org.jetbrains.kotlin.psi.psiUtil.siblings -import org.jsoup.Jsoup -import org.jsoup.nodes.Comment -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode -import java.util.* - -fun interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): DocumentationNode -} - -class JavadocParser( - private val logger: DokkaLogger, - private val resolutionFacade: DokkaResolutionFacade, -) : JavaDocumentationParser { - private val inheritDocResolver = InheritDocResolver(logger) - - /** - * Cache created to make storing entries from kotlin easier. - * - * It has to be mutable to allow for adding entries when @inheritDoc resolves to kotlin code, - * from which we get a DocTags not descriptors. - */ - private var inheritDocSections: MutableMap<UUID, DocumentationNode> = mutableMapOf() - - override fun parseDocumentation(element: PsiNamedElement): DocumentationNode { - return when(val comment = findClosestDocComment(element, logger)){ - is JavaDocComment -> parseDocComment(comment.comment, element) - is KotlinDocComment -> parseDocumentation(comment) - else -> DocumentationNode(emptyList()) - } - } - - internal fun parseDocComment(docComment: PsiDocComment, context: PsiNamedElement): DocumentationNode { - val nodes = listOfNotNull(docComment.getDescription()) + docComment.tags.mapNotNull { tag -> - parseDocTag(tag, docComment, context) - } - return DocumentationNode(nodes) - } - - private fun parseDocumentation(element: KotlinDocComment, parseWithChildren: Boolean = true): DocumentationNode = - MarkdownParser.parseFromKDocTag( - kDocTag = element.comment, - externalDri = { link: String -> - try { - resolveKDocLink( - context = resolutionFacade.resolveSession.bindingContext, - resolutionFacade = resolutionFacade, - fromDescriptor = element.descriptor, - fromSubjectOfTag = null, - qualifiedName = link.split('.') - ).firstOrNull()?.let { DRI.from(it) } - } catch (e1: IllegalArgumentException) { - logger.warn("Couldn't resolve link for $link") - null - } - }, - kdocLocation = null, - parseWithChildren = parseWithChildren - ) - - private fun parseDocTag(tag: PsiDocTag, docComment: PsiDocComment, analysedElement: PsiNamedElement): TagWrapper { - val javadocTag = JavadocTag.lowercaseValueOfOrNull(tag.name) - if (javadocTag == null) { - return emptyTagWrapper(tag, docComment) - } - // Javadoc tag found - val resolutionContext = CommentResolutionContext(comment = docComment, tag = javadocTag) - return when (resolutionContext.tag) { - JavadocTag.PARAM -> { - val name = tag.dataElements.firstOrNull()?.text.orEmpty() - val index = - (analysedElement as? PsiMethod)?.parameterList?.parameters?.map { it.name }?.indexOf(name) - Param( - wrapTagIfNecessary( - convertJavadocElements( - tag.contentElementsWithSiblingIfNeeded().drop(1), - context = resolutionContext.copy(name = name, parameterIndex = index) - ) - ), - name - ) - } - - JavadocTag.THROWS, JavadocTag.EXCEPTION -> { - val resolved = tag.resolveToElement() - val dri = resolved?.let { DRI.from(it) } - val name = resolved?.getKotlinFqName()?.asString() - ?: tag.dataElements.firstOrNull()?.text.orEmpty() - Throws( - root = wrapTagIfNecessary( - convertJavadocElements( - tag.dataElements.drop(1), - context = resolutionContext.copy(name = name) - ) - ), - /* we always would like to have a fully qualified name as name, - * because it will be used as a display name later and we would like to have those unified - * even if documentation states shortened version - * - * Only if dri search fails we should use the provided phrase (since then we are not able to get a fq name) - * */ - name = name, - exceptionAddress = dri - ) - } - - JavadocTag.RETURN -> Return( - wrapTagIfNecessary( - convertJavadocElements( - tag.contentElementsWithSiblingIfNeeded(), - context = resolutionContext - ) - ) - ) - - JavadocTag.AUTHOR -> Author( - wrapTagIfNecessary( - convertJavadocElements( - tag.contentElementsWithSiblingIfNeeded(), - context = resolutionContext - ) - ) - ) // Workaround: PSI returns first word after @author tag as a `DOC_TAG_VALUE_ELEMENT`, then the rest as a `DOC_COMMENT_DATA`, so for `Name Surname` we get them parted - JavadocTag.SEE -> { - val name = - tag.resolveToElement()?.getKotlinFqName()?.asString() ?: tag.referenceElement()?.text.orEmpty() - .removePrefix("#") - getSeeTagElementContent(tag, resolutionContext.copy(name = name)).let { - See( - wrapTagIfNecessary(it.first), - name, - it.second - ) - } - } - - JavadocTag.DEPRECATED -> Deprecated( - wrapTagIfNecessary( - convertJavadocElements( - tag.contentElementsWithSiblingIfNeeded(), - context = resolutionContext - ) - ) - ) - - JavadocTag.SINCE -> Since( - wrapTagIfNecessary( - convertJavadocElements( - tag.contentElementsWithSiblingIfNeeded(), - context = resolutionContext - ) - ) - ) - - else -> emptyTagWrapper(tag, docComment) - } - } - - // Wrapper for unsupported tags https://github.com/Kotlin/dokka/issues/1618 - private fun emptyTagWrapper( - tag: PsiDocTag, - docComment: PsiDocComment - ) = CustomTagWrapper( - wrapTagIfNecessary( - convertJavadocElements( - tag.contentElementsWithSiblingIfNeeded(), - context = CommentResolutionContext(docComment, null) - )), tag.name - ) - - private fun wrapTagIfNecessary(list: List<DocTag>): CustomDocTag = - if (list.size == 1 && (list.first() as? CustomDocTag)?.name == MarkdownElementTypes.MARKDOWN_FILE.name) - list.first() as CustomDocTag - else - CustomDocTag(list, name = MarkdownElementTypes.MARKDOWN_FILE.name) - - private fun getSeeTagElementContent( - tag: PsiDocTag, - context: CommentResolutionContext - ): Pair<List<DocTag>, DRI?> { - val referenceElement = tag.referenceElement() - val linkElement = referenceElement?.toDocumentationLink(context = context) - val content = convertJavadocElements( - tag.dataElements.dropWhile { it is PsiWhiteSpace || (it as? LazyParseablePsiElement)?.tokenType == JavaDocElementType.DOC_REFERENCE_HOLDER || it == referenceElement }, - context = context - ) - return Pair(content, linkElement?.dri) - } - - private fun PsiDocComment.getDescription(): Description? { - return convertJavadocElements( - descriptionElements.asIterable(), - context = CommentResolutionContext(this, JavadocTag.DESCRIPTION) - ).takeIf { it.isNotEmpty() }?.let { - Description(wrapTagIfNecessary(it)) - } - } - - private data class ParserState( - val currentJavadocTag: JavadocTag?, - val previousElement: PsiElement? = null, - val openPreTags: Int = 0, - val closedPreTags: Int = 0 - ) - - private data class ParsingResult(val newState: ParserState, val parsedLine: String? = null) { - constructor(tag: JavadocTag?) : this(ParserState(tag)) - - operator fun plus(other: ParsingResult): ParsingResult = - ParsingResult( - other.newState, - listOfNotNull(parsedLine, other.parsedLine).joinToString(separator = "") - ) - } - - private inner class Parse : (Iterable<PsiElement>, Boolean, CommentResolutionContext) -> List<DocTag> { - val driMap = mutableMapOf<String, DRI>() - - private fun PsiElement.stringify(state: ParserState, context: CommentResolutionContext): ParsingResult = - when (this) { - is PsiReference -> children.fold(ParsingResult(state)) { acc, e -> - acc + e.stringify(acc.newState, context) - } - else -> stringifySimpleElement(state, context) - } - - private fun DocumentationContent.stringify(state: ParserState, context: CommentResolutionContext): ParsingResult = - when(this){ - is PsiDocumentationContent -> psiElement.stringify(state, context) - is DescriptorDocumentationContent -> { - val id = UUID.randomUUID() - inheritDocSections[id] = parseDocumentation(KotlinDocComment(element, descriptor), parseWithChildren = false) - ParsingResult(state, """<inheritdoc id="$id"/>""") - } - else -> throw IllegalStateException("Unrecognised documentation content: $this") - } - - private fun PsiElement.stringifySimpleElement( - state: ParserState, - context: CommentResolutionContext - ): ParsingResult { - val openPre = state.openPreTags + "<pre(\\s+.*)?>".toRegex().findAll(text).toList().size - val closedPre = state.closedPreTags + "</pre>".toRegex().findAll(text).toList().size - val isInsidePre = openPre > closedPre - val parsed = when (this) { - is PsiInlineDocTag -> convertInlineDocTag(this, state.currentJavadocTag, context) - is PsiDocParamRef -> toDocumentationLinkString() - is PsiDocTagValue, - is LeafPsiElement -> stringifyElementAsText(isInsidePre, state.previousElement) - else -> null - } - val previousElement = if (text.trim() == "") state.previousElement else this - return ParsingResult( - state.copy( - previousElement = previousElement, - closedPreTags = closedPre, - openPreTags = openPre - ), parsed - ) - } - - private fun PsiElement.stringifyElementAsText(keepFormatting: Boolean, previousElement: PsiElement? = null) = if (keepFormatting) { - /* - For values in the <pre> tag we try to keep formatting, so only the leading space is trimmed, - since it is there because it separates this line from the leading asterisk - */ - text.let { - if (((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true || (prevSibling as? PsiDocToken)?.isTagName() == true ) && it.firstOrNull() == ' ') - it.drop(1) else it - }.let { - if ((nextSibling as? PsiDocToken)?.isLeadingAsterisk() == true) it.dropLastWhile { it == ' ' } else it - } - } else { - /* - Outside of the <pre> we would like to trim everything from the start and end of a line since - javadoc doesn't care about it. - */ - text.let { - if ((prevSibling as? PsiDocToken)?.isLeadingAsterisk() == true && text.isNotBlank() && previousElement !is PsiInlineDocTag) it?.trimStart() else it - }?.let { - if ((nextSibling as? PsiDocToken)?.isLeadingAsterisk() == true && text.isNotBlank()) it.trimEnd() else it - }?.let { - if (shouldHaveSpaceAtTheEnd()) "$it " else it - } - } - - /** - * We would like to know if we need to have a space after a this tag - * - * The space is required when: - * - tag spans multiple lines, between every line we would need a space - * - * We wouldn't like to render a space if: - * - tag is followed by an end of comment - * - after a tag there is another tag (eg. multiple @author tags) - * - they end with an html tag like: <a href="...">Something</a> since then the space will be displayed in the following text - * - next line starts with a <p> or <pre> token - */ - private fun PsiElement.shouldHaveSpaceAtTheEnd(): Boolean { - val siblings = siblings(withItself = false).toList().filterNot { it.text.trim() == "" } - val nextNotEmptySibling = (siblings.firstOrNull() as? PsiDocToken) - val furtherNotEmptySibling = - (siblings.drop(1).firstOrNull { it is PsiDocToken && !it.isLeadingAsterisk() } as? PsiDocToken) - val lastHtmlTag = text.trim().substringAfterLast("<") - val endsWithAnUnclosedTag = lastHtmlTag.endsWith(">") && !lastHtmlTag.startsWith("</") - - return (nextSibling as? PsiWhiteSpace)?.text?.startsWith("\n ") == true && - (getNextSiblingIgnoringWhitespace() as? PsiDocToken)?.tokenType != JavaDocTokenTypes.INSTANCE.commentEnd() && - nextNotEmptySibling?.isLeadingAsterisk() == true && - furtherNotEmptySibling?.tokenType == JavaDocTokenTypes.INSTANCE.commentData() && - !endsWithAnUnclosedTag - } - - private fun PsiElement.toDocumentationLinkString( - label: String = "" - ): String { - - val dri = reference?.resolve()?.takeIf { it !is PsiParameter }?.let { - val dri = DRI.from(it) - driMap[dri.toString()] = dri - dri.toString() - } ?: UNRESOLVED_PSI_ELEMENT - - return """<a data-dri="${dri.htmlEscape()}">${label.ifBlank{ defaultLabel().text }}</a>""" - } - - private fun convertInlineDocTag( - tag: PsiInlineDocTag, - javadocTag: JavadocTag?, - context: CommentResolutionContext - ) = - when (tag.name) { - "link", "linkplain" -> tag.referenceElement() - ?.toDocumentationLinkString(tag.dataElements.filterIsInstance<PsiDocToken>().joinToString(" ") { - it.stringifyElementAsText(keepFormatting = false).orEmpty() - }) - "code" -> "<code data-inline>${dataElementsAsText(tag)}</code>" - "literal" -> "<literal>${dataElementsAsText(tag)}</literal>" - "index" -> "<index>${tag.children.filterIsInstance<PsiDocTagValue>().joinToString { it.text }}</index>" - "inheritDoc" -> inheritDocResolver.resolveFromContext(context) - ?.fold(ParsingResult(javadocTag)) { result, e -> - result + e.stringify(result.newState, context) - }?.parsedLine.orEmpty() - else -> tag.text - } - - private fun dataElementsAsText(tag: PsiInlineDocTag) = - tag.dataElements.joinToString("") { - it.stringifyElementAsText(keepFormatting = true).orEmpty() - }.htmlEscape() - - private fun createLink(element: Element, children: List<DocTag>): DocTag { - return when { - element.hasAttr("docref") -> - A(children, params = mapOf("docref" to element.attr("docref"))) - element.hasAttr("href") -> - A(children, params = mapOf("href" to element.attr("href"))) - element.hasAttr("data-dri") && driMap.containsKey(element.attr("data-dri")) -> - DocumentationLink(driMap[element.attr("data-dri")]!!, children) - else -> Text(body = children.filterIsInstance<Text>().joinToString { it.body }) - } - } - - private fun createBlock(element: Element, keepFormatting: Boolean = false): List<DocTag> { - val tagName = element.tagName() - val children = element.childNodes() - .flatMap { convertHtmlNode(it, keepFormatting = keepFormatting || tagName == "pre" || tagName == "code") } - - fun ifChildrenPresent(operation: () -> DocTag): List<DocTag> { - return if (children.isNotEmpty()) listOf(operation()) else emptyList() - } - return when (tagName) { - "blockquote" -> ifChildrenPresent { BlockQuote(children) } - "p" -> ifChildrenPresent { P(children) } - "b" -> ifChildrenPresent { B(children) } - "strong" -> ifChildrenPresent { Strong(children) } - "index" -> listOf(Index(children)) - "i" -> ifChildrenPresent { I(children) } - "img" -> listOf( - Img( - children, - element.attributes().associate { (if (it.key == "src") "href" else it.key) to it.value }) - ) - "em" -> listOf(Em(children)) - "code" -> ifChildrenPresent { if(keepFormatting) CodeBlock(children) else CodeInline(children) } - "pre" -> if(children.size == 1) { - when(children.first()) { - is CodeInline -> listOf(CodeBlock(children.first().children)) - is CodeBlock -> listOf(children.first()) - else -> listOf(Pre(children)) - } - } else { - listOf(Pre(children)) - } - "ul" -> ifChildrenPresent { Ul(children) } - "ol" -> ifChildrenPresent { Ol(children) } - "li" -> listOf(Li(children)) - "dl" -> ifChildrenPresent { Dl(children) } - "dt" -> listOf(Dt(children)) - "dd" -> listOf(Dd(children)) - "a" -> listOf(createLink(element, children)) - "table" -> ifChildrenPresent { Table(children) } - "tr" -> ifChildrenPresent { Tr(children) } - "td" -> listOf(Td(children)) - "thead" -> listOf(THead(children)) - "tbody" -> listOf(TBody(children)) - "tfoot" -> listOf(TFoot(children)) - "caption" -> ifChildrenPresent { Caption(children) } - "inheritdoc" -> { - val id = UUID.fromString(element.attr("id")) - val section = inheritDocSections[id] - val parsed = section?.children?.flatMap { it.root.children }.orEmpty() - if(parsed.size == 1 && parsed.first() is P){ - parsed.first().children - } else { - parsed - } - } - "h1" -> ifChildrenPresent { H1(children) } - "h2" -> ifChildrenPresent { H2(children) } - "h3" -> ifChildrenPresent { H3(children) } - "var" -> ifChildrenPresent { Var(children) } - "u" -> ifChildrenPresent { U(children) } - else -> listOf(Text(body = element.ownText())) - } - } - - private fun convertHtmlNode(node: Node, keepFormatting: Boolean = false): List<DocTag> = when (node) { - is TextNode -> (if (keepFormatting) { - node.wholeText.takeIf { it.isNotBlank() }?.let { listOf(Text(body = it)) } - } else { - node.wholeText.parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces = true) - }).orEmpty() - is Comment -> listOf(Text(body = node.outerHtml(), params = DocTag.contentTypeParam("html"))) - is Element -> createBlock(node, keepFormatting) - else -> emptyList() - } - - override fun invoke( - elements: Iterable<PsiElement>, - asParagraph: Boolean, - context: CommentResolutionContext - ): List<DocTag> = - elements.fold(ParsingResult(context.tag)) { acc, e -> - acc + e.stringify(acc.newState, context) - }.parsedLine?.let { - val trimmed = it.trim() - val toParse = if (asParagraph) "<p>$trimmed</p>" else trimmed - Jsoup.parseBodyFragment(toParse).body().childNodes().flatMap { convertHtmlNode(it) } - }.orEmpty() - } - - private fun convertJavadocElements( - elements: Iterable<PsiElement>, - asParagraph: Boolean = true, - context: CommentResolutionContext - ): List<DocTag> = - Parse()(elements, asParagraph, context) - - private fun PsiDocToken.isSharpToken() = tokenType == JavaDocTokenType.DOC_TAG_VALUE_SHARP_TOKEN - - private fun PsiDocToken.isTagName() = tokenType == JavaDocTokenType.DOC_TAG_NAME - - private fun PsiDocToken.isLeadingAsterisk() = tokenType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS - - private fun PsiElement.toDocumentationLink(labelElement: PsiElement? = null, context: CommentResolutionContext) = - resolveToGetDri()?.let { - val dri = DRI.from(it) - val label = labelElement ?: defaultLabel() - DocumentationLink(dri, convertJavadocElements(listOfNotNull(label), asParagraph = false, context)) - } - - private fun PsiDocTag.referenceElement(): PsiElement? = - linkElement()?.referenceElementOrSelf() - - private fun PsiElement.defaultLabel() = children.firstOrNull { - it is PsiDocToken && it.text.isNotBlank() && !it.isSharpToken() - } ?: this - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - companion object { - private const val UNRESOLVED_PSI_ELEMENT = "UNRESOLVED_PSI_ELEMENT" - } -} diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt index 5b3be7e3..e69de29b 100644 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt +++ b/plugins/base/src/main/kotlin/translators/psi/parsers/JavadocTag.kt @@ -1,32 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi.parsers - -internal enum class JavadocTag { - PARAM, THROWS, RETURN, AUTHOR, SEE, DEPRECATED, EXCEPTION, HIDE, SINCE, - - /** - * Artificial tag created to handle tag-less section - */ - DESCRIPTION,; - - override fun toString(): String = super.toString().toLowerCase() - - /* Missing tags: - SERIAL, - SERIAL_DATA, - SERIAL_FIELD, - SINCE, - VERSION - */ - - companion object { - private val name2Value = values().associateBy { it.name.toLowerCase() } - - /** - * Lowercase-based `Enum.valueOf` variation for [JavadocTag]. - * - * Note: tags are [case-sensitive](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html) in Java, - * thus we are not allowed to use case-insensitive or uppercase-based lookup. - */ - fun lowercaseValueOfOrNull(name: String): JavadocTag? = name2Value[name] - } -} diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt deleted file mode 100644 index c4c8cbb2..00000000 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/PsiCommentsUtils.kt +++ /dev/null @@ -1,146 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi.parsers - -import com.intellij.psi.* -import com.intellij.psi.javadoc.PsiDocComment -import com.intellij.psi.javadoc.PsiDocTag -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.translators.psi.findSuperMethodsOrEmptyArray -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName -import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull - -internal interface DocComment { - fun hasTag(tag: JavadocTag): Boolean - fun hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean - fun tagsByName(tag: JavadocTag, param: String? = null): List<DocumentationContent> -} - -internal data class JavaDocComment(val comment: PsiDocComment) : DocComment { - override fun hasTag(tag: JavadocTag): Boolean = comment.hasTag(tag) - override fun hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean = - comment.hasTag(tag) && comment.tagsByName(tag).firstIsInstanceOrNull<PsiDocTag>() - ?.resolveToElement() - ?.getKotlinFqName()?.asString() == exceptionFqName - - override fun tagsByName(tag: JavadocTag, param: String?): List<DocumentationContent> = - comment.tagsByName(tag).map { PsiDocumentationContent(it, tag) } -} - -internal data class KotlinDocComment(val comment: KDocTag, val descriptor: DeclarationDescriptor) : DocComment { - override fun hasTag(tag: JavadocTag): Boolean = - when (tag) { - JavadocTag.DESCRIPTION -> comment.getContent().isNotEmpty() - else -> tagsWithContent.any { it.text.startsWith("@$tag") } - } - - override fun hasTagWithExceptionOfType(tag: JavadocTag, exceptionFqName: String): Boolean = - tagsWithContent.any { it.hasExceptionWithName(tag, exceptionFqName) } - - override fun tagsByName(tag: JavadocTag, param: String?): List<DocumentationContent> = - when (tag) { - JavadocTag.DESCRIPTION -> listOf(DescriptorDocumentationContent(descriptor, comment, tag)) - else -> comment.children.mapNotNull { (it as? KDocTag) } - .filter { it.name == "$tag" && param?.let { param -> it.hasExceptionWithName(param) } != false } - .map { DescriptorDocumentationContent(descriptor, it, tag) } - } - - private val tagsWithContent: List<KDocTag> = comment.children.mapNotNull { (it as? KDocTag) } - - private fun KDocTag.hasExceptionWithName(tag: JavadocTag, exceptionFqName: String) = - text.startsWith("@$tag") && hasExceptionWithName(exceptionFqName) - - private fun KDocTag.hasExceptionWithName(exceptionFqName: String) = - getSubjectName() == exceptionFqName -} - -internal interface DocumentationContent { - val tag: JavadocTag -} - -internal data class PsiDocumentationContent(val psiElement: PsiElement, override val tag: JavadocTag) : - DocumentationContent - -internal data class DescriptorDocumentationContent( - val descriptor: DeclarationDescriptor, - val element: KDocTag, - override val tag: JavadocTag -) : DocumentationContent - -internal fun PsiDocComment.hasTag(tag: JavadocTag): Boolean = - when (tag) { - JavadocTag.DESCRIPTION -> descriptionElements.isNotEmpty() - else -> findTagByName(tag.toString()) != null - } - -internal fun PsiDocComment.tagsByName(tag: JavadocTag): List<PsiElement> = - when (tag) { - JavadocTag.DESCRIPTION -> descriptionElements.toList() - else -> findTagsByName(tag.toString()).toList() - } - -internal fun findClosestDocComment(element: PsiNamedElement, logger: DokkaLogger): DocComment? { - (element as? PsiDocCommentOwner)?.docComment?.run { return JavaDocComment(this) } - element.toKdocComment()?.run { return this } - - if (element is PsiMethod) { - val superMethods = element.findSuperMethodsOrEmptyArray(logger) - if (superMethods.isEmpty()) return null - - if (superMethods.size == 1) { - return findClosestDocComment(superMethods.single(), logger) - } - - val superMethodDocumentation = superMethods.map { method -> findClosestDocComment(method, logger) }.distinct() - if (superMethodDocumentation.size == 1) { - return superMethodDocumentation.single() - } - - logger.debug( - "Conflicting documentation for ${DRI.from(element)}" + - "${superMethods.map { DRI.from(it) }}" - ) - - /* Prioritize super class over interface */ - val indexOfSuperClass = superMethods.indexOfFirst { method -> - val parent = method.parent - if (parent is PsiClass) !parent.isInterface - else false - } - - return if (indexOfSuperClass >= 0) superMethodDocumentation[indexOfSuperClass] - else superMethodDocumentation.first() - } - return element.children.firstIsInstanceOrNull<PsiDocComment>()?.let { JavaDocComment(it) } -} - -internal fun PsiNamedElement.toKdocComment(): KotlinDocComment? = - (navigationElement as? KtElement)?.findKDoc { DescriptorToSourceUtils.descriptorToDeclaration(it) } - ?.run { - (this@toKdocComment.navigationElement as? KtDeclaration)?.descriptor?.let { - KotlinDocComment( - this, - it - ) - } - } - -internal fun PsiDocTag.contentElementsWithSiblingIfNeeded(): List<PsiElement> = if (dataElements.isNotEmpty()) { - listOfNotNull( - dataElements[0], - dataElements[0].nextSibling?.takeIf { it.text != dataElements.drop(1).firstOrNull()?.text }, - *dataElements.drop(1).toTypedArray() - ) -} else { - emptyList() -} - -internal fun PsiDocTag.resolveToElement(): PsiElement? = - dataElements.firstOrNull()?.firstChild?.referenceElementOrSelf()?.resolveToGetDri() diff --git a/plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt b/plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt deleted file mode 100644 index 3cc16251..00000000 --- a/plugins/base/src/main/kotlin/translators/psi/parsers/exceptionTag.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.jetbrains.dokka.base.translators.psi.parsers - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiJavaCodeReferenceElement -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.util.PsiTreeUtil - -internal fun PsiElement.referenceElementOrSelf(): PsiElement? = - if (node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { - PsiTreeUtil.findChildOfType(this, PsiJavaCodeReferenceElement::class.java) - } else this - -internal fun PsiElement.resolveToGetDri(): PsiElement? = - reference?.resolve()
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/CollectionExtensions.kt b/plugins/base/src/main/kotlin/utils/CollectionExtensions.kt index 0de4b5b1..6fc5271f 100644 --- a/plugins/base/src/main/kotlin/translators/CollectionExtensions.kt +++ b/plugins/base/src/main/kotlin/utils/CollectionExtensions.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.base.translators +package org.jetbrains.dokka.base.utils // TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 internal inline fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R? { diff --git a/plugins/base/src/test/kotlin/basic/DRITest.kt b/plugins/base/src/test/kotlin/basic/DRITest.kt index 3a4ff84d..9c443567 100644 --- a/plugins/base/src/test/kotlin/basic/DRITest.kt +++ b/plugins/base/src/test/kotlin/basic/DRITest.kt @@ -1,5 +1,6 @@ package basic +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.* import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.Nullable @@ -8,7 +9,6 @@ import org.jetbrains.dokka.model.* import org.jetbrains.dokka.pages.ClasslikePageNode import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.MemberPageNode -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test diff --git a/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt b/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt index 35e4e52f..d4e307be 100644 --- a/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt +++ b/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt @@ -1,10 +1,9 @@ package basic +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.pages.ClasslikePageNode import org.jetbrains.dokka.pages.ModulePageNode -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import kotlin.test.assertEquals class DokkaBasicTests : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt b/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt index 682a257e..bd8a79ef 100644 --- a/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt +++ b/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt @@ -1,8 +1,8 @@ package basic import org.jetbrains.dokka.DokkaException -import org.jetbrains.dokka.testApi.logger.TestLogger import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.testApi.logger.TestLogger import org.jetbrains.dokka.utilities.DokkaConsoleLogger import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.utilities.LoggingLevel diff --git a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt index f7f7eb66..2659fd86 100644 --- a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt +++ b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt @@ -1,15 +1,14 @@ package content.annotations import matchers.content.* -import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.base.utils.firstNotNullOfOrNull import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.ContentText import org.jetbrains.dokka.pages.MemberPageNode import org.jetbrains.dokka.pages.PackagePageNode -import org.jetbrains.kotlin.util.firstNotNullResult import org.junit.jupiter.api.Test import utils.ParamAttributes import utils.assertNotNull @@ -223,7 +222,7 @@ class ContentForAnnotationsTest : BaseAbstractTest() { )) val property = modules.flatMap { it.packages }.flatMap { it.properties }.first() val annotation = property.extra[Annotations]?.let { - it.directAnnotations.entries.firstNotNullResult { (_, annotations) -> annotations.firstOrNull() } + it.directAnnotations.entries.firstNotNullOfOrNull { (_, annotations) -> annotations.firstOrNull() } } val annotationParams = annotation?.params ?: emptyMap() @@ -284,10 +283,10 @@ class ContentForAnnotationsTest : BaseAbstractTest() { val property = modules.flatMap { it.packages }.flatMap { it.properties }.first() val getterAnnotation = property.getter?.extra?.get(Annotations)?.let { - it.directAnnotations.entries.firstNotNullResult { (_, annotations) -> annotations.firstOrNull() } + it.directAnnotations.entries.firstNotNullOfOrNull { (_, annotations) -> annotations.firstOrNull() } } val setterAnnotation = property.getter?.extra?.get(Annotations)?.let { - it.directAnnotations.entries.firstNotNullResult { (_, annotations) -> annotations.firstOrNull() } + it.directAnnotations.entries.firstNotNullOfOrNull { (_, annotations) -> annotations.firstOrNull() } } assertEquals(expectedAnnotation("xd"), getterAnnotation) diff --git a/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt b/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt index 8b311893..83517254 100644 --- a/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt +++ b/plugins/base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt @@ -1,13 +1,13 @@ package content.annotations import matchers.content.* -import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.base.transformers.documentables.deprecatedAnnotation -import org.jetbrains.dokka.pages.ContentStyle import org.jetbrains.dokka.base.transformers.documentables.isDeprecated import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.ContentStyle import org.junit.jupiter.api.Test import utils.ParamAttributes import utils.bareSignature diff --git a/plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt b/plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt index d658b50b..0b36031c 100644 --- a/plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt +++ b/plugins/base/src/test/kotlin/content/annotations/SinceKotlinTest.kt @@ -9,7 +9,9 @@ import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.doc.CustomTagWrapper import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.pages.ContentPage -import org.junit.jupiter.api.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test import signatures.AbstractRenderingTest import utils.ParamAttributes import utils.TestOutputWriterPlugin diff --git a/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt b/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt index f59ba529..14a36611 100644 --- a/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt +++ b/plugins/base/src/test/kotlin/content/exceptions/ContentForExceptions.kt @@ -6,7 +6,6 @@ import org.jetbrains.dokka.PluginConfigurationImpl import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.kotlin.utils.addIfNotNull import org.junit.jupiter.api.Test import utils.ParamAttributes import utils.bareSignature @@ -47,7 +46,7 @@ class ContentForExceptions : BaseAbstractTest() { sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt") } } - pluginsConfigurations.addIfNotNull( + pluginsConfigurations.add( PluginConfigurationImpl( DokkaBase::class.qualifiedName!!, DokkaConfiguration.SerializationFormat.JSON, @@ -431,4 +430,4 @@ class ContentForExceptions : BaseAbstractTest() { private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) { assertEquals(1, this.size) assertEquals(expectedName, this.first().name) -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt b/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt index 09c927bd..e5059073 100644 --- a/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt +++ b/plugins/base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt @@ -5,7 +5,6 @@ import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.PluginConfigurationImpl import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.kotlin.utils.addIfNotNull import org.junit.jupiter.api.Test import utils.classSignature import utils.findTestType @@ -45,7 +44,7 @@ class ContentForInheritorsTest : BaseAbstractTest() { sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt") } } - pluginsConfigurations.addIfNotNull( + pluginsConfigurations.add( PluginConfigurationImpl( DokkaBase::class.qualifiedName!!, DokkaConfiguration.SerializationFormat.JSON, @@ -491,4 +490,4 @@ class ContentForInheritorsTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt index 742c801f..e74cb49d 100644 --- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt +++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt @@ -9,7 +9,7 @@ import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.model.doc.Param import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.dokka.utilities.firstIsInstanceOrNull import org.junit.jupiter.api.Test import utils.* import kotlin.test.assertEquals diff --git a/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt b/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt index 3497317c..a39ade25 100644 --- a/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt +++ b/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt @@ -2,10 +2,9 @@ package content.signatures import matchers.content.* import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.PackagePageNode import org.junit.jupiter.api.Test import utils.ParamAttributes import utils.bareSignature diff --git a/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt b/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt index b3d56d2e..1602a7cf 100644 --- a/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt +++ b/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt @@ -2,14 +2,11 @@ package enums import org.jetbrains.dokka.SourceLinkDefinitionImpl import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.model.DEnum -import org.jetbrains.dokka.model.ObviousMember import org.junit.jupiter.api.Test import signatures.renderedContent import utils.TestOutputWriterPlugin import java.net.URL import kotlin.test.assertEquals -import kotlin.test.assertNotNull class JavaEnumsTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt index 3b2720c9..a49a29de 100644 --- a/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt +++ b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt @@ -2,19 +2,19 @@ package enums import matchers.content.* import org.jetbrains.dokka.SourceLinkDefinitionImpl -import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.model.* -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.ClasslikePage +import org.jetbrains.dokka.pages.ClasslikePageNode +import org.jetbrains.dokka.pages.ContentGroup import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import signatures.renderedContent import utils.TestOutputWriter import utils.TestOutputWriterPlugin -import java.io.File import java.net.URL class KotlinEnumsTest : BaseAbstractTest() { @@ -347,7 +347,7 @@ class KotlinEnumsTest : BaseAbstractTest() { configuration ) { pagesTransformationStage = { m -> - val entryNode = m.children.first { it.name == "testpackage" }.children.first { it.name == "TestEnum" }.children.firstIsInstance<ClasslikePageNode>() + val entryNode = m.children.first { it.name == "testpackage" }.children.first { it.name == "TestEnum" }.children.filterIsInstance<ClasslikePageNode>().first() val signature = (entryNode.content as ContentGroup).dfs { it is ContentGroup && it.dci.toString() == "[testpackage/TestEnum.E1///PointingToDeclaration/{\"org.jetbrains.dokka.links.EnumEntryDRIExtra\":{\"key\":\"org.jetbrains.dokka.links.EnumEntryDRIExtra\"}}][Cover]" } as ContentGroup signature.assertNode { diff --git a/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt b/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt index 13d4b456..ff1b7989 100644 --- a/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt +++ b/plugins/base/src/test/kotlin/expectActuals/ExpectActualsTest.kt @@ -1,11 +1,10 @@ package expectActuals +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.ClasslikePageNode -import org.jetbrains.dokka.pages.PageNode -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test class ExpectActualsTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/filter/JavaFileFilterTest.kt b/plugins/base/src/test/kotlin/filter/JavaFileFilterTest.kt index f618292c..a1a242c5 100644 --- a/plugins/base/src/test/kotlin/filter/JavaFileFilterTest.kt +++ b/plugins/base/src/test/kotlin/filter/JavaFileFilterTest.kt @@ -3,7 +3,6 @@ package filter import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import kotlin.test.assertEquals class JavaFileFilterTest : BaseAbstractTest() { @Test diff --git a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt index b9b1dc1e..de7c4e43 100644 --- a/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt +++ b/plugins/base/src/test/kotlin/filter/KotlinArrayDocumentableReplacerTest.kt @@ -1,9 +1,11 @@ package filter -import com.jetbrains.rd.util.firstOrNull import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.DClass +import org.jetbrains.dokka.model.FunctionalTypeConstructor +import org.jetbrains.dokka.model.GenericTypeConstructor +import org.jetbrains.dokka.model.Invariance import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -124,7 +126,7 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { val arrTypealias = it.firstOrNull()?.packages?.firstOrNull()?.typealiases?.firstOrNull() Assertions.assertEquals(GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), - arrTypealias?.underlyingType?.firstOrNull()?.value) + arrTypealias?.underlyingType?.values?.firstOrNull()) } } } @@ -196,4 +198,4 @@ class KotlinArrayDocumentableReplacerTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt index d7ac8b97..be75e01f 100644 --- a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt +++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt @@ -2,14 +2,11 @@ package linkableContent import org.jetbrains.dokka.SourceLinkDefinitionImpl import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer import org.jetbrains.dokka.model.WithGenerics import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.doc.Text import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.utils.addToStdlib.cast -import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jsoup.Jsoup import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -135,8 +132,8 @@ class LinkableContentTest : BaseAbstractTest() { Assertions.assertEquals(2, packageChildren.size) packageChildren.forEach { val name = it.name.substringBefore("Class") - val signature = it.safeAs<ClasslikePageNode>()?.content?.dfs { it is ContentGroup && it.dci.kind == ContentKind.Symbol }.assertNotNull("signature") - val crl = signature.children.last().children[1].safeAs<ContentResolvedLink>() + val signature = (it as? ClasslikePageNode)?.content?.dfs { it is ContentGroup && it.dci.kind == ContentKind.Symbol }.assertNotNull("signature") + val crl = signature.children.last().children[1] as? ContentResolvedLink Assertions.assertEquals( "https://github.com/user/repo/tree/master/src/${name.toLowerCase()}Main/kotlin/${name}Class.kt#L3", crl?.address @@ -187,9 +184,10 @@ class LinkableContentTest : BaseAbstractTest() { } testFromData(configuration) { - renderingStage = { rootPageNode, dokkaContext -> - val newRoot = DefaultSamplesTransformer(dokkaContext).invoke(rootPageNode) - + renderingStage = { rootPageNode, _ -> + // TODO [beresnev] :((( +// val newRoot = DefaultSamplesTransformer(dokkaContext).invoke(rootPageNode) + val newRoot = rootPageNode val moduleChildren = newRoot.children Assertions.assertEquals(1, moduleChildren.size) val packageChildren = moduleChildren.first().children @@ -199,12 +197,12 @@ class LinkableContentTest : BaseAbstractTest() { val classChildren = pageNode.children Assertions.assertEquals(2, classChildren.size) val function = classChildren.find { it.name == "printWithExclamation" } - val text = function.cast<MemberPageNode>().content.cast<ContentGroup>().children.last() - .cast<ContentDivergentGroup>().children.single() - .cast<ContentDivergentInstance>().after - .cast<ContentGroup>().children.last() - .cast<ContentGroup>().children.single() - .cast<ContentCodeBlock>().children.single().cast<ContentText>().text + val text = (function as MemberPageNode).content.let { it as ContentGroup }.children.last() + .let { it as ContentDivergentGroup }.children.single().after + .let { it as ContentGroup }.children.last() + .let { it as ContentGroup }.children.single() + .let { it as ContentCodeBlock }.children.single() + .let { it as ContentText }.text Assertions.assertEquals( """|import p2.${name}Class |fun main() { @@ -245,16 +243,20 @@ class LinkableContentTest : BaseAbstractTest() { ) { renderingStage = { module, _ -> val sample = module.children.single { it.name == "test" } - .children.single { it.name == "Sample" }.cast<ClasslikePageNode>() + .children.single { it.name == "Sample" } as ClasslikePageNode val foo = sample - .children.single { it.name == "SampleInner" }.cast<ClasslikePageNode>() - .children.single { it.name == "foo" }.cast<MemberPageNode>() + .children + .single { it.name == "SampleInner" } + .let { it as ClasslikePageNode } + .children + .single { it.name == "foo" } + .let { it as MemberPageNode } val returnTypeNode = foo.content.dfs { - val link = it.safeAs<ContentDRILink>()?.children - val child = link?.first().safeAs<ContentText>() + val link = (it as? ContentDRILink)?.children + val child = link?.first() as? ContentText child?.text == "S" - }?.safeAs<ContentDRILink>() + } as? ContentDRILink Assertions.assertEquals( (sample.documentables.firstOrNull() as WithGenerics).generics.first().dri, diff --git a/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt b/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt index f95d9860..14875832 100644 --- a/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt +++ b/plugins/base/src/test/kotlin/linking/EnumValuesLinkingTest.kt @@ -7,13 +7,12 @@ import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.doc.DocumentationLink import org.jetbrains.dokka.pages.ContentDRILink import org.jetbrains.dokka.pages.ContentPage -import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jsoup.Jsoup -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Test -import java.nio.file.Paths import utils.TestOutputWriterPlugin -import kotlin.AssertionError +import java.nio.file.Paths class EnumValuesLinkingTest : BaseAbstractTest() { @@ -80,13 +79,13 @@ class EnumValuesLinkingTest : BaseAbstractTest() { } assertEquals( - javaLinker.documentation.values.single().children[0].children[1].children[1].safeAs<DocumentationLink>()?.dri, - kotlinLinker.documentation.values.single().children[0].children[0].children[5].safeAs<DocumentationLink>()?.dri + javaLinker.documentation.values.single().children[0].children[1].children[1].let { it as? DocumentationLink }?.dri, + kotlinLinker.documentation.values.single().children[0].children[0].children[5].let { it as? DocumentationLink }?.dri ) assertEquals( - javaLinker.documentation.values.single().children[0].children[2].children[1].safeAs<DocumentationLink>()?.dri, - kotlinLinker.documentation.values.single().children[0].children[0].children[9].safeAs<DocumentationLink>()?.dri + javaLinker.documentation.values.single().children[0].children[2].children[1].let { it as? DocumentationLink }?.dri, + kotlinLinker.documentation.values.single().children[0].children[0].children[9].let { it as? DocumentationLink }?.dri ) } diff --git a/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt index 071997fc..e9e0871a 100644 --- a/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/AndroidExternalLocationProviderTest.kt @@ -5,11 +5,11 @@ import org.jetbrains.dokka.base.resolvers.external.javadoc.AndroidExternalLocati import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import java.net.URL diff --git a/plugins/base/src/test/kotlin/locationProvider/DefaultExternalLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DefaultExternalLocationProviderTest.kt index 3a8aafa7..870d8cf3 100644 --- a/plugins/base/src/test/kotlin/locationProvider/DefaultExternalLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/DefaultExternalLocationProviderTest.kt @@ -3,11 +3,11 @@ package locationProvider import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import java.net.URL diff --git a/plugins/base/src/test/kotlin/locationProvider/Dokka010ExternalLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/Dokka010ExternalLocationProviderTest.kt index 80950747..241e0919 100644 --- a/plugins/base/src/test/kotlin/locationProvider/Dokka010ExternalLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/Dokka010ExternalLocationProviderTest.kt @@ -3,11 +3,11 @@ package locationProvider import org.jetbrains.dokka.base.resolvers.external.Dokka010ExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import java.net.URL diff --git a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt index 59406e1e..f43a4cd7 100644 --- a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt @@ -5,7 +5,6 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.kotlin.backend.common.push import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -49,7 +48,7 @@ class DokkaLocationProviderTest : BaseAbstractTest() { class ModulesDsl(val pages: MutableList<ModulePageNode> = mutableListOf()) { fun modulePage(name: String, fn: PackageDsl.() -> Unit) { val packages = PackageDsl().also { it.fn() } - pages.push( + pages.add( ModulePageNode( name = name, children = packages.pages, @@ -63,7 +62,7 @@ class DokkaLocationProviderTest : BaseAbstractTest() { class PackageDsl(val pages: MutableList<PackagePageNode> = mutableListOf()) { fun packagePage(name: String, fn: ClassDsl.() -> Unit) { val packages = ClassDsl().also { it.fn() } - pages.push( + pages.add( PackagePageNode( name = name, children = packages.pages, @@ -77,7 +76,7 @@ class DokkaLocationProviderTest : BaseAbstractTest() { @TestNavigationDSL class ClassDsl(val pages: MutableList<ClasslikePageNode> = mutableListOf()) { fun classPage(name: String) { - pages.push( + pages.add( ClasslikePageNode( name = name, children = emptyList(), diff --git a/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt index 95179e22..27e51caf 100644 --- a/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/JavadocExternalLocationProviderTest.kt @@ -4,9 +4,12 @@ import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvid import org.jetbrains.dokka.base.resolvers.external.javadoc.JavadocExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList -import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DRIExtraContainer +import org.jetbrains.dokka.links.EnumEntryDRIExtra +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.dokka.plugability.DokkaContext import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import java.net.URL diff --git a/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt b/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt index aefe913c..031dd101 100644 --- a/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/MultiModuleLinkingTest.kt @@ -3,11 +3,10 @@ package locationProvider import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation import org.jetbrains.dokka.base.resolvers.shared.PackageList +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import java.net.URL diff --git a/plugins/base/src/test/kotlin/markdown/KDocTest.kt b/plugins/base/src/test/kotlin/markdown/KDocTest.kt index fddd41e7..69885931 100644 --- a/plugins/base/src/test/kotlin/markdown/KDocTest.kt +++ b/plugins/base/src/test/kotlin/markdown/KDocTest.kt @@ -1,10 +1,10 @@ package markdown +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.DPackage import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.pages.ModulePageNode -import org.junit.jupiter.api.Assertions.* -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.jupiter.api.Assertions.assertEquals abstract class KDocTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/markdown/LinkTest.kt b/plugins/base/src/test/kotlin/markdown/LinkTest.kt index f141bb06..526ff0eb 100644 --- a/plugins/base/src/test/kotlin/markdown/LinkTest.kt +++ b/plugins/base/src/test/kotlin/markdown/LinkTest.kt @@ -1,13 +1,13 @@ package markdown +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.* import org.jetbrains.dokka.model.WithGenerics import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.pages.ClasslikePageNode import org.jetbrains.dokka.pages.ContentDRILink import org.jetbrains.dokka.pages.MemberPageNode -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.links.* -import org.jetbrains.dokka.model.doc.* import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Test diff --git a/plugins/base/src/test/kotlin/markdown/ParserTest.kt b/plugins/base/src/test/kotlin/markdown/ParserTest.kt index fd723263..41b086ee 100644 --- a/plugins/base/src/test/kotlin/markdown/ParserTest.kt +++ b/plugins/base/src/test/kotlin/markdown/ParserTest.kt @@ -1,8 +1,9 @@ package org.jetbrains.dokka.tests import markdown.KDocTest -import org.intellij.markdown.MarkdownElementTypes -import org.jetbrains.dokka.base.parsers.MarkdownParser + +import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME +import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser import org.jetbrains.dokka.model.doc.* import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -25,7 +26,7 @@ class ParserTest : KDocTest() { Description( CustomDocTag( listOf(P(listOf(Text("This is simple test of string Next line")))), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -52,7 +53,7 @@ class ParserTest : KDocTest() { ) ) ), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -81,7 +82,7 @@ class ParserTest : KDocTest() { B(listOf(I(listOf(Text("line"))))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -99,7 +100,7 @@ class ParserTest : KDocTest() { Description( CustomDocTag( listOf(P(listOf(Text("This is simple text with: colon!")))), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -119,7 +120,7 @@ class ParserTest : KDocTest() { Description( CustomDocTag( listOf(P(listOf(Text("Text and String")))), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -143,7 +144,7 @@ class ParserTest : KDocTest() { listOf( P(listOf(Text("Paragraph number one"))), P(listOf(Text("Paragraph"), Br, Text("number two"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -159,7 +160,7 @@ class ParserTest : KDocTest() { Description( CustomDocTag( listOf(P(listOf(I(listOf(Text("text")))))), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -175,7 +176,7 @@ class ParserTest : KDocTest() { Description( CustomDocTag( listOf(P(listOf(Text("text_with_underscores")))), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -191,7 +192,7 @@ class ParserTest : KDocTest() { Description( CustomDocTag( listOf(P(listOf(I(listOf(Text("text")))))), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -220,7 +221,7 @@ class ParserTest : KDocTest() { Text("x\".") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -249,7 +250,7 @@ class ParserTest : KDocTest() { Text("x\".") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -266,7 +267,7 @@ class ParserTest : KDocTest() { CustomDocTag( listOf( P(listOf(Text("Embedded*Star"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -292,7 +293,7 @@ class ParserTest : KDocTest() { Li(listOf(P(listOf(Text("list item 2"))))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -319,7 +320,7 @@ class ParserTest : KDocTest() { Li(listOf(P(listOf(Text("list item 2"), Br, Text("continue 2"))))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -366,7 +367,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -414,7 +415,7 @@ class ParserTest : KDocTest() { ) ), P(listOf(Text("New paragraph"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -440,7 +441,7 @@ class ParserTest : KDocTest() { ), mapOf("start" to "1") ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -467,7 +468,7 @@ class ParserTest : KDocTest() { ), mapOf("start" to "9") ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -495,7 +496,7 @@ class ParserTest : KDocTest() { ), mapOf("start" to "2") ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -543,7 +544,7 @@ class ParserTest : KDocTest() { ), mapOf("start" to "1") ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -594,7 +595,7 @@ class ParserTest : KDocTest() { mapOf("start" to "1") ), P(listOf(Text("New paragraph"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -644,7 +645,7 @@ class ParserTest : KDocTest() { mapOf("start" to "1") ), P(listOf(Text("New paragraph"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -668,7 +669,7 @@ class ParserTest : KDocTest() { H1(listOf(Text("Header 1"))), P(listOf(Text("Following text"))), P(listOf(Text("New paragraph"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -710,7 +711,7 @@ class ParserTest : KDocTest() { P(listOf(Text("Text 5"))), H6(listOf(Text("Header 6"))), P(listOf(Text("Text 6"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -736,7 +737,7 @@ class ParserTest : KDocTest() { B(listOf(Text("line 2"))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -771,7 +772,7 @@ class ParserTest : KDocTest() { HorizontalRule, P(listOf(Text("text 4"))), HorizontalRule - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -809,7 +810,7 @@ class ParserTest : KDocTest() { P(listOf(Text("Quote"))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -854,7 +855,7 @@ class ParserTest : KDocTest() { P(listOf(Text("Quote"))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -916,7 +917,7 @@ class ParserTest : KDocTest() { P(listOf(Text("Quote"))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -941,7 +942,7 @@ class ParserTest : KDocTest() { Text(" Sample text") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -982,7 +983,7 @@ class ParserTest : KDocTest() { mapOf("lang" to "kotlin") ), P(listOf(Text("Sample text"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1009,7 +1010,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1035,7 +1036,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1063,7 +1064,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1091,7 +1092,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1121,7 +1122,7 @@ class ParserTest : KDocTest() { Text(".") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1151,7 +1152,7 @@ class ParserTest : KDocTest() { Text(" and sometimes example.com (but not on Github, for example).") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1240,7 +1241,7 @@ class ParserTest : KDocTest() { ) ), P(listOf(Text("Some text to show that the reference links can follow later."))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1261,7 +1262,7 @@ class ParserTest : KDocTest() { Text("text text") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1288,7 +1289,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1315,7 +1316,7 @@ class ParserTest : KDocTest() { A(listOf(Text("link to Google!")), mapOf("href" to "http://google.com")) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1348,7 +1349,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1413,7 +1414,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1437,7 +1438,7 @@ class ParserTest : KDocTest() { Strikethrough(listOf(Text("strikethroughed"))) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1464,7 +1465,7 @@ class ParserTest : KDocTest() { ) ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1514,7 +1515,7 @@ class ParserTest : KDocTest() { ) ) ), - name = MarkdownElementTypes.MARKDOWN_FILE.name + name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) @@ -1537,7 +1538,7 @@ class ParserTest : KDocTest() { CodeInline(listOf(Text("``` "))), ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt index 920dea10..6a3e80cd 100644 --- a/plugins/base/src/test/kotlin/model/ClassesTest.kt +++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt @@ -1,6 +1,7 @@ package model import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.links.sureClassNames import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.KotlinModifier.* @@ -10,7 +11,6 @@ import utils.AbstractModelTest import utils.assertNotNull import utils.name import utils.supers -import org.jetbrains.dokka.links.TypeConstructor class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") { diff --git a/plugins/base/src/test/kotlin/model/CommentTest.kt b/plugins/base/src/test/kotlin/model/CommentTest.kt index 7f2151bc..cd149209 100644 --- a/plugins/base/src/test/kotlin/model/CommentTest.kt +++ b/plugins/base/src/test/kotlin/model/CommentTest.kt @@ -3,9 +3,11 @@ package model import org.jetbrains.dokka.model.DClass import org.jetbrains.dokka.model.DProperty import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.doc.Br import org.junit.jupiter.api.Test -import utils.* +import utils.AbstractModelTest +import utils.assertNotNull +import utils.comments +import utils.docs class CommentTest : AbstractModelTest("/src/main/kotlin/comment/Test.kt", "comment") { diff --git a/plugins/base/src/test/kotlin/model/ExtensionsTest.kt b/plugins/base/src/test/kotlin/model/ExtensionsTest.kt index f2657ef8..e28b442f 100644 --- a/plugins/base/src/test/kotlin/model/ExtensionsTest.kt +++ b/plugins/base/src/test/kotlin/model/ExtensionsTest.kt @@ -1,10 +1,13 @@ package model import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions -import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.DClass +import org.jetbrains.dokka.model.DFunction +import org.jetbrains.dokka.model.DInterface +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.properties.WithExtraProperties import org.junit.jupiter.api.Test import utils.AbstractModelTest -import org.jetbrains.dokka.model.properties.WithExtraProperties class ExtensionsTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") { private fun <T : WithExtraProperties<R>, R : Documentable> T.checkExtension(name: String = "extension") = @@ -149,4 +152,4 @@ class ExtensionsTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "cl } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/model/InheritorsTest.kt b/plugins/base/src/test/kotlin/model/InheritorsTest.kt index 265bb7a0..641f6ef5 100644 --- a/plugins/base/src/test/kotlin/model/InheritorsTest.kt +++ b/plugins/base/src/test/kotlin/model/InheritorsTest.kt @@ -1,18 +1,12 @@ package model import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.analysis.DokkaAnalysisConfiguration -import org.jetbrains.dokka.analysis.ProjectKotlinAnalysis -import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo import org.jetbrains.dokka.model.DClass import org.jetbrains.dokka.model.DFunction import org.jetbrains.dokka.model.DInterface import org.jetbrains.dokka.model.doc.P import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.plugability.DokkaPlugin -import org.jetbrains.dokka.plugability.DokkaPluginApiPreview -import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import utils.AbstractModelTest @@ -155,220 +149,221 @@ class InheritorsTest : AbstractModelTest("/src/main/kotlin/inheritors/Test.kt", } } - class IgnoreCommonBuiltInsPlugin : DokkaPlugin() { - private val dokkaBase by lazy { plugin<DokkaBase>() } - @Suppress("unused") - val stdLibKotlinAnalysis by extending { - dokkaBase.kotlinAnalysis providing { ctx -> - ProjectKotlinAnalysis( - sourceSets = ctx.configuration.sourceSets, - logger = ctx.logger, - analysisConfiguration = DokkaAnalysisConfiguration(ignoreCommonBuiltIns = true) - ) - } override dokkaBase.defaultKotlinAnalysis - } - - @OptIn(DokkaPluginApiPreview::class) - override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = - PluginApiPreviewAcknowledgement - } - @Test - fun `should inherit docs for stdLib #2638`() { - val testConfiguration = dokkaConfiguration { - suppressObviousFunctions = false - sourceSets { - sourceSet { - sourceRoots = listOf("src/") - analysisPlatform = "common" - languageVersion = "1.4" - } - } - } - - inlineModelTest( - """ - package kotlin.collections - - import kotlin.internal.PlatformDependent - - /** - * Classes that inherit from this interface can be represented as a sequence of elements that can - * be iterated over. - * @param T the type of element being iterated over. The iterator is covariant in its element type. - */ - public interface Iterable<out T> { - /** - * Returns an iterator over the elements of this object. - */ - public operator fun iterator(): Iterator<T> - } - - /** - * Classes that inherit from this interface can be represented as a sequence of elements that can - * be iterated over and that supports removing elements during iteration. - * @param T the type of element being iterated over. The mutable iterator is invariant in its element type. - */ - public interface MutableIterable<out T> : Iterable<T> { - /** - * Returns an iterator over the elements of this sequence that supports removing elements during iteration. - */ - override fun iterator(): MutableIterator<T> - } - - /** - * A generic collection of elements. Methods in this interface support only read-only access to the collection; - * read/write access is supported through the [MutableCollection] interface. - * @param E the type of elements contained in the collection. The collection is covariant in its element type. - */ - public interface Collection<out E> : Iterable<E> { - // Query Operations - /** - * Returns the size of the collection. - */ - public val size: Int - - /** - * Returns `true` if the collection is empty (contains no elements), `false` otherwise. - */ - public fun isEmpty(): Boolean - - /** - * Checks if the specified element is contained in this collection. - */ - public operator fun contains(element: @UnsafeVariance E): Boolean - - override fun iterator(): Iterator<E> - - // Bulk Operations - /** - * Checks if all elements in the specified collection are contained in this collection. - */ - public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean - } - - /** - * A generic collection of elements that supports adding and removing elements. - * - * @param E the type of elements contained in the collection. The mutable collection is invariant in its element type. - */ - public interface MutableCollection<E> : Collection<E>, MutableIterable<E> { - // Query Operations - override fun iterator(): MutableIterator<E> - - // Modification Operations - /** - * Adds the specified element to the collection. - * - * @return `true` if the element has been added, `false` if the collection does not support duplicates - * and the element is already contained in the collection. - */ - public fun add(element: E): Boolean - - /** - * Removes a single instance of the specified element from this - * collection, if it is present. - * - * @return `true` if the element has been successfully removed; `false` if it was not present in the collection. - */ - public fun remove(element: E): Boolean - - // Bulk Modification Operations - /** - * Adds all of the elements of the specified collection to this collection. - * - * @return `true` if any of the specified elements was added to the collection, `false` if the collection was not modified. - */ - public fun addAll(elements: Collection<E>): Boolean - - /** - * Removes all of this collection's elements that are also contained in the specified collection. - * - * @return `true` if any of the specified elements was removed from the collection, `false` if the collection was not modified. - */ - public fun removeAll(elements: Collection<E>): Boolean - - /** - * Retains only the elements in this collection that are contained in the specified collection. - * - * @return `true` if any element was removed from the collection, `false` if the collection was not modified. - */ - public fun retainAll(elements: Collection<E>): Boolean - - /** - * Removes all elements from this collection. - */ - public fun clear(): Unit - } - - /** - * A generic ordered collection of elements. Methods in this interface support only read-only access to the list; - * read/write access is supported through the [MutableList] interface. - * @param E the type of elements contained in the list. The list is covariant in its element type. - */ - public interface List<out E> : Collection<E> { - // Query Operations - - override val size: Int - override fun isEmpty(): Boolean - override fun contains(element: @UnsafeVariance E): Boolean - override fun iterator(): Iterator<E> - - // Bulk Operations - override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean - - // Positional Access Operations - /** - * Returns the element at the specified index in the list. - */ - public operator fun get(index: Int): E - - // Search Operations - /** - * Returns the index of the first occurrence of the specified element in the list, or -1 if the specified - * element is not contained in the list. - */ - public fun indexOf(element: @UnsafeVariance E): Int - - /** - * Returns the index of the last occurrence of the specified element in the list, or -1 if the specified - * element is not contained in the list. - */ - public fun lastIndexOf(element: @UnsafeVariance E): Int - - // List Iterators - /** - * Returns a list iterator over the elements in this list (in proper sequence). - */ - public fun listIterator(): ListIterator<E> - - /** - * Returns a list iterator over the elements in this list (in proper sequence), starting at the specified [index]. - */ - public fun listIterator(index: Int): ListIterator<E> - - // View - /** - * Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive). - * The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. - * - * Structural changes in the base list make the behavior of the view undefined. - */ - public fun subList(fromIndex: Int, toIndex: Int): List<E> - } - - // etc - """.trimMargin(), - platform = Platform.common.toString(), - configuration = testConfiguration, - prependPackage = false, - pluginsOverrides = listOf(IgnoreCommonBuiltInsPlugin()) - ) { - with((this / "kotlin.collections" / "List" / "contains").cast<DFunction>()) { - documentation.size equals 1 - - } - } - } +// TODO [beresnev] fix, needs access to analysis +// class IgnoreCommonBuiltInsPlugin : DokkaPlugin() { +// private val kotlinAnalysisPlugin by lazy { plugin<DescriptorKotlinAnalysisPlugin>() } +// @Suppress("unused") +// val stdLibKotlinAnalysis by extending { +// kotlinAnalysisPlugin.kotlinAnalysis providing { ctx -> +// ProjectKotlinAnalysis( +// sourceSets = ctx.configuration.sourceSets, +// logger = ctx.logger, +// analysisConfiguration = DokkaAnalysisConfiguration(ignoreCommonBuiltIns = true) +// ) +// } override kotlinAnalysisPlugin.defaultKotlinAnalysis +// } +// +// @OptIn(DokkaPluginApiPreview::class) +// override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = +// PluginApiPreviewAcknowledgement +// } +// @Test +// fun `should inherit docs for stdLib #2638`() { +// val testConfiguration = dokkaConfiguration { +// suppressObviousFunctions = false +// sourceSets { +// sourceSet { +// sourceRoots = listOf("src/") +// analysisPlatform = "common" +// languageVersion = "1.4" +// } +// } +// } +// +// inlineModelTest( +// """ +// package kotlin.collections +// +// import kotlin.internal.PlatformDependent +// +// /** +// * Classes that inherit from this interface can be represented as a sequence of elements that can +// * be iterated over. +// * @param T the type of element being iterated over. The iterator is covariant in its element type. +// */ +// public interface Iterable<out T> { +// /** +// * Returns an iterator over the elements of this object. +// */ +// public operator fun iterator(): Iterator<T> +// } +// +// /** +// * Classes that inherit from this interface can be represented as a sequence of elements that can +// * be iterated over and that supports removing elements during iteration. +// * @param T the type of element being iterated over. The mutable iterator is invariant in its element type. +// */ +// public interface MutableIterable<out T> : Iterable<T> { +// /** +// * Returns an iterator over the elements of this sequence that supports removing elements during iteration. +// */ +// override fun iterator(): MutableIterator<T> +// } +// +// /** +// * A generic collection of elements. Methods in this interface support only read-only access to the collection; +// * read/write access is supported through the [MutableCollection] interface. +// * @param E the type of elements contained in the collection. The collection is covariant in its element type. +// */ +// public interface Collection<out E> : Iterable<E> { +// // Query Operations +// /** +// * Returns the size of the collection. +// */ +// public val size: Int +// +// /** +// * Returns `true` if the collection is empty (contains no elements), `false` otherwise. +// */ +// public fun isEmpty(): Boolean +// +// /** +// * Checks if the specified element is contained in this collection. +// */ +// public operator fun contains(element: @UnsafeVariance E): Boolean +// +// override fun iterator(): Iterator<E> +// +// // Bulk Operations +// /** +// * Checks if all elements in the specified collection are contained in this collection. +// */ +// public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean +// } +// +// /** +// * A generic collection of elements that supports adding and removing elements. +// * +// * @param E the type of elements contained in the collection. The mutable collection is invariant in its element type. +// */ +// public interface MutableCollection<E> : Collection<E>, MutableIterable<E> { +// // Query Operations +// override fun iterator(): MutableIterator<E> +// +// // Modification Operations +// /** +// * Adds the specified element to the collection. +// * +// * @return `true` if the element has been added, `false` if the collection does not support duplicates +// * and the element is already contained in the collection. +// */ +// public fun add(element: E): Boolean +// +// /** +// * Removes a single instance of the specified element from this +// * collection, if it is present. +// * +// * @return `true` if the element has been successfully removed; `false` if it was not present in the collection. +// */ +// public fun remove(element: E): Boolean +// +// // Bulk Modification Operations +// /** +// * Adds all of the elements of the specified collection to this collection. +// * +// * @return `true` if any of the specified elements was added to the collection, `false` if the collection was not modified. +// */ +// public fun addAll(elements: Collection<E>): Boolean +// +// /** +// * Removes all of this collection's elements that are also contained in the specified collection. +// * +// * @return `true` if any of the specified elements was removed from the collection, `false` if the collection was not modified. +// */ +// public fun removeAll(elements: Collection<E>): Boolean +// +// /** +// * Retains only the elements in this collection that are contained in the specified collection. +// * +// * @return `true` if any element was removed from the collection, `false` if the collection was not modified. +// */ +// public fun retainAll(elements: Collection<E>): Boolean +// +// /** +// * Removes all elements from this collection. +// */ +// public fun clear(): Unit +// } +// +// /** +// * A generic ordered collection of elements. Methods in this interface support only read-only access to the list; +// * read/write access is supported through the [MutableList] interface. +// * @param E the type of elements contained in the list. The list is covariant in its element type. +// */ +// public interface List<out E> : Collection<E> { +// // Query Operations +// +// override val size: Int +// override fun isEmpty(): Boolean +// override fun contains(element: @UnsafeVariance E): Boolean +// override fun iterator(): Iterator<E> +// +// // Bulk Operations +// override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean +// +// // Positional Access Operations +// /** +// * Returns the element at the specified index in the list. +// */ +// public operator fun get(index: Int): E +// +// // Search Operations +// /** +// * Returns the index of the first occurrence of the specified element in the list, or -1 if the specified +// * element is not contained in the list. +// */ +// public fun indexOf(element: @UnsafeVariance E): Int +// +// /** +// * Returns the index of the last occurrence of the specified element in the list, or -1 if the specified +// * element is not contained in the list. +// */ +// public fun lastIndexOf(element: @UnsafeVariance E): Int +// +// // List Iterators +// /** +// * Returns a list iterator over the elements in this list (in proper sequence). +// */ +// public fun listIterator(): ListIterator<E> +// +// /** +// * Returns a list iterator over the elements in this list (in proper sequence), starting at the specified [index]. +// */ +// public fun listIterator(index: Int): ListIterator<E> +// +// // View +// /** +// * Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive). +// * The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. +// * +// * Structural changes in the base list make the behavior of the view undefined. +// */ +// public fun subList(fromIndex: Int, toIndex: Int): List<E> +// } +// +// // etc +// """.trimMargin(), +// platform = Platform.common.toString(), +// configuration = testConfiguration, +// prependPackage = false, +// pluginsOverrides = listOf(IgnoreCommonBuiltInsPlugin()) +// ) { +// with((this / "kotlin.collections" / "List" / "contains").cast<DFunction>()) { +// documentation.size equals 1 +// +// } +// } +// } @Test fun `should inherit docs in case of diamond inheritance`() { diff --git a/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt b/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt index a163f7f4..5fe17fc8 100644 --- a/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt +++ b/plugins/base/src/test/kotlin/model/MultiLanguageInheritanceTest.kt @@ -3,13 +3,10 @@ package model import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.PointingToDeclaration -import org.jetbrains.dokka.model.childrenOfType import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.firstMemberOfType import org.jetbrains.dokka.model.withDescendants -import org.jetbrains.dokka.pages.ContentText -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.dokka.utilities.firstIsInstanceOrNull import org.junit.jupiter.api.Test import translators.documentationOf import utils.docs @@ -361,4 +358,4 @@ class MultiLanguageInheritanceTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/model/PropertyTest.kt b/plugins/base/src/test/kotlin/model/PropertyTest.kt index 1047d6cf..d38667bf 100644 --- a/plugins/base/src/test/kotlin/model/PropertyTest.kt +++ b/plugins/base/src/test/kotlin/model/PropertyTest.kt @@ -1,13 +1,10 @@ package model -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.junit.jupiter.api.Test import utils.AbstractModelTest import utils.assertNotNull import utils.name -import kotlin.test.assertEquals class PropertyTest : AbstractModelTest("/src/main/kotlin/property/Test.kt", "property") { diff --git a/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt b/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt index 1a1340dc..1e4869f8 100644 --- a/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt +++ b/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt @@ -1,8 +1,8 @@ package multiplatform +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest class BasicMultiplatformTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt index 6e85fe01..13431bd2 100644 --- a/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt +++ b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt @@ -1,14 +1,13 @@ package pageMerger -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.* +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.RepeatedTest -import java.lang.IllegalArgumentException +import org.junit.jupiter.api.Test import kotlin.test.assertEquals class PageNodeMergerTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt b/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt index cf1332db..f4615216 100644 --- a/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt +++ b/plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt @@ -1,21 +1,18 @@ package parsers -import com.jetbrains.rd.util.first import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.base.translators.psi.parsers.* import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.JavaClassReference import org.jetbrains.dokka.model.DEnum import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.doc.* -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.dokka.utilities.firstIsInstanceOrNull import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import utils.docs import utils.text -import kotlin.random.* -import kotlin.test.* +import kotlin.test.assertNotNull class JavadocParserTest : BaseAbstractTest() { @@ -98,7 +95,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -136,7 +133,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -171,7 +168,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -208,7 +205,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -242,7 +239,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -359,7 +356,7 @@ class JavadocParserTest : BaseAbstractTest() { testInline(source, configuration) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() assertEquals(expected, docs.children.first().root.children) } } @@ -384,7 +381,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -428,7 +425,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -462,7 +459,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root assertEquals( @@ -578,7 +575,7 @@ class JavadocParserTest : BaseAbstractTest() { configuration, ) { documentablesCreationStage = { modules -> - val docs = modules.first().packages.first().classlikes.single().documentation.first().value + val docs = modules.first().packages.first().classlikes.single().documentation.values.first() val root = docs.children.first().root kotlin.test.assertEquals( @@ -591,26 +588,27 @@ class JavadocParserTest : BaseAbstractTest() { } } - @Test - fun `test isolated parsing is case sensitive`() { - // Ensure that it won't accidentally break - val values = JavadocTag.values().map { it.toString().toLowerCase() } - val withRandomizedCapitalization = values.map { - val result = buildString { - for (char in it) { - if (Random.nextBoolean()) { - append(char) - } else { - append(char.toLowerCase()) - } - } - } - if (result == it) result.toUpperCase() else result - } - - for ((index, value) in JavadocTag.values().withIndex()) { - assertEquals(value, JavadocTag.lowercaseValueOfOrNull(values[index])) - assertNull(JavadocTag.lowercaseValueOfOrNull(withRandomizedCapitalization[index])) - } - } + // TODO [beresnev] move to java-analysis +// @Test +// fun `test isolated parsing is case sensitive`() { +// // Ensure that it won't accidentally break +// val values = JavadocTag.values().map { it.toString().toLowerCase() } +// val withRandomizedCapitalization = values.map { +// val result = buildString { +// for (char in it) { +// if (Random.nextBoolean()) { +// append(char) +// } else { +// append(char.toLowerCase()) +// } +// } +// } +// if (result == it) result.toUpperCase() else result +// } +// +// for ((index, value) in JavadocTag.values().withIndex()) { +// assertEquals(value, JavadocTag.lowercaseValueOfOrNull(values[index])) +// assertNull(JavadocTag.lowercaseValueOfOrNull(withRandomizedCapitalization[index])) +// } +// } } diff --git a/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt index 244163ec..f69cf80f 100644 --- a/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt +++ b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt @@ -3,7 +3,7 @@ package renderers.html import org.jetbrains.dokka.base.renderers.html.HtmlRenderer import org.jetbrains.dokka.pages.TextStyle import org.junit.jupiter.api.Test -import renderers.* +import renderers.testPage import utils.Div import utils.P import utils.match diff --git a/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt index 56329940..7afc978c 100644 --- a/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt +++ b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt @@ -10,11 +10,9 @@ import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProviderFactory import org.jetbrains.dokka.testApi.context.MockContext import org.jsoup.Jsoup import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode import renderers.RenderingOnlyTestBase -import utils.TestOutputWriter import testApi.testRunner.defaultSourceSet +import utils.TestOutputWriter import java.io.File abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() { diff --git a/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt b/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt index e83f70d5..5e2560bf 100644 --- a/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt +++ b/plugins/base/src/test/kotlin/renderers/html/NavigationIconTest.kt @@ -3,9 +3,9 @@ package renderers.html import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin -import kotlin.test.assertEquals import utils.navigationHtml import utils.selectNavigationGrid +import kotlin.test.assertEquals class NavigationIconTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt b/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt index 21b4ea3c..75993e49 100644 --- a/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt +++ b/plugins/base/src/test/kotlin/renderers/html/NavigationTest.kt @@ -5,8 +5,8 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jsoup.nodes.Element import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin -import kotlin.test.assertEquals import utils.navigationHtml +import kotlin.test.assertEquals import kotlin.test.assertNull class NavigationTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt index edb5089d..c9787b67 100644 --- a/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/FunctionalTypeConstructorsSignatureTest.kt @@ -1,15 +1,14 @@ package signatures import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.jdk import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.jdk import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import utils.A import utils.Span import utils.TestOutputWriterPlugin import utils.match -import java.lang.IllegalStateException class FunctionalTypeConstructorsSignatureTest : BaseAbstractTest() { private val configuration = dokkaConfiguration { diff --git a/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt b/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt index 5a6d95eb..2837e891 100644 --- a/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt +++ b/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt @@ -2,10 +2,10 @@ package signatures import matchers.content.assertNode import matchers.content.hasExactText +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.firstMemberOfType import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.testApi.logger.TestLogger -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.utilities.DokkaConsoleLogger import org.jetbrains.dokka.utilities.LoggingLevel import org.junit.jupiter.params.ParameterizedTest diff --git a/plugins/base/src/test/kotlin/signatures/VarianceSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/VarianceSignatureTest.kt index b82b673c..06a3daae 100644 --- a/plugins/base/src/test/kotlin/signatures/VarianceSignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/VarianceSignatureTest.kt @@ -3,7 +3,6 @@ package signatures import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.junit.jupiter.api.Test import utils.A -import utils.Span import utils.TestOutputWriterPlugin import utils.match diff --git a/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt b/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt index 5b97f969..30fc127f 100644 --- a/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt +++ b/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt @@ -1,10 +1,10 @@ package transformerBuilders import org.jetbrains.dokka.CoreExtensions -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.plugability.DokkaPluginApiPreview import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin import utils.assertContains import utils.assertNotNull + class PageTransformerBuilderTest : BaseAbstractTest() { class ProxyPlugin(transformer: PageTransformer) : DokkaPlugin() { diff --git a/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt index 07dc0bc7..e1029856 100644 --- a/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt +++ b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt @@ -5,7 +5,6 @@ import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConve import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.utils.addToStdlib.assertedCast import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -335,7 +334,7 @@ class CommentsToContentConverterTest { +"I'm an inline-style link" check { assertEquals( - assertedCast<ContentResolvedLink> { "Link should be resolved" }.address, + (this as? ContentResolvedLink)?.address ?: error("Link should be resolved"), "https://www.google.com" ) } diff --git a/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest1.kt b/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest1.kt index 8ed34b2a..fcdee619 100644 --- a/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest1.kt +++ b/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest1.kt @@ -1,13 +1,15 @@ package transformers import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationReader import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.testApi.logger.TestLogger import org.jetbrains.dokka.utilities.DokkaConsoleLogger import org.jetbrains.dokka.utilities.LoggingLevel +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -81,11 +83,11 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd ) } - private val reader by lazy { ModuleAndPackageDocumentationReader(context) } + private val reader by lazy { context.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } } @Test fun `assert moduleA with sourceSetA`() { - val documentation = reader[dModule(name = "moduleA", sourceSets = setOf(sourceSetA))] + val documentation = reader.read(dModule(name = "moduleA", sourceSets = setOf(sourceSetA))) assertEquals( 1, documentation.keys.size, "Expected moduleA only containing documentation in a single source set" @@ -103,7 +105,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert moduleA with no source sets`() { - val documentation = reader[dModule("moduleA")] + val documentation = reader.read(dModule("moduleA")) assertEquals( emptyMap<DokkaSourceSet, DocumentationNode>(), documentation, "Expected no documentation received for module not declaring a matching sourceSet" @@ -115,15 +117,15 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd assertThrows<IllegalStateException>( "Expected no documentation received for module with unknown sourceSet" ) { - reader[ + reader.read( dModule("moduleA", sourceSets = setOf(configurationBuilder.unattachedSourceSet { name = "unknown" })) - ] + ) } } @Test fun `assert moduleA with all sourceSets`() { - val documentation = reader[dModule("moduleA", sourceSets = setOf(sourceSetA, sourceSetB, sourceSetB2))] + val documentation = reader.read(dModule("moduleA", sourceSets = setOf(sourceSetA, sourceSetB, sourceSetB2))) assertEquals(1, documentation.entries.size, "Expected only one entry from sourceSetA") assertEquals(sourceSetA, documentation.keys.single(), "Expected only one entry from sourceSetA") assertEquals("This is moduleA", documentation.texts.single()) @@ -131,7 +133,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert moduleB with sourceSetB and sourceSetB2`() { - val documentation = reader[dModule("moduleB", sourceSets = setOf(sourceSetB, sourceSetB2))] + val documentation = reader.read(dModule("moduleB", sourceSets = setOf(sourceSetB, sourceSetB2))) assertEquals(1, documentation.keys.size, "Expected only one entry from sourceSetB") assertEquals(sourceSetB, documentation.keys.single(), "Expected only one entry from sourceSetB") assertEquals("This is moduleB", documentation.texts.single()) @@ -139,7 +141,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert sample_A in sourceSetA`() { - val documentation = reader[dPackage(DRI("sample.a"), sourceSets = setOf(sourceSetA))] + val documentation = reader.read(dPackage(DRI("sample.a"), sourceSets = setOf(sourceSetA))) assertEquals(1, documentation.keys.size, "Expected only one entry from sourceSetA") assertEquals(sourceSetA, documentation.keys.single(), "Expected only one entry from sourceSetA") assertEquals("This is package sample.a\\r\\n", documentation.texts.single()) @@ -147,7 +149,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert sample_a_sub in sourceSetA`() { - val documentation = reader[dPackage(DRI("sample.a.sub"), sourceSets = setOf(sourceSetA))] + val documentation = reader.read(dPackage(DRI("sample.a.sub"), sourceSets = setOf(sourceSetA))) assertEquals( emptyMap<DokkaSourceSet, DocumentationNode>(), documentation, "Expected no documentation found for different package" @@ -156,7 +158,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert sample_a in sourceSetB`() { - val documentation = reader[dPackage(DRI("sample.a"), sourceSets = setOf(sourceSetB))] + val documentation = reader.read(dPackage(DRI("sample.a"), sourceSets = setOf(sourceSetB))) assertEquals( emptyMap<DokkaSourceSet, DocumentationNode>(), documentation, "Expected no documentation found for different sourceSet" @@ -165,7 +167,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert sample_b in sourceSetB`() { - val documentation = reader[dPackage(DRI("sample.b"), sourceSets = setOf(sourceSetB))] + val documentation = reader.read(dPackage(DRI("sample.b"), sourceSets = setOf(sourceSetB))) assertEquals(1, documentation.keys.size, "Expected only one entry from sourceSetB") assertEquals(sourceSetB, documentation.keys.single(), "Expected only one entry from sourceSetB") assertEquals("This is package sample.b", documentation.texts.single()) @@ -173,7 +175,7 @@ class ContextModuleAndPackageDocumentationReaderTest1 : AbstractContextModuleAnd @Test fun `assert sample_b in sourceSetB and sourceSetB2`() { - val documentation = reader[dPackage(DRI("sample.b"), sourceSets = setOf(sourceSetB, sourceSetB2))] + val documentation = reader.read(dPackage(DRI("sample.b"), sourceSets = setOf(sourceSetB, sourceSetB2))) assertEquals(1, documentation.keys.size, "Expected only one entry from sourceSetB") assertEquals(sourceSetB, documentation.keys.single(), "Expected only one entry from sourceSetB") assertEquals("This is package sample.b", documentation.texts.single()) diff --git a/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest3.kt b/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest3.kt index 609f808d..4a8a81f2 100644 --- a/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest3.kt +++ b/plugins/base/src/test/kotlin/transformers/ContextModuleAndPackageDocumentationReaderTest3.kt @@ -1,10 +1,12 @@ package transformers -import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationReader import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.DokkaConsoleLogger import org.jetbrains.dokka.utilities.LoggingLevel +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testApi.testRunner.TestDokkaConfigurationBuilder @@ -42,12 +44,12 @@ class ContextModuleAndPackageDocumentationReaderTest3 : AbstractContextModuleAnd ) } - private val reader by lazy { ModuleAndPackageDocumentationReader(context) } + private val reader by lazy { context.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } } @Test fun `root package is matched by empty string and the root keyword`() { - val documentation = reader[dPackage(DRI(""), sourceSets = setOf(sourceSet))] + val documentation = reader.read(dPackage(DRI(""), sourceSets = setOf(sourceSet))) assertEquals( listOf("This is the root package", "This is also the root package"), documentation.texts ) diff --git a/plugins/base/src/test/kotlin/transformers/DivisionSwitchTest.kt b/plugins/base/src/test/kotlin/transformers/DivisionSwitchTest.kt index ef36d811..16798508 100644 --- a/plugins/base/src/test/kotlin/transformers/DivisionSwitchTest.kt +++ b/plugins/base/src/test/kotlin/transformers/DivisionSwitchTest.kt @@ -3,13 +3,12 @@ package transformers import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.PluginConfigurationImpl import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.pages.ClasslikePageNode import org.jetbrains.dokka.pages.ContentHeader import org.jetbrains.dokka.pages.ContentNode import org.jetbrains.dokka.pages.ContentText -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.kotlin.utils.addIfNotNull import org.junit.jupiter.api.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -47,7 +46,7 @@ class DivisionSwitchTest : BaseAbstractTest() { } } suppressObviousFunctions = false - pluginsConfigurations.addIfNotNull( + pluginsConfigurations.add( PluginConfigurationImpl( DokkaBase::class.qualifiedName!!, DokkaConfiguration.SerializationFormat.JSON, @@ -120,4 +119,4 @@ class DivisionSwitchTest : BaseAbstractTest() { assertEquals(1, inheritedProperties.children.size, "Incorrect number of inherited properties found") } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/transformers/InvalidContentModuleAndPackageDocumentationReaderTest.kt b/plugins/base/src/test/kotlin/transformers/InvalidContentModuleAndPackageDocumentationReaderTest.kt index 59c83b7d..59c29435 100644 --- a/plugins/base/src/test/kotlin/transformers/InvalidContentModuleAndPackageDocumentationReaderTest.kt +++ b/plugins/base/src/test/kotlin/transformers/InvalidContentModuleAndPackageDocumentationReaderTest.kt @@ -1,9 +1,11 @@ package transformers -import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationReader import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.DokkaConsoleLogger import org.jetbrains.dokka.utilities.LoggingLevel +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testApi.testRunner.TestDokkaConfigurationBuilder @@ -65,14 +67,14 @@ class InvalidContentModuleAndPackageDocumentationReaderTest : AbstractContextMod ) } - private val readerA by lazy { ModuleAndPackageDocumentationReader(contextA) } - private val readerB by lazy { ModuleAndPackageDocumentationReader(contextB) } + private val readerA by lazy { contextA.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } } + private val readerB by lazy { contextB.plugin<InternalKotlinAnalysisPlugin>().querySingle { moduleAndPackageDocumentationReader } } @Test fun `parsing should fail with a message when documentation is in not proper format`() { val exception = - runCatching { readerA[dModule(name = "moduleA", sourceSets = setOf(sourceSetA))] }.exceptionOrNull() + runCatching { readerA.read(dModule(name = "moduleA", sourceSets = setOf(sourceSetA))) }.exceptionOrNull() assertEquals( "Unexpected classifier: \"Invalid\", expected either \"Module\" or \"Package\". \n" + "For more information consult the specification: https://kotlinlang.org/docs/dokka-module-and-package-docs.html", @@ -83,7 +85,7 @@ class InvalidContentModuleAndPackageDocumentationReaderTest : AbstractContextMod @Test fun `parsing should fail with a message where it encountered error and why`() { val exception = - runCatching { readerB[dModule(name = "moduleB", sourceSets = setOf(sourceSetB))] }.exceptionOrNull()?.message!! + runCatching { readerB.read(dModule(name = "moduleB", sourceSets = setOf(sourceSetB))) }.exceptionOrNull()?.message!! //I don't want to assert whole message since it contains a path to a temporary folder assertTrue(exception.contains("Wrong AST Tree. Header does not contain expected content in ")) diff --git a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt index 241fb481..39d725bb 100644 --- a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt +++ b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt @@ -8,7 +8,6 @@ import org.jetbrains.dokka.model.childrenOfType import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.model.firstChildOfType import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.utils.addIfNotNull import org.junit.jupiter.api.Test import utils.assertNotNull import kotlin.test.assertEquals @@ -39,7 +38,7 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt") } } - pluginsConfigurations.addIfNotNull( + pluginsConfigurations.add( PluginConfigurationImpl( DokkaBase::class.qualifiedName!!, DokkaConfiguration.SerializationFormat.JSON, @@ -387,4 +386,4 @@ class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerUnitTest.kt b/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerUnitTest.kt index 9f934f3f..e773a4da 100644 --- a/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerUnitTest.kt +++ b/plugins/base/src/test/kotlin/transformers/ModuleAndPackageDocumentationTransformerUnitTest.kt @@ -1,16 +1,21 @@ package transformers -import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationReader + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationTransformer import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.DPackage import org.jetbrains.dokka.model.SourceSetDependent +import org.jetbrains.dokka.model.doc.CustomDocTag +import org.jetbrains.dokka.model.doc.Description import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.kotlin.analysis.kotlin.internal.ModuleAndPackageDocumentationReader import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import testApi.testRunner.dPackage -import testApi.testRunner.documentationNode import testApi.testRunner.sourceSet @@ -20,8 +25,9 @@ class ModuleAndPackageDocumentationTransformerUnitTest { fun `empty list of modules`() { val transformer = ModuleAndPackageDocumentationTransformer( object : ModuleAndPackageDocumentationReader { - override fun get(module: DModule): SourceSetDependent<DocumentationNode> = throw NotImplementedError() - override fun get(pkg: DPackage): SourceSetDependent<DocumentationNode> = throw NotImplementedError() + override fun read(module: DModule): SourceSetDependent<DocumentationNode> = throw NotImplementedError() + override fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> = throw NotImplementedError() + override fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode = throw NotImplementedError() } ) @@ -34,13 +40,13 @@ class ModuleAndPackageDocumentationTransformerUnitTest { fun `single module documentation`() { val transformer = ModuleAndPackageDocumentationTransformer( object : ModuleAndPackageDocumentationReader { - override fun get(pkg: DPackage): SourceSetDependent<DocumentationNode> = throw NotImplementedError() - override fun get(module: DModule): SourceSetDependent<DocumentationNode> { + override fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> = throw NotImplementedError() + override fun read(module: DModule): SourceSetDependent<DocumentationNode> { return module.sourceSets.associateWith { sourceSet -> documentationNode("doc" + sourceSet.displayName) } } - + override fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode = throw NotImplementedError() } ) @@ -77,13 +83,14 @@ class ModuleAndPackageDocumentationTransformerUnitTest { fun `merges with already existing module documentation`() { val transformer = ModuleAndPackageDocumentationTransformer( object : ModuleAndPackageDocumentationReader { - override fun get(pkg: DPackage): SourceSetDependent<DocumentationNode> = throw NotImplementedError() - override fun get(module: DModule): SourceSetDependent<DocumentationNode> { + override fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> = throw NotImplementedError() + override fun read(module: DModule): SourceSetDependent<DocumentationNode> { /* Only add documentation for first source set */ return module.sourceSets.take(1).associateWith { sourceSet -> documentationNode("doc" + sourceSet.displayName) } } + override fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode = throw NotImplementedError() } ) @@ -121,8 +128,8 @@ class ModuleAndPackageDocumentationTransformerUnitTest { fun `package documentation`() { val transformer = ModuleAndPackageDocumentationTransformer( object : ModuleAndPackageDocumentationReader { - override fun get(module: DModule): SourceSetDependent<DocumentationNode> = emptyMap() - override fun get(pkg: DPackage): SourceSetDependent<DocumentationNode> { + override fun read(module: DModule): SourceSetDependent<DocumentationNode> = emptyMap() + override fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> { /* Only attach documentation to packages with 'attach' */ if ("attach" !in pkg.dri.packageName.orEmpty()) return emptyMap() /* Only attach documentation to two source sets */ @@ -130,6 +137,7 @@ class ModuleAndPackageDocumentationTransformerUnitTest { documentationNode("doc:${sourceSet.displayName}:${pkg.dri.packageName}") } } + override fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode = throw NotImplementedError() } ) @@ -239,4 +247,10 @@ class ModuleAndPackageDocumentationTransformerUnitTest { ) } + + private fun documentationNode(vararg texts: String): DocumentationNode { + return DocumentationNode( + texts.toList() + .map { Description(CustomDocTag(listOf(Text(it)), name = MARKDOWN_ELEMENT_FILE_NAME)) }) + } } diff --git a/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt index 13e90f42..431abef5 100644 --- a/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt +++ b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt @@ -5,7 +5,7 @@ import org.jetbrains.dokka.DokkaDefaults import org.jetbrains.dokka.PackageOptionsImpl import org.jetbrains.dokka.Platform import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test diff --git a/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt index 11996186..469c1a1e 100644 --- a/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt +++ b/plugins/base/src/test/kotlin/transformers/SourceLinkTransformerTest.kt @@ -3,12 +3,12 @@ package transformers import org.jetbrains.dokka.DokkaSourceSetID import org.jetbrains.dokka.SourceLinkDefinitionImpl import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jsoup.nodes.Element import org.junit.jupiter.api.Test import signatures.renderedContent import utils.TestOutputWriterPlugin import java.net.URL import kotlin.test.assertEquals -import org.jsoup.nodes.Element class SourceLinkTransformerTest : BaseAbstractTest() { @@ -124,4 +124,4 @@ class SourceLinkTransformerTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt index 73f043bc..a6060970 100644 --- a/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt +++ b/plugins/base/src/test/kotlin/transformers/SuppressedByConfigurationDocumentableFilterTransformerTest.kt @@ -2,8 +2,8 @@ package transformers import org.jetbrains.dokka.DokkaDefaults import org.jetbrains.dokka.PackageOptionsImpl -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.DRI import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertIterableEquals import org.junit.jupiter.api.Test diff --git a/plugins/base/src/test/kotlin/translators/AccessorMethodNamingTest.kt b/plugins/base/src/test/kotlin/translators/AccessorMethodNamingTest.kt index aeea194f..dee4bf7a 100644 --- a/plugins/base/src/test/kotlin/translators/AccessorMethodNamingTest.kt +++ b/plugins/base/src/test/kotlin/translators/AccessorMethodNamingTest.kt @@ -1,8 +1,9 @@ package translators -import org.junit.jupiter.api.Assertions.* import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.DProperty +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test /** @@ -115,4 +116,4 @@ class AccessorMethodNamingTest : BaseAbstractTest() { assertEquals(testsDone, properties.size) } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/translators/Bug1341.kt b/plugins/base/src/test/kotlin/translators/Bug1341.kt index a4b93a7e..a8c9e342 100644 --- a/plugins/base/src/test/kotlin/translators/Bug1341.kt +++ b/plugins/base/src/test/kotlin/translators/Bug1341.kt @@ -1,7 +1,7 @@ package translators -import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.links.DRI import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt index a940264a..a763cbd2 100644 --- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt +++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt @@ -1,7 +1,6 @@ package translators import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.PointingToDeclaration @@ -13,7 +12,6 @@ import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement import org.jetbrains.dokka.DokkaConfiguration.Visibility import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test -import utils.assertNotNull class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { val configuration = dokkaConfiguration { @@ -320,70 +318,71 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { } } - class OnlyPsiPlugin : DokkaPlugin() { - private val dokkaBase by lazy { plugin<DokkaBase>() } - - @Suppress("unused") - val psiOverrideDescriptorTranslator by extending { - (dokkaBase.psiToDocumentableTranslator - override dokkaBase.descriptorToDocumentableTranslator) - } - - @OptIn(DokkaPluginApiPreview::class) - override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = - PluginApiPreviewAcknowledgement - } - - // for Kotlin classes from DefaultPsiToDocumentableTranslator - @Test - fun `should resolve ultralight class`() { - val configurationWithNoJVM = dokkaConfiguration { - sourceSets { - sourceSet { - sourceRoots = listOf("src/main/java") - } - } - } - - testInline( - """ - |/src/main/java/example/Test.kt - |package example - | - |open class KotlinSubClass { - | fun kotlinSubclassFunction(bar: String): String { - | return "KotlinSubClass" - | } - |} - | - |/src/main/java/example/JavaLeafClass.java - |package example; - | - |public class JavaLeafClass extends KotlinSubClass { - | public String javaLeafClassFunction(String baz) { - | return "JavaLeafClass"; - | } - |} - """.trimMargin(), - configurationWithNoJVM, - pluginOverrides = listOf(OnlyPsiPlugin()) // suppress a descriptor translator because of psi and descriptor translators work in parallel - ) { - documentablesMergingStage = { module -> - val kotlinSubclassFunction = - module.packages.single().classlikes.find { it.name == "JavaLeafClass" }?.functions?.find { it.name == "kotlinSubclassFunction" } - .assertNotNull("kotlinSubclassFunction ") - - assertEquals( - "String", - (kotlinSubclassFunction.type as? TypeConstructor)?.dri?.classNames - ) - assertEquals( - "String", - (kotlinSubclassFunction.parameters.firstOrNull()?.type as? TypeConstructor)?.dri?.classNames - ) - } - } - } + // TODO [beresnev] fix +// class OnlyPsiPlugin : DokkaPlugin() { +// private val kotlinAnalysisPlugin by lazy { plugin<Kotlin>() } +// +// @Suppress("unused") +// val psiOverrideDescriptorTranslator by extending { +// (plugin<JavaAnalysisPlugin>().psiToDocumentableTranslator +// override kotlinAnalysisPlugin.descriptorToDocumentableTranslator) +// } +// +// @OptIn(DokkaPluginApiPreview::class) +// override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = +// PluginApiPreviewAcknowledgement +// } +// +// // for Kotlin classes from DefaultPsiToDocumentableTranslator +// @Test +// fun `should resolve ultralight class`() { +// val configurationWithNoJVM = dokkaConfiguration { +// sourceSets { +// sourceSet { +// sourceRoots = listOf("src/main/java") +// } +// } +// } +// +// testInline( +// """ +// |/src/main/java/example/Test.kt +// |package example +// | +// |open class KotlinSubClass { +// | fun kotlinSubclassFunction(bar: String): String { +// | return "KotlinSubClass" +// | } +// |} +// | +// |/src/main/java/example/JavaLeafClass.java +// |package example; +// | +// |public class JavaLeafClass extends KotlinSubClass { +// | public String javaLeafClassFunction(String baz) { +// | return "JavaLeafClass"; +// | } +// |} +// """.trimMargin(), +// configurationWithNoJVM, +// pluginOverrides = listOf(OnlyPsiPlugin()) // suppress a descriptor translator because of psi and descriptor translators work in parallel +// ) { +// documentablesMergingStage = { module -> +// val kotlinSubclassFunction = +// module.packages.single().classlikes.find { it.name == "JavaLeafClass" }?.functions?.find { it.name == "kotlinSubclassFunction" } +// .assertNotNull("kotlinSubclassFunction ") +// +// assertEquals( +// "String", +// (kotlinSubclassFunction.type as? TypeConstructor)?.dri?.classNames +// ) +// assertEquals( +// "String", +// (kotlinSubclassFunction.parameters.firstOrNull()?.type as? TypeConstructor)?.dri?.classNames +// ) +// } +// } +// } @Test fun `should preserve regular functions that are named like getters, but are not getters`() { @@ -822,12 +821,12 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { configuration ) { documentablesMergingStage = { module -> - val kotlinEnum = module.packages.find { it.name == "test" } + val javaEnum = module.packages.find { it.name == "test" } ?.classlikes ?.single { it.name == "JavaEnum" } - checkNotNull(kotlinEnum) + checkNotNull(javaEnum) - val valueOfFunction = kotlinEnum.functions.single { it.name == "valueOf" } + val valueOfFunction = javaEnum.functions.single { it.name == "valueOf" } val expectedDocumentation = DocumentationNode(listOf( Description( diff --git a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt index 011ae729..bc5c909f 100644 --- a/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt +++ b/plugins/base/src/test/kotlin/translators/ExternalDocumentablesTest.kt @@ -1,15 +1,13 @@ package translators -import com.intellij.openapi.application.PathManager -import kotlinx.coroutines.Job -import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider import org.jetbrains.dokka.model.DClass import org.jetbrains.dokka.model.DInterface import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.cast +import org.jetbrains.kotlin.analysis.kotlin.internal.ExternalDocumentablesProvider +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -36,7 +34,7 @@ class ExternalDocumentablesTest : BaseAbstractTest() { ) { lateinit var provider: ExternalDocumentablesProvider pluginsSetupStage = { - provider = it.plugin<DokkaBase>().querySingle { externalDocumentablesProvider } + provider = it.plugin<InternalKotlinAnalysisPlugin>().querySingle { externalDocumentablesProvider } } documentablesTransformationStage = { mod -> val entry = mod.packages.single().classlikes.single().cast<DClass>().supertypes.entries.single() @@ -59,7 +57,10 @@ class ExternalDocumentablesTest : BaseAbstractTest() { @Test fun `external documentable from dependency`() { val coroutinesPath = - PathManager.getResourceRoot(Job::class.java, "/kotlinx/coroutines/Job.class") + ClassLoader.getSystemResource("kotlinx/coroutines/Job.class") + ?.file + ?.replace("file:", "") + ?.replaceAfter(".jar", "") val configuration = dokkaConfiguration { sourceSets { @@ -82,7 +83,7 @@ class ExternalDocumentablesTest : BaseAbstractTest() { ) { lateinit var provider: ExternalDocumentablesProvider pluginsSetupStage = { - provider = it.plugin<DokkaBase>().querySingle { externalDocumentablesProvider } + provider = it.plugin<InternalKotlinAnalysisPlugin>().querySingle { externalDocumentablesProvider } } documentablesTransformationStage = { mod -> val entry = mod.packages.single().classlikes.single().cast<DClass>().supertypes.entries.single() @@ -124,7 +125,7 @@ class ExternalDocumentablesTest : BaseAbstractTest() { ) { lateinit var provider: ExternalDocumentablesProvider pluginsSetupStage = { - provider = it.plugin<DokkaBase>().querySingle { externalDocumentablesProvider } + provider = it.plugin<InternalKotlinAnalysisPlugin>().querySingle { externalDocumentablesProvider } } documentablesTransformationStage = { mod -> val entry = mod.packages.single().classlikes.single().cast<DClass>().supertypes.entries.single() @@ -136,4 +137,4 @@ class ExternalDocumentablesTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt b/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt index d5d25dc7..7fc6b7fa 100644 --- a/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt +++ b/plugins/base/src/test/kotlin/translators/JavadocInheritDocsTest.kt @@ -1,11 +1,10 @@ package translators +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.doc.CustomDocTag import org.jetbrains.dokka.model.doc.Description import org.jetbrains.dokka.model.doc.P import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.junit.Ignore import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -306,4 +305,4 @@ class JavadocInheritDocsTest : BaseAbstractTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt b/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt index 7510e541..ba0d95d5 100644 --- a/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt +++ b/plugins/base/src/test/kotlin/translators/JavadocInheritedDocTagsTest.kt @@ -1,11 +1,10 @@ package translators +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.PointingToDeclaration import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.jetbrains.dokka.model.doc.Deprecated as DokkaDeprecatedTag @@ -89,7 +88,7 @@ class JavadocInheritedDocTagsTest : BaseAbstractTest() { fun `work with return`() { performTagsTest { module -> val function = module.findFunction("sample", "Subclass", "test") - val renderedTag = function.documentation.values.first().children.firstIsInstance<Return>() + val renderedTag = function.documentation.values.first().children.filterIsInstance<Return>().first() val expectedTag = Return( CustomDocTag( children = listOf( @@ -155,7 +154,7 @@ class JavadocInheritedDocTagsTest : BaseAbstractTest() { fun `work with deprecated`() { performTagsTest { module -> val function = module.findFunction("sample", "Subclass", "test") - val renderedTag = function.documentation.values.first().children.firstIsInstance<DokkaDeprecatedTag>() + val renderedTag = function.documentation.values.first().children.filterIsInstance<DokkaDeprecatedTag>().first() val expectedTag = DokkaDeprecatedTag( CustomDocTag( children = listOf( @@ -175,7 +174,7 @@ class JavadocInheritedDocTagsTest : BaseAbstractTest() { fun `work with see also`() { performTagsTest { module -> val function = module.findFunction("sample", "Subclass", "test") - val renderedTag = function.documentation.values.first().children.firstIsInstance<See>() + val renderedTag = function.documentation.values.first().children.filterIsInstance<See>().first() val expectedTag = See( CustomDocTag( children = listOf( @@ -197,7 +196,7 @@ class JavadocInheritedDocTagsTest : BaseAbstractTest() { fun `work with author`() { performTagsTest { module -> val classlike = module.findClasslike("sample", "Subclass") - val renderedTag = classlike.documentation.values.first().children.firstIsInstance<Author>() + val renderedTag = classlike.documentation.values.first().children.filterIsInstance<Author>().first() val expectedTag = Author( CustomDocTag( children = listOf( @@ -246,4 +245,4 @@ class JavadocInheritedDocTagsTest : BaseAbstractTest() { assertEquals(expectedXdTag, xdTag) } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt b/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt index 52902205..2c1173c0 100644 --- a/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt +++ b/plugins/base/src/test/kotlin/translators/JavadocParserTest.kt @@ -1,14 +1,14 @@ package translators +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.childrenOfType import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.firstChildOfType import org.jetbrains.dokka.model.firstMemberOfType -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import org.junit.jupiter.api.Assertions.* import utils.text class JavadocParserTest : BaseAbstractTest() { diff --git a/plugins/base/src/test/kotlin/translators/utils.kt b/plugins/base/src/test/kotlin/translators/utils.kt index f0522ade..f9049ee5 100644 --- a/plugins/base/src/test/kotlin/translators/utils.kt +++ b/plugins/base/src/test/kotlin/translators/utils.kt @@ -3,7 +3,6 @@ package translators import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.Description import org.jetbrains.dokka.model.doc.Text -import java.util.NoSuchElementException fun DModule.documentationOf(className: String, functionName: String? = null): String = descriptionOf(className, functionName) diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt index 4fce1155..d741c250 100644 --- a/plugins/base/src/test/kotlin/utils/contentUtils.kt +++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt @@ -1,7 +1,10 @@ package utils import matchers.content.* -import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.pages.BasicTabbedContentType +import org.jetbrains.dokka.pages.ContentGroup +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.RootPageNode //TODO: Try to unify those functions after update to 1.4 fun ContentMatcherBuilder<*>.functionSignature( @@ -328,4 +331,4 @@ data class ParamAttributes( ) fun RootPageNode.findTestType(packageName: String, name: String) = - children.single { it.name == packageName }.children.single { it.name == name } as ContentPage
\ No newline at end of file + children.single { it.name == packageName }.children.single { it.name == name } as ContentPage diff --git a/plugins/gfm/build.gradle.kts b/plugins/gfm/build.gradle.kts index da64c0e9..b7e6fc18 100644 --- a/plugins/gfm/build.gradle.kts +++ b/plugins/gfm/build.gradle.kts @@ -7,20 +7,22 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) + implementation(projects.plugins.base) - testImplementation(projects.plugins.base) - testImplementation(projects.plugins.base.baseTestUtils) - implementation(libs.jackson.kotlin) - testImplementation(projects.core.testApi) - testImplementation(platform(libs.junit.bom)) - testImplementation(libs.junit.jupiter) + implementation(kotlin("reflect")) + implementation(libs.jackson.kotlin) constraints { implementation(libs.jackson.databind) { because("CVE-2022-42003") } } + + testImplementation(projects.plugins.base) + testImplementation(projects.plugins.base.baseTestUtils) + testImplementation(projects.core.testApi) + testImplementation(platform(libs.junit.bom)) + testImplementation(libs.junit.jupiter) } registerDokkaArtifactPublication("gfmPlugin") { diff --git a/plugins/gfm/gfm-template-processing/build.gradle.kts b/plugins/gfm/gfm-template-processing/build.gradle.kts index 021adae2..9611a8aa 100644 --- a/plugins/gfm/gfm-template-processing/build.gradle.kts +++ b/plugins/gfm/gfm-template-processing/build.gradle.kts @@ -8,12 +8,12 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) implementation(projects.plugins.base) implementation(projects.plugins.gfm) implementation(projects.plugins.allModulesPage) implementation(projects.plugins.templating) + implementation(kotlin("reflect")) implementation(libs.kotlinx.coroutines.core) testImplementation(projects.core.testApi) diff --git a/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt b/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt index e66f8a12..e286d9e5 100644 --- a/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt +++ b/plugins/gfm/src/main/kotlin/org/jetbrains/dokka/gfm/gfmTemplating.kt @@ -1,9 +1,9 @@ package org.jetbrains.dokka.gfm -import org.jetbrains.dokka.base.templating.toJsonString -import org.jetbrains.dokka.links.DRI import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS +import org.jetbrains.dokka.base.templating.toJsonString +import org.jetbrains.dokka.links.DRI @JsonTypeInfo(use = CLASS) sealed class GfmCommand { diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/CodeWrappingTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/CodeWrappingTest.kt index a8b263eb..ae40694c 100644 --- a/plugins/gfm/src/test/kotlin/renderers/gfm/CodeWrappingTest.kt +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/CodeWrappingTest.kt @@ -1,10 +1,9 @@ package renderers.gfm import org.jetbrains.dokka.gfm.renderer.CommonmarkRenderer -import org.jetbrains.dokka.pages.TextStyle import org.junit.jupiter.api.Test +import renderers.testPage import kotlin.test.assertEquals -import renderers.* class CodeWrappingTest : GfmRenderingOnlyTestBase() { @Test diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt index eafe3e13..1d6a79ca 100644 --- a/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt @@ -3,7 +3,7 @@ package renderers.gfm import org.jetbrains.dokka.gfm.renderer.CommonmarkRenderer import org.jetbrains.dokka.pages.TextStyle import org.junit.jupiter.api.Test -import renderers.* +import renderers.testPage class GroupWrappingTest : GfmRenderingOnlyTestBase() { diff --git a/plugins/javadoc/api/javadoc.api b/plugins/javadoc/api/javadoc.api index 4d077882..66475972 100644 --- a/plugins/javadoc/api/javadoc.api +++ b/plugins/javadoc/api/javadoc.api @@ -583,13 +583,13 @@ public final class org/jetbrains/dokka/javadoc/pages/TitleNode : org/jetbrains/d } public final class org/jetbrains/dokka/javadoc/pages/TreeViewInstaller : org/jetbrains/dokka/transformers/pages/PageTransformer { - public static final field INSTANCE Lorg/jetbrains/dokka/javadoc/pages/TreeViewInstaller; + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V public fun invoke (Lorg/jetbrains/dokka/pages/RootPageNode;)Lorg/jetbrains/dokka/pages/RootPageNode; } public final class org/jetbrains/dokka/javadoc/pages/TreeViewPage : org/jetbrains/dokka/javadoc/pages/JavadocPageNode { - public fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Lorg/jetbrains/dokka/pages/PageNode;)V - public synthetic fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Lorg/jetbrains/dokka/pages/PageNode;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Lorg/jetbrains/dokka/pages/PageNode;Lorg/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder;)V + public synthetic fun <init> (Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/util/Set;Ljava/util/List;Lorg/jetbrains/dokka/pages/PageNode;Lorg/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun getChildren ()Ljava/util/List; public final fun getClasses ()Ljava/util/List; public fun getContent ()Lorg/jetbrains/dokka/pages/ContentNode; @@ -597,6 +597,7 @@ public final class org/jetbrains/dokka/javadoc/pages/TreeViewPage : org/jetbrain public fun getDocumentables ()Ljava/util/List; public fun getDri ()Ljava/util/Set; public fun getEmbeddedResources ()Ljava/util/List; + public final fun getInheritanceBuilder ()Lorg/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder; public final fun getKind ()Ljava/lang/String; public fun getName ()Ljava/lang/String; public final fun getPackages ()Ljava/util/List; @@ -606,24 +607,6 @@ public final class org/jetbrains/dokka/javadoc/pages/TreeViewPage : org/jetbrain public fun modified (Ljava/lang/String;Lorg/jetbrains/dokka/pages/ContentNode;Ljava/util/Set;Ljava/util/List;Ljava/util/List;)Lorg/jetbrains/dokka/pages/ContentPage; } -public final class org/jetbrains/dokka/javadoc/pages/TreeViewPage$InheritanceNode { - public fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;Z)V - public synthetic fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lorg/jetbrains/dokka/links/DRI; - public final fun component2 ()Ljava/util/List; - public final fun component3 ()Ljava/util/List; - public final fun component4 ()Z - public final fun copy (Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;Z)Lorg/jetbrains/dokka/javadoc/pages/TreeViewPage$InheritanceNode; - public static synthetic fun copy$default (Lorg/jetbrains/dokka/javadoc/pages/TreeViewPage$InheritanceNode;Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;ZILjava/lang/Object;)Lorg/jetbrains/dokka/javadoc/pages/TreeViewPage$InheritanceNode; - public fun equals (Ljava/lang/Object;)Z - public final fun getChildren ()Ljava/util/List; - public final fun getDri ()Lorg/jetbrains/dokka/links/DRI; - public final fun getInterfaces ()Ljava/util/List; - public fun hashCode ()I - public final fun isInterface ()Z - public fun toString ()Ljava/lang/String; -} - public abstract interface class org/jetbrains/dokka/javadoc/pages/WithBrief { public abstract fun getBrief ()Ljava/util/List; } diff --git a/plugins/javadoc/build.gradle.kts b/plugins/javadoc/build.gradle.kts index 462e966f..5c6d2eb6 100644 --- a/plugins/javadoc/build.gradle.kts +++ b/plugins/javadoc/build.gradle.kts @@ -7,21 +7,19 @@ plugins { dependencies { compileOnly(projects.core) - compileOnly(projects.kotlinAnalysis) + compileOnly(projects.subprojects.analysisKotlinApi) - implementation(kotlin("reflect")) - implementation(libs.soywiz.korte) implementation(projects.plugins.base) implementation(projects.plugins.kotlinAsJava) + implementation(kotlin("reflect")) + implementation(libs.soywiz.korte) implementation(libs.kotlinx.html) implementation(libs.kotlinx.coroutines.core) testImplementation(projects.plugins.base.baseTestUtils) testImplementation(projects.core.testApi) - testImplementation(libs.jsoup) - testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) } diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt index 50355c7e..70362ad4 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/JavadocPlugin.kt @@ -1,15 +1,15 @@ package org.jetbrains.dokka.javadoc -import org.jetbrains.dokka.javadoc.location.JavadocLocationProviderFactory -import org.jetbrains.dokka.javadoc.renderer.KorteJavadocRenderer -import org.jetbrains.dokka.javadoc.signatures.JavadocSignatureProvider import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.renderers.PackageListCreator import org.jetbrains.dokka.base.renderers.RootCreator import org.jetbrains.dokka.base.resolvers.shared.PackageList.Companion.PACKAGE_LIST_NAME import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat +import org.jetbrains.dokka.javadoc.location.JavadocLocationProviderFactory import org.jetbrains.dokka.javadoc.pages.* +import org.jetbrains.dokka.javadoc.renderer.KorteJavadocRenderer +import org.jetbrains.dokka.javadoc.signatures.JavadocSignatureProvider import org.jetbrains.dokka.javadoc.transformers.documentables.JavadocDocumentableJVMSourceSetFilter import org.jetbrains.dokka.javadoc.validity.MultiplatformConfiguredChecker import org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin @@ -70,7 +70,7 @@ class JavadocPlugin : DokkaPlugin() { } val treeViewInstaller by extending { - javadocPreprocessors with TreeViewInstaller order { after(rootCreator) } + javadocPreprocessors providing ::TreeViewInstaller order { after(rootCreator) } } val allClassessPageInstaller by extending { diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt index 8210b30a..083b5c9a 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/JavadocPageNodes.kt @@ -1,19 +1,14 @@ package org.jetbrains.dokka.javadoc.pages -import com.intellij.psi.PsiClass import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource -import org.jetbrains.dokka.analysis.PsiDocumentableSource -import org.jetbrains.dokka.analysis.from import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType +import org.jetbrains.kotlin.analysis.kotlin.internal.InheritanceBuilder +import org.jetbrains.kotlin.analysis.kotlin.internal.InheritanceNode interface JavadocPageNode : ContentPage, WithDocumentables @@ -386,7 +381,8 @@ class TreeViewPage( val classes: List<JavadocClasslikePageNode>?, override val dri: Set<DRI>, override val documentables: List<Documentable> = emptyList(), - val root: PageNode + val root: PageNode, + val inheritanceBuilder: InheritanceBuilder ) : JavadocPageNode { init { assert(packages == null || classes == null) @@ -397,7 +393,6 @@ class TreeViewPage( getDocumentableEntries(node) }.groupBy({ it.first }) { it.second }.map { (l, r) -> l to r.first() }.toMap() - private val descriptorMap = getDescriptorMap() private val inheritanceTuple = generateInheritanceTree() internal val classGraph = inheritanceTuple.first internal val interfaceGraph = inheritanceTuple.second @@ -427,7 +422,8 @@ class TreeViewPage( classes = children.filterIsInstance<JavadocClasslikePageNode>().takeIf { it.isNotEmpty() }, dri = dri, documentables, - root = root + root = root, + inheritanceBuilder ) override fun modified(name: String, children: List<PageNode>): PageNode = @@ -437,7 +433,8 @@ class TreeViewPage( classes = children.filterIsInstance<JavadocClasslikePageNode>().takeIf { it.isNotEmpty() }, dri = dri, documentables, - root = root + root = root, + inheritanceBuilder ) override val embeddedResources: List<String> = emptyList() @@ -484,51 +481,8 @@ class TreeViewPage( fun interfaceTree(node: InheritanceNode) = interfaceTreeRec(node).firstOrNull() // TODO.single() - fun gatherPsiClasses(psi: PsiClass): List<Pair<PsiClass, List<PsiClass>>> = psi.supers.toList().let { l -> - listOf(psi to l) + l.flatMap { gatherPsiClasses(it) } - } - - val psiInheritanceTree = - childrenDocumentables.flatMap { (_, v) -> (v as? WithSources)?.sources?.values.orEmpty() } - .filterIsInstance<PsiDocumentableSource>().mapNotNull { it.psi as? PsiClass } - .flatMap(::gatherPsiClasses) - .flatMap { entry -> entry.second.map { it to entry.first } } - .let { - it + it.map { it.second to null } - } - .groupBy({ it.first }) { it.second } - .map { it.key to it.value.filterNotNull().distinct() } - .map { (k, v) -> - InheritanceNode( - DRI.from(k), - v.map { InheritanceNode(DRI.from(it)) }, - k.supers.filter { it.isInterface }.map { DRI.from(it) }, - k.isInterface - ) - - } - - val descriptorInheritanceTree = descriptorMap.flatMap { (_, v) -> - v.typeConstructor.supertypes - .map { getClassDescriptorForType(it) to v } - } - .let { - it + it.map { it.second to null } - } - .groupBy({ it.first }) { it.second } - .map { it.key to it.value.filterNotNull().distinct() } - .map { (k, v) -> - InheritanceNode( - DRI.from(k), - v.map { InheritanceNode(DRI.from(it)) }, - k.typeConstructor.supertypes.map { getClassDescriptorForType(it) } - .mapNotNull { cd -> cd.takeIf { it.kind == ClassKind.INTERFACE }?.let { DRI.from(it) } }, - isInterface = k.kind == ClassKind.INTERFACE - ) - } - - descriptorInheritanceTree.forEach { addToMap(it, mergeMap) } - psiInheritanceTree.forEach { addToMap(it, mergeMap) } + val inheritanceNodes = inheritanceBuilder.build(childrenDocumentables) + inheritanceNodes.forEach { addToMap(it, mergeMap) } val rootNodes = mergeMap.entries.filter { it.key.classNames in setOf("Any", "Object") //TODO: Probably should be matched by DRI, not just className @@ -539,47 +493,11 @@ class TreeViewPage( return rootNodes.let { Pair(it.mapNotNull(::classTree), it.mapNotNull(::interfaceTree)) } } - private fun generateInterfaceGraph() { - childrenDocumentables.values.filterIsInstance<DInterface>() - } - private fun getDocumentableEntries(node: WithDocumentables): List<Pair<DRI, Documentable>> = node.documentables.map { it.dri to it } + (node as? ContentPage)?.children?.filterIsInstance<WithDocumentables>() ?.flatMap(::getDocumentableEntries).orEmpty() - private fun getDescriptorMap(): Map<DRI, ClassDescriptor> { - val map: MutableMap<DRI, ClassDescriptor> = mutableMapOf() - childrenDocumentables - .mapNotNull { (k, v) -> - v.descriptorForPlatform()?.let { k to it }?.also { (k, v) -> map[k] = v } - }.map { it.second }.forEach { gatherSupertypes(it, map) } - - return map.toMap() - } - - private fun gatherSupertypes(descriptor: ClassDescriptor, map: MutableMap<DRI, ClassDescriptor>) { - map.putIfAbsent(DRI.from(descriptor), descriptor) - descriptor.typeConstructor.supertypes.map { getClassDescriptorForType(it) } - .forEach { gatherSupertypes(it, map) } - } - - private fun Documentable?.descriptorForPlatform(platform: Platform = Platform.jvm) = - (this as? WithSources).descriptorForPlatform(platform) - - private fun WithSources?.descriptorForPlatform(platform: Platform = Platform.jvm) = this?.let { - it.sources.entries.find { it.key.analysisPlatform == platform }?.value?.let { it as? DescriptorDocumentableSource }?.descriptor as? ClassDescriptor - } - - data class InheritanceNode( - val dri: DRI, - val children: List<InheritanceNode> = emptyList(), - val interfaces: List<DRI> = emptyList(), - val isInterface: Boolean = false - ) { - override fun equals(other: Any?): Boolean = other is InheritanceNode && other.dri == dri - override fun hashCode(): Int = dri.hashCode() - } } private fun Documentable.kind(): String? = diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt index e6e0e037..db6845a5 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/pages/htmlPreprocessors.kt @@ -8,7 +8,11 @@ import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.BooleanValue import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin object ResourcesInstaller : PageTransformer { override fun invoke(input: RootPageNode): RootPageNode = input.modified( @@ -21,7 +25,7 @@ object ResourcesInstaller : PageTransformer { ) } -object TreeViewInstaller : PageTransformer { +class TreeViewInstaller(private val context: DokkaContext) : PageTransformer { override fun invoke(input: RootPageNode): RootPageNode = install(input, input) as RootPageNode private fun install(node: PageNode, root: RootPageNode): PageNode = when (node) { @@ -37,7 +41,8 @@ object TreeViewInstaller : PageTransformer { classes = null, dri = node.dri, documentables = node.documentables, - root = root + root = root, + inheritanceBuilder = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { inheritanceBuilder } ) val nodeChildren = node.children.map { childNode -> @@ -56,7 +61,8 @@ object TreeViewInstaller : PageTransformer { classes = node.children.filterIsInstance<JavadocClasslikePageNode>(), dri = node.dri, documentables = node.documentables, - root = root + root = root, + inheritanceBuilder = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { inheritanceBuilder } ) return node.modified(children = node.children + packageTree) as JavadocPackagePageNode @@ -180,4 +186,4 @@ object DeprecatedPageCreator : PageTransformer { ) ) } -}
\ No newline at end of file +} diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt index 6a590bc7..f345e32b 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt @@ -1,16 +1,17 @@ package org.jetbrains.dokka.javadoc.renderer +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.javadoc.location.JavadocLocationProvider import org.jetbrains.dokka.javadoc.pages.* import org.jetbrains.dokka.javadoc.toNormalized -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.renderers.sourceSets import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.parent import org.jetbrains.dokka.links.sureClassNames import org.jetbrains.dokka.model.ImplementedInterfaces import org.jetbrains.dokka.model.InheritedMember -import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.formatToEndWithHtml import java.io.File diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/KorteJavadocRenderer.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/KorteJavadocRenderer.kt index f50ae124..aecd85f7 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/KorteJavadocRenderer.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/KorteJavadocRenderer.kt @@ -1,10 +1,6 @@ package org.jetbrains.dokka.javadoc.renderer import com.soywiz.korte.* -import org.jetbrains.dokka.javadoc.location.JavadocLocationProvider -import org.jetbrains.dokka.javadoc.pages.* -import org.jetbrains.dokka.javadoc.renderer.JavadocContentToHtmlTranslator.Companion.buildLink -import org.jetbrains.dokka.javadoc.toNormalized import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -12,14 +8,21 @@ import kotlinx.coroutines.runBlocking import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.renderers.OutputWriter import org.jetbrains.dokka.javadoc.JavadocPlugin +import org.jetbrains.dokka.javadoc.location.JavadocLocationProvider +import org.jetbrains.dokka.javadoc.pages.* +import org.jetbrains.dokka.javadoc.renderer.JavadocContentToHtmlTranslator.Companion.buildLink +import org.jetbrains.dokka.javadoc.toNormalized import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RendererSpecificPage +import org.jetbrains.dokka.pages.RenderingStrategy +import org.jetbrains.dokka.pages.RootPageNode 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.renderers.Renderer -import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.analysis.kotlin.internal.InheritanceNode import java.time.LocalDate typealias TemplateMap = Map<String, Any?> @@ -146,9 +149,9 @@ class KorteJavadocRenderer(val context: DokkaContext, resourceDir: String) : }, TeFunction("renderInheritanceGraph") { args -> @Suppress("UNCHECKED_CAST") - val rootNodes = args.first() as List<TreeViewPage.InheritanceNode> + val rootNodes = args.first() as List<InheritanceNode> - fun drawRec(node: TreeViewPage.InheritanceNode): String = + fun drawRec(node: InheritanceNode): String = "<li class=\"circle\">" + node.dri.let { dri -> listOfNotNull( dri.packageName, @@ -170,8 +173,9 @@ class KorteJavadocRenderer(val context: DokkaContext, resourceDir: String) : }, Filter("length") { subject.dynamicLength() }, TeFunction("hasAnyDescription") { args -> - args.first().safeAs<List<HashMap<String, String>>>() - ?.any { it["description"]?.trim()?.isNotEmpty() ?: false } + @Suppress("UNCHECKED_CAST") + val map = args.first() as? List<HashMap<String, String>> + map?.any { it["description"]?.trim()?.isNotEmpty() ?: false } } ).forEach { when (it) { diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/SearchScriptsCreator.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/SearchScriptsCreator.kt index 6c2fed58..a7a01fc1 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/SearchScriptsCreator.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/renderer/SearchScriptsCreator.kt @@ -1,16 +1,15 @@ package org.jetbrains.dokka.javadoc.renderer -import org.jetbrains.dokka.javadoc.pages.* -import org.jetbrains.dokka.javadoc.renderer.SearchRecord.Companion.allTypes import org.jetbrains.dokka.base.renderers.sourceSets -import org.jetbrains.dokka.base.resolvers.local.resolveOrThrow import org.jetbrains.dokka.base.resolvers.local.LocationProvider +import org.jetbrains.dokka.base.resolvers.local.resolveOrThrow +import org.jetbrains.dokka.javadoc.pages.* +import org.jetbrains.dokka.javadoc.renderer.SearchRecord.Companion.allTypes import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DisplaySourceSet import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.formatToEndWithHtml -import java.lang.StringBuilder class SearchScriptsCreator(private val locationProvider: LocationProvider) { diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/signatures/JavadocSignatureProvider.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/signatures/JavadocSignatureProvider.kt index 385e0986..f81b8c5c 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/signatures/JavadocSignatureProvider.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/signatures/JavadocSignatureProvider.kt @@ -1,12 +1,12 @@ package org.jetbrains.dokka.javadoc.signatures -import org.jetbrains.dokka.javadoc.translators.documentables.JavadocPageContentBuilder import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.signatures.JvmSignatureUtils import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.javadoc.translators.documentables.JavadocPageContentBuilder import org.jetbrains.dokka.kotlinAsJava.signatures.JavaSignatureUtils import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.sureClassNames diff --git a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/translators/documentables/JavadocPageContentBuilder.kt b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/translators/documentables/JavadocPageContentBuilder.kt index e70f8370..68faba5f 100644 --- a/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/translators/documentables/JavadocPageContentBuilder.kt +++ b/plugins/javadoc/src/main/kotlin/org/jetbrains/dokka/javadoc/translators/documentables/JavadocPageContentBuilder.kt @@ -1,16 +1,15 @@ package org.jetbrains.dokka.javadoc.translators.documentables -import org.jetbrains.dokka.javadoc.pages.JavadocSignatureContentNode import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.javadoc.pages.JavadocSignatureContentNode import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.ContentKind import org.jetbrains.dokka.pages.ContentNode import org.jetbrains.dokka.utilities.DokkaLogger -import java.lang.IllegalStateException class JavadocPageContentBuilder( commentsConverter: CommentsToContentConverter, @@ -77,4 +76,4 @@ class JavadocPageContentBuilder( supertypes = supertypes ) } -}
\ No newline at end of file +} diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt index 4d059d0e..0e018b8e 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/AbstractJavadocTemplateMapTest.kt @@ -1,14 +1,16 @@ package org.jetbrains.dokka.javadoc -import org.jetbrains.dokka.* +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.javadoc.location.JavadocLocationProvider import org.jetbrains.dokka.javadoc.pages.JavadocPageNode import org.jetbrains.dokka.javadoc.renderer.JavadocContentToTemplateMapTranslator -import org.jetbrains.dokka.javadoc.JavadocPlugin -import org.jetbrains.dokka.javadoc.location.JavadocLocationProvider +import org.jetbrains.dokka.jdk +import org.jetbrains.dokka.kotlinStdlib import org.jetbrains.dokka.model.withDescendants import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.* -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest internal abstract class AbstractJavadocTemplateMapTest : BaseAbstractTest() { protected var config: DokkaConfigurationImpl = dokkaConfiguration { diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/Asserts.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/Asserts.kt index 9f48b1db..ca739ed3 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/Asserts.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/Asserts.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.javadoc -import kotlin.contracts.* +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract // TODO replace with assertIs<T> from kotlin-test as part of #2924 @OptIn(ExperimentalContracts::class) diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt index 907b032b..eca46421 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocAccessorNamingTest.kt @@ -1,9 +1,9 @@ package org.jetbrains.dokka.javadoc import org.jsoup.Jsoup -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.assertEquals -import utils.* +import org.junit.jupiter.api.Test +import utils.TestOutputWriterPlugin internal class JavadocAccessorNamingTest : AbstractJavadocTemplateMapTest() { @@ -77,4 +77,4 @@ internal class JavadocAccessorNamingTest : AbstractJavadocTemplateMapTest() { } } } -}
\ No newline at end of file +} diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt index 87c5246d..afb4f04c 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLinkingTest.kt @@ -1,11 +1,11 @@ package org.jetbrains.dokka.javadoc.location import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.jdk import org.jetbrains.dokka.kotlinStdlib import org.jetbrains.dokka.model.doc.DocumentationLink import org.jetbrains.dokka.model.doc.Text -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.utilities.cast import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt index f0e2b49d..6f61f72b 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/location/JavadocLocationTest.kt @@ -1,19 +1,21 @@ package org.jetbrains.dokka.javadoc.location -import org.jetbrains.dokka.* +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.javadoc.JavadocPlugin import org.jetbrains.dokka.javadoc.pages.JavadocClasslikePageNode +import org.jetbrains.dokka.javadoc.pages.JavadocFunctionNode import org.jetbrains.dokka.javadoc.pages.JavadocPackagePageNode import org.jetbrains.dokka.javadoc.renderer.JavadocContentToHtmlTranslator -import org.jetbrains.dokka.javadoc.JavadocPlugin +import org.jetbrains.dokka.jdk +import org.jetbrains.dokka.kotlinStdlib import org.jetbrains.dokka.model.firstChildOfType import org.jetbrains.dokka.pages.RootPageNode import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest -import org.jetbrains.dokka.javadoc.pages.JavadocFunctionNode -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class JavadocLocationTest : BaseAbstractTest() { diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt index 89e4c535..1947fec7 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/packagelist/JavadocPackageListTest.kt @@ -1,9 +1,9 @@ package org.jetbrains.dokka.javadoc.packagelist import org.jetbrains.dokka.javadoc.AbstractJavadocTemplateMapTest +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin -import org.junit.jupiter.api.Assertions.* internal class JavadocPackageListTest : AbstractJavadocTemplateMapTest() { @Test diff --git a/plugins/jekyll/build.gradle.kts b/plugins/jekyll/build.gradle.kts index ab99e220..ca8ad618 100644 --- a/plugins/jekyll/build.gradle.kts +++ b/plugins/jekyll/build.gradle.kts @@ -7,10 +7,12 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) + implementation(projects.plugins.base) implementation(projects.plugins.gfm) + implementation(kotlin("reflect")) + testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) diff --git a/plugins/jekyll/jekyll-template-processing/build.gradle.kts b/plugins/jekyll/jekyll-template-processing/build.gradle.kts index 936c77bd..d18b203e 100644 --- a/plugins/jekyll/jekyll-template-processing/build.gradle.kts +++ b/plugins/jekyll/jekyll-template-processing/build.gradle.kts @@ -7,7 +7,6 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) implementation(projects.plugins.base) implementation(projects.plugins.jekyll) @@ -16,6 +15,7 @@ dependencies { implementation(projects.plugins.gfm) implementation(projects.plugins.gfm.gfmTemplateProcessing) + implementation(kotlin("reflect")) implementation(libs.kotlinx.coroutines.core) testImplementation(projects.core.testApi) diff --git a/plugins/kotlin-as-java/api/kotlin-as-java.api b/plugins/kotlin-as-java/api/kotlin-as-java.api index 301e432e..bc710a66 100644 --- a/plugins/kotlin-as-java/api/kotlin-as-java.api +++ b/plugins/kotlin-as-java/api/kotlin-as-java.api @@ -15,6 +15,10 @@ public final class org/jetbrains/dokka/kotlinAsJava/TransformToJavaKt { public static synthetic fun transformToJava$default (Lorg/jetbrains/dokka/model/DProperty;Lorg/jetbrains/dokka/plugability/DokkaContext;ZLjava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/dokka/model/DProperty; } +public final class org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverter { + public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V +} + public final class org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverterKt { public static final fun getJvmNameProvider ()Lorg/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider; } @@ -93,7 +97,7 @@ public final class org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaDocu } public final class org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaPageCreator : org/jetbrains/dokka/base/translators/documentables/DefaultPageCreator { - public fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;)V + public fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser;)V public fun pageForProperty (Lorg/jetbrains/dokka/model/DProperty;)Lorg/jetbrains/dokka/pages/MemberPageNode; } diff --git a/plugins/kotlin-as-java/build.gradle.kts b/plugins/kotlin-as-java/build.gradle.kts index 471578db..2132be86 100644 --- a/plugins/kotlin-as-java/build.gradle.kts +++ b/plugins/kotlin-as-java/build.gradle.kts @@ -7,15 +7,16 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) - compileOnly(projects.kotlinAnalysis) + compileOnly(projects.subprojects.analysisKotlinApi) + implementation(projects.plugins.base) + + implementation(kotlin("reflect")) + + testImplementation(libs.jsoup) testImplementation(projects.plugins.base) testImplementation(projects.plugins.base.baseTestUtils) testImplementation(projects.core.contentMatcherTestUtils) - testImplementation(libs.jsoup) - testImplementation(projects.kotlinAnalysis) - testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) diff --git a/plugins/kotlin-as-java/src/main/kotlin/CollectionExtensions.kt b/plugins/kotlin-as-java/src/main/kotlin/CollectionExtensions.kt new file mode 100644 index 00000000..77e4ab0a --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/CollectionExtensions.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.kotlinAsJava + +// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 +internal inline 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 +} diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinCompanion.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinCompanion.kt index dec1591a..68ad1ecf 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinCompanion.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinCompanion.kt @@ -46,20 +46,11 @@ internal fun DObject.companionInstancePropertyForJava(): DProperty? { ) } -internal fun DObject.companionAsJava(): DObject? { - if (hasNothingToRender()) return null - - return asJava( - excludedProps = staticPropertiesForJava(), - excludedFunctions = staticFunctionsForJava() - ) -} - /** * Hide companion object if there isn't members of parents. * Properties and functions that are moved to outer class are not counted as members. */ -private fun DObject.hasNothingToRender(): Boolean { +internal fun DObject.hasNothingToRender(): Boolean { val nonStaticPropsCount = properties.size - staticPropertiesForJava().size val nonStaticFunctionsCount = functions.size - staticFunctionsForJava().size val classLikesCount = classlikes.size diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt index 4df0d3c5..309781aa 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt @@ -5,14 +5,13 @@ import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameProvider import org.jetbrains.dokka.kotlinAsJava.transformers.withCallableName import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.PointingToDeclaration import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.properties.PropertyContainer -import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin val jvmNameProvider = JvmNameProvider() internal const val OBJECT_INSTANCE_NAME = "INSTANCE" @@ -29,469 +28,477 @@ internal val DProperty.isJvmField: Boolean internal val DFunction.isJvmStatic: Boolean get() = jvmStatic() != null -internal fun DPackage.asJava(): DPackage { - val syntheticClasses = - (properties.map { jvmNameProvider.nameForSyntheticClass(it) to it } - + functions.map { jvmNameProvider.nameForSyntheticClass(it) to it }) - .groupBy({ it.first }) { it.second } - .map { (syntheticClassName, nodes) -> - DClass( - dri = dri.withClass(syntheticClassName.name), - name = syntheticClassName.name, - properties = nodes - .filterIsInstance<DProperty>() - .filterNot { it.hasJvmSynthetic() } - .map { it.asJava(true) }, - constructors = emptyList(), - functions = ( - nodes - .filterIsInstance<DProperty>() - .filterNot { it.isConst || it.isJvmField || it.hasJvmSynthetic() } - .flatMap { it.javaAccessors(relocateToClass = syntheticClassName.name) } + - nodes - .filterIsInstance<DFunction>() - .flatMap { it.asJava(syntheticClassName.name, true) }) - .filterNot { it.hasJvmSynthetic() }, - classlikes = emptyList(), - sources = emptyMap(), - expectPresentInSet = null, - visibility = sourceSets.associateWith { - JavaVisibility.Public - }, - companion = null, - generics = emptyList(), - supertypes = emptyMap(), - documentation = emptyMap(), - modifier = sourceSets.associateWith { JavaModifier.Final }, - sourceSets = sourceSets, - isExpectActual = false, - extra = PropertyContainer.empty() - ) - } +private fun DProperty.hasModifier(modifier: ExtraModifiers.KotlinOnlyModifiers): Boolean = + extra[AdditionalModifiers] + ?.content + ?.any { (_, modifiers) -> modifier in modifiers } == true - return copy( - functions = emptyList(), - properties = emptyList(), - classlikes = classlikes.map { it.asJava() } + syntheticClasses, - typealiases = emptyList() - ) -} +class KotlinToJavaConverter( + private val context: DokkaContext +) { + private val kotlinToJavaMapper by lazy { + context.plugin<InternalKotlinAnalysisPlugin>().querySingle { kotlinToJavaService } + } -internal fun DProperty.asJava( - isTopLevel: Boolean = false, - relocateToClass: String? = null, - isFromObjectOrCompanion: Boolean = false -) = - copy( - dri = if (relocateToClass.isNullOrBlank()) { - dri - } else { - dri.withClass(relocateToClass) - }, - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { - if (isConst || isJvmField || (getter == null && setter == null) || (isFromObjectOrCompanion && isLateInit)) { - it.value.asJava() - } else { - it.value.propertyVisibilityAsJava() - } - }, - type = type.asJava(), // TODO: check - setter = null, - getter = null, // Removing getters and setters as they will be available as functions - extra = if (isTopLevel || isConst || (isFromObjectOrCompanion && isJvmField) || (isFromObjectOrCompanion && isLateInit)) - extra + extra.mergeAdditionalModifiers( - sourceSets.associateWith { - setOf(ExtraModifiers.JavaOnlyModifiers.Static) + internal fun DPackage.asJava(): DPackage { + val syntheticClasses = + (properties.map { jvmNameProvider.nameForSyntheticClass(it) to it } + + functions.map { jvmNameProvider.nameForSyntheticClass(it) to it }) + .groupBy({ it.first }) { it.second } + .map { (syntheticClassName, nodes) -> + DClass( + dri = dri.withClass(syntheticClassName.name), + name = syntheticClassName.name, + properties = nodes + .filterIsInstance<DProperty>() + .filterNot { it.hasJvmSynthetic() } + .map { it.asJava(true) }, + constructors = emptyList(), + functions = ( + nodes + .filterIsInstance<DProperty>() + .filterNot { it.isConst || it.isJvmField || it.hasJvmSynthetic() } + .flatMap { it.javaAccessors(relocateToClass = syntheticClassName.name) } + + nodes + .filterIsInstance<DFunction>() + .flatMap { it.asJava(syntheticClassName.name, true) }) + .filterNot { it.hasJvmSynthetic() }, + classlikes = emptyList(), + sources = emptyMap(), + expectPresentInSet = null, + visibility = sourceSets.associateWith { + JavaVisibility.Public + }, + companion = null, + generics = emptyList(), + supertypes = emptyMap(), + documentation = emptyMap(), + modifier = sourceSets.associateWith { JavaModifier.Final }, + sourceSets = sourceSets, + isExpectActual = false, + extra = PropertyContainer.empty() + ) } - ) - else extra - ) -internal fun Visibility.asJava() = - when (this) { - is JavaVisibility -> this - is KotlinVisibility.Public, KotlinVisibility.Internal -> JavaVisibility.Public - is KotlinVisibility.Private -> JavaVisibility.Private - is KotlinVisibility.Protected -> JavaVisibility.Protected + return copy( + functions = emptyList(), + properties = emptyList(), + classlikes = classlikes.map { it.asJava() } + syntheticClasses, + typealiases = emptyList() + ) } -internal fun DProperty.javaModifierFromSetter() = - modifier.mapValues { - when { - it.value is JavaModifier -> it.value - setter == null -> JavaModifier.Final - else -> JavaModifier.Empty + internal fun DProperty.asJava( + isTopLevel: Boolean = false, + relocateToClass: String? = null, + isFromObjectOrCompanion: Boolean = false + ) = + copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { + if (isConst || isJvmField || (getter == null && setter == null) || (isFromObjectOrCompanion && isLateInit)) { + it.value.asJava() + } else { + it.value.propertyVisibilityAsJava() + } + }, + type = type.asJava(), // TODO: check + setter = null, + getter = null, // Removing getters and setters as they will be available as functions + extra = if (isTopLevel || isConst || (isFromObjectOrCompanion && isJvmField) || (isFromObjectOrCompanion && isLateInit)) + extra + extra.mergeAdditionalModifiers( + sourceSets.associateWith { + setOf(ExtraModifiers.JavaOnlyModifiers.Static) + } + ) + else extra + ) + + internal fun Visibility.asJava() = + when (this) { + is JavaVisibility -> this + is KotlinVisibility.Public, KotlinVisibility.Internal -> JavaVisibility.Public + is KotlinVisibility.Private -> JavaVisibility.Private + is KotlinVisibility.Protected -> JavaVisibility.Protected } - } -internal fun DProperty.javaAccessors(isTopLevel: Boolean = false, relocateToClass: String? = null): List<DFunction> = - listOfNotNull( - getter?.let { getter -> - val name = "get" + name.capitalize() - getter.copy( - dri = if (relocateToClass.isNullOrBlank()) { - getter.dri + internal fun DProperty.javaModifierFromSetter() = + modifier.mapValues { + when { + it.value is JavaModifier -> it.value + setter == null -> JavaModifier.Final + else -> JavaModifier.Empty + } + } + + internal fun DProperty.javaAccessors( + isTopLevel: Boolean = false, + relocateToClass: String? = null + ): List<DFunction> = + listOfNotNull( + getter?.let { getter -> + val name = "get" + name.capitalize() + getter.copy( + dri = if (relocateToClass.isNullOrBlank()) { + getter.dri + } else { + getter.dri.withClass(relocateToClass) + }.withCallableName(name), + name = name, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = getter.type.asJava(), + extra = if (isTopLevel) getter.extra + + getter.extra.mergeAdditionalModifiers( + sourceSets.associateWith { + setOf(ExtraModifiers.JavaOnlyModifiers.Static) + } + ) + else getter.extra + ) + }, + setter?.let { setter -> + val name = "set" + name.capitalize() + val baseDRI = (if (relocateToClass.isNullOrBlank()) { + setter.dri } else { - getter.dri.withClass(relocateToClass) - }.withCallableName(name), - name = name, - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { JavaVisibility.Public }, - type = getter.type.asJava(), - extra = if (isTopLevel) getter.extra + - getter.extra.mergeAdditionalModifiers( - sourceSets.associateWith { - setOf(ExtraModifiers.JavaOnlyModifiers.Static) - } + setter.dri.withClass(relocateToClass) + }).withCallableName(name) + setter.copy( + dri = baseDRI, + name = name, + parameters = setter.parameters.map { + it.copy( + dri = baseDRI.copy( + target = it.dri.target, + extra = it.dri.extra + ), type = it.type.asJava() ) - else getter.extra - ) - }, - setter?.let { setter -> - val name = "set" + name.capitalize() - val baseDRI = (if (relocateToClass.isNullOrBlank()) { - setter.dri - } else { - setter.dri.withClass(relocateToClass) - }).withCallableName(name) - setter.copy( - dri = baseDRI, - name = name, - parameters = setter.parameters.map { - it.copy( - dri = baseDRI.copy( - target = it.dri.target, - extra = it.dri.extra - ), type = it.type.asJava() + }, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = Void, + extra = if (isTopLevel) setter.extra + setter.extra.mergeAdditionalModifiers( + sourceSets.associateWith { + setOf(ExtraModifiers.JavaOnlyModifiers.Static) + } ) - }, - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { JavaVisibility.Public }, - type = Void, - extra = if (isTopLevel) setter.extra + setter.extra.mergeAdditionalModifiers( + else setter.extra + ) + } + ) + + private fun DFunction.asJava( + containingClassName: String, + newName: String, + parameters: List<DParameter>, + isTopLevel: Boolean = false + ): DFunction { + return copy( + dri = dri.copy(classNames = containingClassName, callable = dri.callable?.copy(name = newName)), + name = newName, + type = type.asJava(), + modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Final } && isConstructor) + sourceSets.associateWith { JavaModifier.Empty } + else sourceSets.associateWith { modifier.values.first() }, + parameters = listOfNotNull(receiver?.asJava()) + parameters.map { it.asJava() }, + visibility = visibility.map { (sourceSet, visibility) -> Pair(sourceSet, visibility.asJava()) }.toMap(), + receiver = null, + extra = if (isTopLevel || isJvmStatic) { + extra + extra.mergeAdditionalModifiers( sourceSets.associateWith { setOf(ExtraModifiers.JavaOnlyModifiers.Static) } ) - else setter.extra - ) - } - ) + } else { + extra + } + ) + } -private fun DFunction.asJava( - containingClassName: String, - newName: String, - parameters: List<DParameter>, - isTopLevel: Boolean = false -): DFunction { - return copy( - dri = dri.copy(classNames = containingClassName, callable = dri.callable?.copy(name = newName)), - name = newName, - type = type.asJava(), - modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Final } && isConstructor) - sourceSets.associateWith { JavaModifier.Empty } - else sourceSets.associateWith { modifier.values.first() }, - parameters = listOfNotNull(receiver?.asJava()) + parameters.map { it.asJava() }, - visibility = visibility.map { (sourceSet, visibility) -> Pair(sourceSet, visibility.asJava()) }.toMap(), - receiver = null, - extra = if (isTopLevel || isJvmStatic) { - extra + extra.mergeAdditionalModifiers( - sourceSets.associateWith { - setOf(ExtraModifiers.JavaOnlyModifiers.Static) - } - ) + private fun DFunction.withJvmOverloads( + containingClassName: String, + newName: String, + isTopLevel: Boolean = false + ): List<DFunction>? { + val (paramsWithDefaults, paramsWithoutDefaults) = parameters + .withIndex() + .partition { (_, p) -> p.extra[DefaultValue] != null } + return paramsWithDefaults + .runningFold(paramsWithoutDefaults) { acc, param -> (acc + param) } + .map { params -> + asJava( + containingClassName, + newName, + params + .sortedBy(IndexedValue<DParameter>::index) + .map { it.value }, + isTopLevel + ) + } + .reversed() + .takeIf { it.isNotEmpty() } + } + + internal fun DFunction.asJava(containingClassName: String, isTopLevel: Boolean = false): List<DFunction> { + val newName = when { + isConstructor -> containingClassName + else -> name + } + val baseFunction = asJava(containingClassName, newName, parameters, isTopLevel) + return if (hasJvmOverloads()) { + withJvmOverloads(containingClassName, newName, isTopLevel) ?: listOf(baseFunction) } else { - extra + listOf(baseFunction) } - ) -} + } -private fun DFunction.withJvmOverloads( - containingClassName: String, - newName: String, - isTopLevel: Boolean = false -): List<DFunction>? { - val (paramsWithDefaults, paramsWithoutDefaults) = parameters - .withIndex() - .partition { (_, p) -> p.extra[DefaultValue] != null } - return paramsWithDefaults - .runningFold(paramsWithoutDefaults) { acc, param -> (acc + param) } - .map { params -> - asJava( - containingClassName, - newName, - params - .sortedBy(IndexedValue<DParameter>::index) - .map { it.value }, - isTopLevel - ) - } - .reversed() - .takeIf { it.isNotEmpty() } -} + internal fun DClasslike.asJava(): DClasslike = when (this) { + is DClass -> asJava() + is DEnum -> asJava() + is DAnnotation -> asJava() + is DObject -> asJava() + is DInterface -> asJava() + else -> throw IllegalArgumentException("$this shouldn't be here") + } + + internal fun DClass.asJava(): DClass = copy( + constructors = constructors + .filterNot { it.hasJvmSynthetic() } + .flatMap { + it.asJava( + dri.classNames ?: name + ) + }, // name may not always be valid here, however classNames should always be not null + functions = functionsInJava(), + properties = propertiesInJava(), + classlikes = classlikesInJava(), + generics = generics.map { it.asJava() }, + companion = companion?.companionAsJava(), + supertypes = supertypes.mapValues { it.value.map { it.asJava() } }, + modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Empty }) sourceSets.associateWith { JavaModifier.Final } + else sourceSets.associateWith { modifier.values.first() } + ) -internal fun DFunction.asJava(containingClassName: String, isTopLevel: Boolean = false): List<DFunction> { - val newName = when { - isConstructor -> containingClassName - else -> name + /** + * Companion objects requires some custom logic for rendering as Java. + * They are excluded from usual classlikes rendering and added after. + */ + internal fun DClass.classlikesInJava(): List<DClasslike> { + val classlikes = classlikes + .filter { it.name != companion?.name } + .map { it.asJava() } + + val companionAsJava = companion?.companionAsJava() + return if (companionAsJava != null) classlikes.plus(companionAsJava) else classlikes } - val baseFunction = asJava(containingClassName, newName, parameters, isTopLevel) - return if (hasJvmOverloads()) { - withJvmOverloads(containingClassName, newName, isTopLevel) ?: listOf(baseFunction) - } else { - listOf(baseFunction) + + + internal fun DClass.functionsInJava(): List<DFunction> = + properties + .filter { !it.isJvmField && !it.hasJvmSynthetic() } + .flatMap { property -> listOfNotNull(property.getter, property.setter) } + .plus(functions) + .plus(companion.staticFunctionsForJava()) + .filterNot { it.hasJvmSynthetic() } + .flatMap { it.asJava(it.dri.classNames ?: it.name) } + + internal fun DClass.propertiesInJava(): List<DProperty> { + val propertiesFromCompanion = companion + .staticPropertiesForJava() + .filterNot { it.hasJvmSynthetic() } + .map { it.asJava(isFromObjectOrCompanion = true) } + val companionInstanceProperty = companion?.companionInstancePropertyForJava() + val ownProperties = properties + .filterNot { it.hasJvmSynthetic() } + .map { it.asJava() } + + return propertiesFromCompanion + ownProperties + listOfNotNull(companionInstanceProperty) } -} -internal fun DClasslike.asJava(): DClasslike = when (this) { - is DClass -> asJava() - is DEnum -> asJava() - is DAnnotation -> asJava() - is DObject -> asJava() - is DInterface -> asJava() - else -> throw IllegalArgumentException("$this shouldn't be here") -} + private fun DTypeParameter.asJava(): DTypeParameter = copy( + variantTypeParameter = variantTypeParameter.withDri(dri.possiblyAsJava()), + bounds = bounds.map { it.asJava() } + ) -internal fun DClass.asJava(): DClass = copy( - constructors = constructors - .filterNot { it.hasJvmSynthetic() } - .flatMap { - it.asJava( - dri.classNames ?: name - ) - }, // name may not always be valid here, however classNames should always be not null - functions = functionsInJava(), - properties = propertiesInJava(), - classlikes = classlikesInJava(), - generics = generics.map { it.asJava() }, - companion = companion?.companionAsJava(), - supertypes = supertypes.mapValues { it.value.map { it.asJava() } }, - modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Empty }) sourceSets.associateWith { JavaModifier.Final } - else sourceSets.associateWith { modifier.values.first() } -) - -/** - * Companion objects requires some custom logic for rendering as Java. - * They are excluded from usual classlikes rendering and added after. - */ -internal fun DClass.classlikesInJava(): List<DClasslike> { - val classlikes = classlikes - .filter { it.name != companion?.name } - .map { it.asJava() } - - val companionAsJava = companion?.companionAsJava() - return if (companionAsJava != null) classlikes.plus(companionAsJava) else classlikes -} + private fun Projection.asJava(): Projection = when (this) { + is Star -> Star + is Covariance<*> -> copy(inner.asJava()) + is Contravariance<*> -> copy(inner.asJava()) + is Invariance<*> -> copy(inner.asJava()) + is Bound -> asJava() + } + private fun Bound.asJava(): Bound = when (this) { + is TypeParameter -> copy(dri.possiblyAsJava()) + is GenericTypeConstructor -> copy( + dri = dri.possiblyAsJava(), + projections = projections.map { it.asJava() } + ) -internal fun DClass.functionsInJava(): List<DFunction> = - properties - .filter { !it.isJvmField && !it.hasJvmSynthetic() } - .flatMap { property -> listOfNotNull(property.getter, property.setter) } - .plus(functions) - .plus(companion.staticFunctionsForJava()) - .filterNot { it.hasJvmSynthetic() } - .flatMap { it.asJava(it.dri.classNames ?: it.name) } - -internal fun DClass.propertiesInJava(): List<DProperty> { - val propertiesFromCompanion = companion - .staticPropertiesForJava() - .filterNot { it.hasJvmSynthetic() } - .map { it.asJava(isFromObjectOrCompanion = true) } - val companionInstanceProperty = companion?.companionInstancePropertyForJava() - val ownProperties = properties - .filterNot { it.hasJvmSynthetic() } - .map { it.asJava() } - - return propertiesFromCompanion + ownProperties + listOfNotNull(companionInstanceProperty) -} + is FunctionalTypeConstructor -> copy( + dri = dri.possiblyAsJava(), + projections = projections.map { it.asJava() } + ) -private fun DTypeParameter.asJava(): DTypeParameter = copy( - variantTypeParameter = variantTypeParameter.withDri(dri.possiblyAsJava()), - bounds = bounds.map { it.asJava() } -) - -private fun Projection.asJava(): Projection = when (this) { - is Star -> Star - is Covariance<*> -> copy(inner.asJava()) - is Contravariance<*> -> copy(inner.asJava()) - is Invariance<*> -> copy(inner.asJava()) - is Bound -> asJava() -} + is TypeAliased -> copy( + typeAlias = typeAlias.asJava(), + inner = inner.asJava() + ) + + is Nullable -> copy(inner.asJava()) + is DefinitelyNonNullable -> copy(inner.asJava()) + is PrimitiveJavaType -> this + is Void -> this + is JavaObject -> this + is Dynamic -> this + is UnresolvedBound -> this + } -private fun Bound.asJava(): Bound = when (this) { - is TypeParameter -> copy(dri.possiblyAsJava()) - is GenericTypeConstructor -> copy( - dri = dri.possiblyAsJava(), - projections = projections.map { it.asJava() } + internal fun DEnum.asJava(): DEnum = copy( + constructors = constructors.flatMap { it.asJava(dri.classNames ?: name) }, + functions = functions + .plus( + properties + .filter { !it.isJvmField && !it.hasJvmSynthetic() } + .flatMap { listOf(it.getter, it.setter) } + ) + .filterNotNull() + .filterNot { it.hasJvmSynthetic() } + .flatMap { it.asJava(dri.classNames ?: name) }, + properties = properties + .filterNot { it.hasJvmSynthetic() } + .map { it.asJava() }, + classlikes = classlikes.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } } +// , entries = entries.map { it.asJava() } ) - is FunctionalTypeConstructor -> copy( - dri = dri.possiblyAsJava(), - projections = projections.map { it.asJava() } + + /** + * Parameters [excludedProps] and [excludedFunctions] used for rendering companion objects + * where some members (that lifted to outer class) are not rendered + */ + internal fun DObject.asJava( + excludedProps: List<DProperty> = emptyList(), + excludedFunctions: List<DFunction> = emptyList() + ): DObject = copy( + functions = functions + .plus( + properties + .filterNot { it in excludedProps } + .filter { !it.isJvmField && !it.isConst && !it.isLateInit && !it.hasJvmSynthetic() } + .flatMap { listOf(it.getter, it.setter) } + ) + .filterNotNull() + .filterNot { it in excludedFunctions } + .filterNot { it.hasJvmSynthetic() } + .flatMap { it.asJava(dri.classNames ?: name.orEmpty()) }, + properties = properties + .filterNot { it.hasJvmSynthetic() } + .filterNot { it in excludedProps } + .map { it.asJava(isFromObjectOrCompanion = true) } + + DProperty( + name = OBJECT_INSTANCE_NAME, + modifier = sourceSets.associateWith { JavaModifier.Final }, + dri = dri.copy(callable = Callable(OBJECT_INSTANCE_NAME, null, emptyList())), + documentation = emptyMap(), + sources = emptyMap(), + visibility = sourceSets.associateWith { + JavaVisibility.Public + }, + type = GenericTypeConstructor(dri, emptyList()), + setter = null, + getter = null, + sourceSets = sourceSets, + receiver = null, + generics = emptyList(), + expectPresentInSet = expectPresentInSet, + isExpectActual = false, + extra = PropertyContainer.withAll(sourceSets.map { + mapOf(it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)).toAdditionalModifiers() + }) + ), + classlikes = classlikes.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } } ) - is TypeAliased -> copy( - typeAlias = typeAlias.asJava(), - inner = inner.asJava() + + internal fun DInterface.asJava(): DInterface = copy( + functions = functions + .plus( + properties + .filter { it.jvmField() == null && !it.hasJvmSynthetic() } + .flatMap { listOf(it.getter, it.setter) } + ) + .filterNotNull() + .filterNot { it.hasJvmSynthetic() } + .flatMap { it.asJava(dri.classNames ?: name) }, + properties = emptyList(), + classlikes = classlikes.map { it.asJava() }, // TODO: public static final class DefaultImpls with impls for methods + generics = generics.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } } ) - is Nullable -> copy(inner.asJava()) - is DefinitelyNonNullable -> copy(inner.asJava()) - is PrimitiveJavaType -> this - is Void -> this - is JavaObject -> this - is Dynamic -> this - is UnresolvedBound -> this -} -internal fun DEnum.asJava(): DEnum = copy( - constructors = constructors.flatMap { it.asJava(dri.classNames ?: name) }, - functions = functions - .plus( - properties - .filter { !it.isJvmField && !it.hasJvmSynthetic() } - .flatMap { listOf(it.getter, it.setter) } - ) - .filterNotNull() - .filterNot { it.hasJvmSynthetic() } - .flatMap { it.asJava(dri.classNames ?: name) }, - properties = properties - .filterNot { it.hasJvmSynthetic() } - .map { it.asJava() }, - classlikes = classlikes.map { it.asJava() }, - supertypes = supertypes.mapValues { it.value.map { it.asJava() } } -// , entries = entries.map { it.asJava() } -) - -/** - * Parameters [excludedProps] and [excludedFunctions] used for rendering companion objects - * where some members (that lifted to outer class) are not rendered - */ -internal fun DObject.asJava( - excludedProps: List<DProperty> = emptyList(), - excludedFunctions: List<DFunction> = emptyList() -): DObject = copy( - functions = functions - .plus( - properties - .filterNot { it in excludedProps } - .filter { !it.isJvmField && !it.isConst && !it.isLateInit && !it.hasJvmSynthetic() } - .flatMap { listOf(it.getter, it.setter) } - ) - .filterNotNull() - .filterNot { it in excludedFunctions } - .filterNot { it.hasJvmSynthetic() } - .flatMap { it.asJava(dri.classNames ?: name.orEmpty()) }, - properties = properties - .filterNot { it.hasJvmSynthetic() } - .filterNot { it in excludedProps } - .map { it.asJava(isFromObjectOrCompanion = true) } + - DProperty( - name = OBJECT_INSTANCE_NAME, - modifier = sourceSets.associateWith { JavaModifier.Final }, - dri = dri.copy(callable = Callable(OBJECT_INSTANCE_NAME, null, emptyList())), - documentation = emptyMap(), - sources = emptyMap(), - visibility = sourceSets.associateWith { - JavaVisibility.Public - }, - type = GenericTypeConstructor(dri, emptyList()), - setter = null, - getter = null, - sourceSets = sourceSets, - receiver = null, - generics = emptyList(), - expectPresentInSet = expectPresentInSet, - isExpectActual = false, - extra = PropertyContainer.withAll(sourceSets.map { - mapOf(it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)).toAdditionalModifiers() - }) - ), - classlikes = classlikes.map { it.asJava() }, - supertypes = supertypes.mapValues { it.value.map { it.asJava() } } -) - -internal fun DInterface.asJava(): DInterface = copy( - functions = functions - .plus( - properties - .filter { it.jvmField() == null && !it.hasJvmSynthetic() } - .flatMap { listOf(it.getter, it.setter) } - ) - .filterNotNull() - .filterNot { it.hasJvmSynthetic() } - .flatMap { it.asJava(dri.classNames ?: name) }, - properties = emptyList(), - classlikes = classlikes.map { it.asJava() }, // TODO: public static final class DefaultImpls with impls for methods - generics = generics.map { it.asJava() }, - supertypes = supertypes.mapValues { it.value.map { it.asJava() } } -) - -internal fun DAnnotation.asJava(): DAnnotation = copy( - properties = properties.map { it.asJava() }, - constructors = emptyList(), - classlikes = classlikes.map { it.asJava() } -) // TODO investigate if annotation class can have methods and properties not from constructor - -internal fun DParameter.asJava(): DParameter = copy( - type = type.asJava(), - name = if (name.isNullOrBlank()) "\$self" else name -) - -internal fun Visibility.propertyVisibilityAsJava(): Visibility = - if (this is JavaVisibility) this - else JavaVisibility.Private - -internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() - .find { it.typeFqName.asString() == this } - ?.let { JvmPrimitiveType.get(it) } - -private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames -private fun DRI.possiblyAsJava() = this.partialFqName().mapToJava()?.toDRI(this) ?: this -private fun TypeConstructor.possiblyAsJava(): TypeConstructor = when (this) { - is GenericTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) - is FunctionalTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) -} + internal fun DAnnotation.asJava(): DAnnotation = copy( + properties = properties.map { it.asJava() }, + constructors = emptyList(), + classlikes = classlikes.map { it.asJava() } + ) // TODO investigate if annotation class can have methods and properties not from constructor -private fun String.mapToJava(): ClassId? = - JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) - -internal fun ClassId.toDRI(dri: DRI?): DRI = DRI( - packageName = packageFqName.asString(), - classNames = classNames(), - callable = dri?.callable,//?.asJava(), TODO: check this - extra = null, - target = PointingToDeclaration -) - -internal fun TypeConstructorWithKind.asJava(): TypeConstructorWithKind = - TypeConstructorWithKind( - typeConstructor = typeConstructor.possiblyAsJava(), - kind = kind.asJava() + internal fun DParameter.asJava(): DParameter = copy( + type = type.asJava(), + name = if (name.isNullOrBlank()) "\$self" else name ) -internal fun ClassKind.asJava(): ClassKind { - return when (this) { - is JavaClassKindTypes -> this - KotlinClassKindTypes.CLASS -> JavaClassKindTypes.CLASS - KotlinClassKindTypes.INTERFACE -> JavaClassKindTypes.INTERFACE - KotlinClassKindTypes.ENUM_CLASS -> JavaClassKindTypes.ENUM_CLASS - KotlinClassKindTypes.ENUM_ENTRY -> JavaClassKindTypes.ENUM_ENTRY - KotlinClassKindTypes.ANNOTATION_CLASS -> JavaClassKindTypes.ANNOTATION_CLASS - KotlinClassKindTypes.OBJECT -> JavaClassKindTypes.CLASS - else -> throw IllegalStateException("Non exchaustive match while trying to convert $this to Java") + internal fun Visibility.propertyVisibilityAsJava(): Visibility = + if (this is JavaVisibility) this + else JavaVisibility.Private + + private fun TypeConstructor.possiblyAsJava(): TypeConstructor = when (this) { + is GenericTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) + is FunctionalTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) } -} -private fun <T : Documentable> PropertyContainer<T>.mergeAdditionalModifiers(second: SourceSetDependent<Set<ExtraModifiers>>) = - this[AdditionalModifiers]?.squash(AdditionalModifiers(second)) ?: AdditionalModifiers(second) -private fun AdditionalModifiers.squash(second: AdditionalModifiers) = - AdditionalModifiers(content + second.content) + internal fun TypeConstructorWithKind.asJava(): TypeConstructorWithKind = + TypeConstructorWithKind( + typeConstructor = typeConstructor.possiblyAsJava(), + kind = kind.asJava() + ) -internal fun ClassId.classNames(): String = - shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") + internal fun ClassKind.asJava(): ClassKind { + return when (this) { + is JavaClassKindTypes -> this + KotlinClassKindTypes.CLASS -> JavaClassKindTypes.CLASS + KotlinClassKindTypes.INTERFACE -> JavaClassKindTypes.INTERFACE + KotlinClassKindTypes.ENUM_CLASS -> JavaClassKindTypes.ENUM_CLASS + KotlinClassKindTypes.ENUM_ENTRY -> JavaClassKindTypes.ENUM_ENTRY + KotlinClassKindTypes.ANNOTATION_CLASS -> JavaClassKindTypes.ANNOTATION_CLASS + KotlinClassKindTypes.OBJECT -> JavaClassKindTypes.CLASS + else -> throw IllegalStateException("Non exchaustive match while trying to convert $this to Java") + } + } -private fun DProperty.hasModifier(modifier: ExtraModifiers.KotlinOnlyModifiers): Boolean = - extra[AdditionalModifiers] - ?.content - ?.any { (_, modifiers) -> modifier in modifiers } == true + private fun <T : Documentable> PropertyContainer<T>.mergeAdditionalModifiers(second: SourceSetDependent<Set<ExtraModifiers>>) = + this[AdditionalModifiers]?.squash(AdditionalModifiers(second)) ?: AdditionalModifiers(second) + + private fun AdditionalModifiers.squash(second: AdditionalModifiers) = + AdditionalModifiers(content + second.content) + + internal fun DObject.companionAsJava(): DObject? { + if (hasNothingToRender()) return null + + return asJava( + excludedProps = staticPropertiesForJava(), + excludedFunctions = staticFunctionsForJava() + ) + } + + private fun DRI.possiblyAsJava(): DRI { + return kotlinToJavaMapper.findAsJava(this) ?: this + } +} diff --git a/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt b/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt index fea78abb..8dc7bdb1 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt @@ -3,10 +3,10 @@ package org.jetbrains.dokka.kotlinAsJava import org.jetbrains.dokka.model.Annotations import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.kotlin.util.firstNotNullResult internal fun <T : Documentable> WithExtraProperties<T>.jvmField(): Annotations.Annotation? = - extra[Annotations]?.directAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmFieldAnnotation() } + extra[Annotations]?.directAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations) -> annotations.jvmFieldAnnotation() } internal fun List<Annotations.Annotation>.jvmFieldAnnotation(): Annotations.Annotation? = firstOrNull { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmField" } + diff --git a/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt b/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt index 600318e5..8eed96e0 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt @@ -5,15 +5,15 @@ import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.StringValue import org.jetbrains.dokka.model.isJvmName import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.kotlin.util.firstNotNullResult internal fun <T : Documentable> WithExtraProperties<T>.directlyAnnotatedJvmName(): Annotations.Annotation? = - extra[Annotations]?.directAnnotations?.entries?.firstNotNullResult { (_, annotations)-> annotations.jvmNameAnnotation() } + extra[Annotations]?.directAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations)-> annotations.jvmNameAnnotation() } internal fun <T : Documentable> WithExtraProperties<T>.fileLevelJvmName(): Annotations.Annotation? = - extra[Annotations]?.fileLevelAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmNameAnnotation() } + extra[Annotations]?.fileLevelAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations) -> annotations.jvmNameAnnotation() } internal fun List<Annotations.Annotation>.jvmNameAnnotation(): Annotations.Annotation? = firstOrNull { it.isJvmName() } internal fun Annotations.Annotation.jvmNameAsString(): String? = (params["name"] as? StringValue)?.value + diff --git a/plugins/kotlin-as-java/src/main/kotlin/jvmStatic.kt b/plugins/kotlin-as-java/src/main/kotlin/jvmStatic.kt index 50ea7795..82aac734 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/jvmStatic.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/jvmStatic.kt @@ -3,10 +3,10 @@ package org.jetbrains.dokka.kotlinAsJava import org.jetbrains.dokka.model.Annotations import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.kotlin.util.firstNotNullResult internal fun <T : Documentable> WithExtraProperties<T>.jvmStatic(): Annotations.Annotation? = - extra[Annotations]?.directAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmStaticAnnotation() } + extra[Annotations]?.directAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations) -> annotations.jvmStaticAnnotation() } internal fun List<Annotations.Annotation>.jvmStaticAnnotation(): Annotations.Annotation? = firstOrNull { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmStatic" } + diff --git a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt index 05442b54..e51b31cb 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt @@ -8,7 +8,10 @@ import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.TextStyle +import org.jetbrains.dokka.pages.TokenStyle import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformToJava.kt b/plugins/kotlin-as-java/src/main/kotlin/transformToJava.kt index 69df21ee..809f8a3c 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/transformToJava.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/transformToJava.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.kotlinAsJava -import org.jetbrains.dokka.kotlinAsJava.converters.asJava +import org.jetbrains.dokka.kotlinAsJava.converters.KotlinToJavaConverter import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameDocumentableTransformer import org.jetbrains.dokka.model.DClasslike import org.jetbrains.dokka.model.DFunction @@ -13,17 +13,26 @@ private val JVM_NAME_DOCUMENTABLE_TRANSFORMER by lazy { } fun DPackage.transformToJava(context: DokkaContext): DPackage { - return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this.asJava(), context) + with(KotlinToJavaConverter(context)) { + return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this@transformToJava.asJava(), context) + } } fun DClasslike.transformToJava(context: DokkaContext): DClasslike { - return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this.asJava(), context) + with(KotlinToJavaConverter(context)) { + return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this@transformToJava.asJava(), context) + } } fun DFunction.transformToJava(context: DokkaContext, containingClassName: String, isTopLevel: Boolean = false): List<DFunction> { - return this.asJava(containingClassName, isTopLevel).map { JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(it, context) } + with(KotlinToJavaConverter(context)) { + return this@transformToJava.asJava(containingClassName, isTopLevel) + .map { JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(it, context) } + } } fun DProperty.transformToJava(context: DokkaContext, isTopLevel: Boolean = false, relocateToClass: String? = null): DProperty { - return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this.asJava(isTopLevel, relocateToClass), context) + with(KotlinToJavaConverter(context)) { + return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this@transformToJava.asJava(isTopLevel, relocateToClass), context) + } } diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt index 8b07670f..20bc420c 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt @@ -1,11 +1,15 @@ package org.jetbrains.dokka.kotlinAsJava.transformers -import org.jetbrains.dokka.kotlinAsJava.converters.asJava +import org.jetbrains.dokka.kotlinAsJava.converters.KotlinToJavaConverter import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer class KotlinAsJavaDocumentableTransformer : DocumentableTransformer { override fun invoke(original: DModule, context: DokkaContext): DModule = - original.copy(packages = original.packages.map { it.asJava() }) + original.copy(packages = original.packages.map { + with(KotlinToJavaConverter(context)) { + it.asJava() + } + }) } diff --git a/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaDocumentableToPageTranslator.kt b/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaDocumentableToPageTranslator.kt index 80c3a3de..e33b8420 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaDocumentableToPageTranslator.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaDocumentableToPageTranslator.kt @@ -4,19 +4,17 @@ import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.configuration -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.query -import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.plugability.* import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin class KotlinAsJavaDocumentableToPageTranslator(context: DokkaContext) : DocumentableToPageTranslator { private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(context) private val commentsToContentConverter = context.plugin<DokkaBase>().querySingle { commentsToContentConverter } private val signatureProvider = context.plugin<DokkaBase>().querySingle { signatureProvider } private val customTagContentProviders = context.plugin<DokkaBase>().query { customTagContentProvider } + private val documentableSourceLanguageParser = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { documentableSourceLanguageParser } private val logger: DokkaLogger = context.logger override fun invoke(module: DModule): ModulePageNode = @@ -25,6 +23,7 @@ class KotlinAsJavaDocumentableToPageTranslator(context: DokkaContext) : Document commentsToContentConverter, signatureProvider, logger, - customTagContentProviders + customTagContentProviders, + documentableSourceLanguageParser ).pageForModule(module) -}
\ No newline at end of file +} diff --git a/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaPageCreator.kt b/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaPageCreator.kt index ad3c8b0e..e03b5fe2 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaPageCreator.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/translators/KotlinAsJavaPageCreator.kt @@ -3,24 +3,27 @@ package org.jetbrains.dokka.kotlinAsJava.translators import org.jetbrains.dokka.base.DokkaBaseConfiguration import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.base.translators.documentables.DefaultPageCreator import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider +import org.jetbrains.dokka.base.translators.documentables.DefaultPageCreator import org.jetbrains.dokka.model.DProperty import org.jetbrains.dokka.pages.MemberPageNode import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.analysis.kotlin.internal.DocumentableSourceLanguageParser class KotlinAsJavaPageCreator( configuration: DokkaBaseConfiguration?, commentsToContentConverter: CommentsToContentConverter, signatureProvider: SignatureProvider, logger: DokkaLogger, - customTagContentProviders: List<CustomTagContentProvider> + customTagContentProviders: List<CustomTagContentProvider>, + documentableAnalyzer: DocumentableSourceLanguageParser ) : DefaultPageCreator( configuration, commentsToContentConverter, signatureProvider, logger, - customTagContentProviders = customTagContentProviders + customTagContentProviders = customTagContentProviders, + documentableAnalyzer = documentableAnalyzer ) { override fun pageForProperty(p: DProperty): MemberPageNode? = null -}
\ No newline at end of file +} diff --git a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt index 031f5ee8..ab11120a 100644 --- a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt +++ b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt @@ -12,8 +12,13 @@ import org.jetbrains.dokka.pages.* import org.junit.Assert import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import signatures.* -import utils.* +import signatures.Parameter +import signatures.Parameters +import signatures.firstSignature +import signatures.renderedContent +import utils.A +import utils.TestOutputWriterPlugin +import utils.match import kotlin.test.assertEquals import kotlin.test.assertNotNull diff --git a/plugins/mathjax/build.gradle.kts b/plugins/mathjax/build.gradle.kts index 8b155862..4c572450 100644 --- a/plugins/mathjax/build.gradle.kts +++ b/plugins/mathjax/build.gradle.kts @@ -7,15 +7,15 @@ plugins { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) + implementation(projects.plugins.base) + implementation(kotlin("reflect")) + testImplementation(libs.jsoup) testImplementation(projects.plugins.base.baseTestUtils) testImplementation(projects.core.contentMatcherTestUtils) testImplementation(kotlin("test-junit")) - testImplementation(projects.kotlinAnalysis) - testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) diff --git a/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt b/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt index 0f713708..c603e588 100644 --- a/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt +++ b/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt @@ -1,8 +1,8 @@ package mathjaxTest +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.mathjax.LIB_PATH import org.jetbrains.dokka.mathjax.MathjaxPlugin -import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jsoup.Jsoup import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin diff --git a/plugins/templating/build.gradle.kts b/plugins/templating/build.gradle.kts index 1111bfad..333daf22 100644 --- a/plugins/templating/build.gradle.kts +++ b/plugins/templating/build.gradle.kts @@ -12,23 +12,16 @@ registerDokkaArtifactPublication("templating-plugin") { dependencies { compileOnly(projects.core) + api(libs.jsoup) - implementation(kotlin("reflect")) implementation(projects.plugins.base) + implementation(kotlin("reflect")) implementation(libs.kotlinx.coroutines.core) - implementation(libs.jackson.kotlin) - constraints { - implementation(libs.jackson.databind) { - because("CVE-2022-42003") - } - } - implementation(libs.kotlinx.html) - implementation(libs.jsoup) testImplementation(projects.plugins.base.baseTestUtils) - testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) + testImplementation(libs.kotlinx.html) } diff --git a/plugins/versioning/build.gradle.kts b/plugins/versioning/build.gradle.kts index 370338a8..68db9d1c 100644 --- a/plugins/versioning/build.gradle.kts +++ b/plugins/versioning/build.gradle.kts @@ -11,22 +11,20 @@ registerDokkaArtifactPublication("versioning-plugin") { dependencies { compileOnly(projects.core) - implementation(kotlin("reflect")) + implementation(projects.plugins.base) implementation(projects.plugins.templating) - implementation(projects.plugins.templating) + implementation(kotlin("reflect")) implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.html) + implementation(libs.apacheMaven.artifact) implementation(libs.jackson.kotlin) constraints { implementation(libs.jackson.databind) { because("CVE-2022-42003") } } - implementation(libs.kotlinx.html) - - implementation(libs.jsoup) - implementation(libs.apacheMaven.artifact) testImplementation(projects.core.testApi) testImplementation(platform(libs.junit.bom)) diff --git a/plugins/versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt b/plugins/versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt index 6ab81d31..43abdb9c 100644 --- a/plugins/versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt +++ b/plugins/versioning/src/main/kotlin/org/jetbrains/dokka/versioning/DefaultPreviousDocumentationCopyPostAction.kt @@ -4,11 +4,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import org.jetbrains.dokka.renderers.PostAction 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.renderers.PostAction import org.jetbrains.dokka.templates.TemplateProcessingStrategy import org.jetbrains.dokka.templates.TemplatingPlugin import java.io.File @@ -51,4 +51,4 @@ class DefaultPreviousDocumentationCopyPostAction(private val context: DokkaConte versionRootContent.copyTo(targetParent.resolve(versionRootContent.name), overwrite = true) } } -}
\ No newline at end of file +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt index 184e61bb..2975749f 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt @@ -10,7 +10,9 @@ internal class DokkaArtifacts(private val project: Project) { private fun fromModuleName(name: String): Dependency = project.dependencies.create("org.jetbrains.dokka:$name:${DokkaVersion.version}") - val dokkaAnalysis get() = fromModuleName("dokka-analysis") + // TODO [beresnev] analysis switcher + val analysisKotlinDescriptors get() = fromModuleName("analysis-kotlin-descriptors") + val allModulesPage get() = fromModuleName("all-modules-page-plugin") val dokkaCore get() = fromModuleName("dokka-core") val dokkaBase get() = fromModuleName("dokka-base") diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt index e27fee30..46712c81 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt @@ -393,7 +393,7 @@ open class GradleDokkaSourceSetBuilder( * * @see [GradleSourceLinkBuilder] for details. */ - @Suppress("DEPRECATION") // TODO [beresnev] ConfigureUtil will be removed in Gradle 8 + @Suppress("DEPRECATION") fun sourceLink(c: Closure<in GradleSourceLinkBuilder>) { val configured = org.gradle.util.ConfigureUtil.configure(c, GradleSourceLinkBuilder(project)) sourceLinks.add(configured) @@ -415,7 +415,7 @@ open class GradleDokkaSourceSetBuilder( * * @see [GradlePackageOptionsBuilder] for details. */ - @Suppress("DEPRECATION") // TODO [beresnev] ConfigureUtil will be removed in Gradle 8 + @Suppress("DEPRECATION") fun perPackageOption(c: Closure<in GradlePackageOptionsBuilder>) { val configured = org.gradle.util.ConfigureUtil.configure(c, GradlePackageOptionsBuilder(project)) perPackageOptions.add(configured) @@ -437,7 +437,7 @@ open class GradleDokkaSourceSetBuilder( * * @see [GradleExternalDocumentationLinkBuilder] for details. */ - @Suppress("DEPRECATION") // TODO [beresnev] ConfigureUtil will be removed in Gradle 8 + @Suppress("DEPRECATION") fun externalDocumentationLink(c: Closure<in GradleExternalDocumentationLinkBuilder>) { val link = org.gradle.util.ConfigureUtil.configure(c, GradleExternalDocumentationLinkBuilder(project)) externalDocumentationLinks.add(link) diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/gradleConfigurations.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/gradleConfigurations.kt index 86dd3716..a3f8af2d 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/gradleConfigurations.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/gradleConfigurations.kt @@ -25,7 +25,7 @@ internal fun Project.maybeCreateDokkaPluginConfiguration(dokkaTaskName: String, extendsFrom(maybeCreateDokkaDefaultPluginConfiguration()) attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME)) isCanBeConsumed = false - dependencies.add(project.dokkaArtifacts.dokkaAnalysis) // compileOnly in base plugin + dependencies.add(project.dokkaArtifacts.analysisKotlinDescriptors) dependencies.add(project.dokkaArtifacts.dokkaBase) dependencies.addAll(additionalDependencies) } diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/ConfigureWithKotlinSourceSetGistTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/ConfigureWithKotlinSourceSetGistTest.kt index c1e0c85b..9594887c 100644 --- a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/ConfigureWithKotlinSourceSetGistTest.kt +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/ConfigureWithKotlinSourceSetGistTest.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import kotlin.test.Test import kotlin.test.assertEquals +import org.jetbrains.dokka.gradle.utils.withDependencies_ class ConfigureWithKotlinSourceSetGistTest { @Test @@ -96,7 +97,7 @@ class ConfigureWithKotlinSourceSetGistTest { /* Make sure to remove dependencies that cannot be resolved during test */ project.configurations.configureEach { - withDependencies { + withDependencies_ { removeIf { dependency -> dependency !is FileCollectionDependency } } } diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGistTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGistTest.kt index c40b5811..713109bf 100644 --- a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGistTest.kt +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGistTest.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.gradle +import org.jetbrains.dokka.gradle.utils.withDependencies_ import org.gradle.api.artifacts.FileCollectionDependency import org.gradle.kotlin.dsl.get import org.gradle.testfixtures.ProjectBuilder @@ -117,7 +118,7 @@ class KotlinSourceSetGistTest { /* Only work with file dependencies */ project.configurations.forEach { configuration -> - configuration.withDependencies { + configuration.withDependencies_ { removeIf { dependency -> dependency !is FileCollectionDependency } diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt index 64293332..8d790729 100644 --- a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt +++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt @@ -426,7 +426,8 @@ abstract class AbstractDokkaMojo(private val defaultDokkaPlugins: List<Dependenc offlineMode = offlineMode, cacheRoot = cacheRoot?.let(::File), sourceSets = listOf(sourceSet), - pluginsClasspath = getArtifactByMaven("org.jetbrains.dokka", "dokka-analysis", dokkaVersion) + // compileOnly in base plugin + // TODO [beresnev] analysis switcher + pluginsClasspath = getArtifactByMaven("org.jetbrains.dokka", "analysis-kotlin-descriptors", dokkaVersion) + getArtifactByMaven("org.jetbrains.dokka", "dokka-base", dokkaVersion) + dokkaPlugins.map { getArtifactByMaven(it.groupId, it.artifactId, it.version ?: dokkaVersion) } .flatten(), diff --git a/settings.gradle.kts b/settings.gradle.kts index a1229458..6e67f58c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,15 +12,16 @@ pluginManagement { } dependencyResolutionManagement { - - // subproject :kotlin-analysis:intellij-dependency requires specific repositories that should not be used in - // the other subprojects, so use PREFER_PROJECT to allow subprojects to override the repositories defined here. - repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) - repositories { mavenCentral() google() + maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide") + maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide-plugin-dependencies") + + maven("https://cache-redirector.jetbrains.com/intellij-repository/releases") + maven("https://cache-redirector.jetbrains.com/intellij-third-party-dependencies") + // Declare the Node.js & Yarn download repositories // Required by Gradle Node plugin: https://github.com/node-gradle/gradle-node-plugin/blob/3.5.1/docs/faq.md#is-this-plugin-compatible-with-centralized-repositories-declaration exclusiveContent { @@ -58,9 +59,17 @@ include( ":core:test-api", ":core:content-matcher-test-utils", - ":kotlin-analysis", - ":kotlin-analysis:intellij-dependency", - ":kotlin-analysis:compiler-dependency", + ":subprojects", + + ":subprojects:analysis-java-psi", + ":subprojects:analysis-kotlin-api", + ":subprojects:analysis-kotlin-descriptors", + ":subprojects:analysis-kotlin-descriptors:compiler", + ":subprojects:analysis-kotlin-descriptors:ide", + ":subprojects:analysis-kotlin-symbols", + ":subprojects:analysis-kotlin-symbols:compiler", + ":subprojects:analysis-kotlin-symbols:ide", + ":subprojects:analysis-markdown-jb", ":runners:gradle-plugin", ":runners:cli", diff --git a/subprojects/analysis-java-psi/README.md b/subprojects/analysis-java-psi/README.md new file mode 100644 index 00000000..d2bbd080 --- /dev/null +++ b/subprojects/analysis-java-psi/README.md @@ -0,0 +1,5 @@ +# Analysis: Java PSI + +An internal module for parsing Java sources. Defines no stable public API and is not published anywhere. + +Used by the Kotlin analysis artifacts to provide support for mixed-language (Kotlin+Java) projects. diff --git a/subprojects/analysis-java-psi/api/analysis-java-psi.api b/subprojects/analysis-java-psi/api/analysis-java-psi.api new file mode 100644 index 00000000..404249f8 --- /dev/null +++ b/subprojects/analysis-java-psi/api/analysis-java-psi.api @@ -0,0 +1,148 @@ +public final class org/jetbrains/dokka/analysis/java/AuthorJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field INSTANCE Lorg/jetbrains/dokka/analysis/java/AuthorJavadocTag; +} + +public abstract interface class org/jetbrains/dokka/analysis/java/BreakingAbstractionKotlinLightMethodChecker { + public abstract fun isLightAnnotation (Lcom/intellij/psi/PsiAnnotation;)Z + public abstract fun isLightAnnotationAttribute (Lcom/intellij/lang/jvm/annotation/JvmAnnotationAttribute;)Z +} + +public final class org/jetbrains/dokka/analysis/java/DeprecatedJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field INSTANCE Lorg/jetbrains/dokka/analysis/java/DeprecatedJavadocTag; +} + +public final class org/jetbrains/dokka/analysis/java/DescriptionJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field INSTANCE Lorg/jetbrains/dokka/analysis/java/DescriptionJavadocTag; +} + +public final class org/jetbrains/dokka/analysis/java/ExceptionJavadocTag : org/jetbrains/dokka/analysis/java/ThrowingExceptionJavadocTag { + public static final field Companion Lorg/jetbrains/dokka/analysis/java/ExceptionJavadocTag$Companion; + public static final field name Ljava/lang/String; + public fun <init> (Ljava/lang/String;)V +} + +public final class org/jetbrains/dokka/analysis/java/ExceptionJavadocTag$Companion { +} + +public final class org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V + public final fun getDocCommentCreators ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getDocCommentFinder ()Lorg/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder; + public final fun getDocCommentParsers ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getInheritDocTagContentProviders ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKotlinLightMethodChecker ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getProjectProvider ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getSourceRootsExtractor ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; +} + +public abstract class org/jetbrains/dokka/analysis/java/JavadocTag { + public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getName ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/java/ParamJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field Companion Lorg/jetbrains/dokka/analysis/java/ParamJavadocTag$Companion; + public static final field name Ljava/lang/String; + public fun <init> (Lcom/intellij/psi/PsiMethod;Ljava/lang/String;I)V + public final fun getMethod ()Lcom/intellij/psi/PsiMethod; + public final fun getParamIndex ()I + public final fun getParamName ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/java/ParamJavadocTag$Companion { +} + +public abstract interface class org/jetbrains/dokka/analysis/java/ProjectProvider { + public abstract fun getProject (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lcom/intellij/openapi/project/Project; +} + +public final class org/jetbrains/dokka/analysis/java/ReturnJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field INSTANCE Lorg/jetbrains/dokka/analysis/java/ReturnJavadocTag; +} + +public final class org/jetbrains/dokka/analysis/java/SeeJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field Companion Lorg/jetbrains/dokka/analysis/java/SeeJavadocTag$Companion; + public static final field name Ljava/lang/String; + public fun <init> (Ljava/lang/String;)V + public final fun getQualifiedReference ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/java/SeeJavadocTag$Companion { +} + +public final class org/jetbrains/dokka/analysis/java/SinceJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public static final field INSTANCE Lorg/jetbrains/dokka/analysis/java/SinceJavadocTag; +} + +public abstract interface class org/jetbrains/dokka/analysis/java/SourceRootsExtractor { + public abstract fun extract (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Lorg/jetbrains/dokka/plugability/DokkaContext;)Ljava/util/List; +} + +public abstract class org/jetbrains/dokka/analysis/java/ThrowingExceptionJavadocTag : org/jetbrains/dokka/analysis/java/JavadocTag { + public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getExceptionQualifiedName ()Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/java/ThrowsJavadocTag : org/jetbrains/dokka/analysis/java/ThrowingExceptionJavadocTag { + public static final field Companion Lorg/jetbrains/dokka/analysis/java/ThrowsJavadocTag$Companion; + public static final field name Ljava/lang/String; + public fun <init> (Ljava/lang/String;)V +} + +public final class org/jetbrains/dokka/analysis/java/ThrowsJavadocTag$Companion { +} + +public abstract interface class org/jetbrains/dokka/analysis/java/doccomment/DocComment { + public abstract fun hasTag (Lorg/jetbrains/dokka/analysis/java/JavadocTag;)Z + public abstract fun resolveTag (Lorg/jetbrains/dokka/analysis/java/JavadocTag;)Ljava/util/List; +} + +public abstract interface class org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator { + public abstract fun create (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/analysis/java/doccomment/DocComment; +} + +public final class org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory { + public fun <init> (Ljava/util/List;)V + public final fun fromElement (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/analysis/java/doccomment/DocComment; +} + +public final class org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder { + public fun <init> (Lorg/jetbrains/dokka/utilities/DokkaLogger;Lorg/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory;)V + public final fun findClosestToElement (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/analysis/java/doccomment/DocComment; +} + +public abstract interface class org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent { + public abstract fun getTag ()Lorg/jetbrains/dokka/analysis/java/JavadocTag; + public abstract fun resolveSiblings ()Ljava/util/List; +} + +public abstract interface class org/jetbrains/dokka/analysis/java/parsers/DocCommentParser { + public abstract fun canParse (Lorg/jetbrains/dokka/analysis/java/doccomment/DocComment;)Z + public abstract fun parse (Lorg/jetbrains/dokka/analysis/java/doccomment/DocComment;Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; +} + +public final class org/jetbrains/dokka/analysis/java/parsers/JavadocParser : org/jetbrains/dokka/analysis/java/parsers/JavaDocumentationParser { + public fun <init> (Ljava/util/List;Lorg/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder;)V + public fun parseDocumentation (Lcom/intellij/psi/PsiNamedElement;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; +} + +public final class org/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext { + public fun <init> ()V + public final fun getDocumentationNode (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; + public final fun getDri (Ljava/lang/String;)Lorg/jetbrains/dokka/links/DRI; + public final fun store (Lorg/jetbrains/dokka/links/DRI;)Ljava/lang/String; + public final fun store (Lorg/jetbrains/dokka/model/doc/DocumentationNode;)Ljava/lang/String; +} + +public abstract interface class org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagContentProvider { + public abstract fun canConvert (Lorg/jetbrains/dokka/analysis/java/doccomment/DocumentationContent;)Z + public abstract fun convertToHtml (Lorg/jetbrains/dokka/analysis/java/doccomment/DocumentationContent;Lorg/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext;)Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/java/util/PsiDocumentableSource : org/jetbrains/dokka/model/DocumentableSource { + public fun <init> (Lcom/intellij/psi/PsiNamedElement;)V + public fun computeLineNumber ()Ljava/lang/Integer; + public fun getPath ()Ljava/lang/String; + public final fun getPsi ()Lcom/intellij/psi/PsiNamedElement; +} + diff --git a/subprojects/analysis-java-psi/build.gradle.kts b/subprojects/analysis-java-psi/build.gradle.kts new file mode 100644 index 00000000..88d043ee --- /dev/null +++ b/subprojects/analysis-java-psi/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("org.jetbrains.conventions.kotlin-jvm") +} + +dependencies { + compileOnly(projects.core) + + api(libs.intellij.java.psi.api) + + implementation(projects.subprojects.analysisMarkdownJb) + + implementation(libs.intellij.java.psi.impl) + implementation(libs.intellij.platform.util.api) + implementation(libs.intellij.platform.util.rt) + + implementation(libs.kotlinx.coroutines.core) + implementation(libs.jsoup) +} 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 new file mode 100644 index 00000000..72cf45d9 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/DefaultPsiToDocumentableTranslator.kt @@ -0,0 +1,83 @@ +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 new file mode 100644 index 00000000..8884d444 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavaAnalysisPlugin.kt @@ -0,0 +1,106 @@ +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 +interface ProjectProvider { + fun getProject(sourceSet: DokkaSourceSet, context: DokkaContext): Project +} + +@InternalDokkaApi +interface SourceRootsExtractor { + fun extract(sourceSet: DokkaSourceSet, context: DokkaContext): List<File> +} + +@InternalDokkaApi +interface BreakingAbstractionKotlinLightMethodChecker { + // TODO [beresnev] not even sure it's needed, but left for compatibility and to preserve behaviour + fun isLightAnnotation(annotation: PsiAnnotation): Boolean + fun isLightAnnotationAttribute(attribute: JvmAnnotationAttribute): Boolean +} + +@InternalDokkaApi +class JavaAnalysisPlugin : DokkaPlugin() { + + // single + val projectProvider by extensionPoint<ProjectProvider>() + + // single + val sourceRootsExtractor by extensionPoint<SourceRootsExtractor>() + + // multiple + val docCommentCreators by extensionPoint<DocCommentCreator>() + + // multiple + val docCommentParsers by extensionPoint<DocCommentParser>() + + // none or more + val inheritDocTagContentProviders by extensionPoint<InheritDocTagContentProvider>() + + // TODO [beresnev] figure out a better way depending on what it's used for + val kotlinLightMethodChecker by extensionPoint<BreakingAbstractionKotlinLightMethodChecker>() + + private val docCommentFactory by lazy { + DocCommentFactory(query { docCommentCreators }.reversed()) + } + + val 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 new file mode 100644 index 00000000..f5cd550f --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/JavadocTag.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka.analysis.java + +import com.intellij.psi.PsiMethod +import org.jetbrains.dokka.InternalDokkaApi + +@InternalDokkaApi +sealed class JavadocTag(val name: String) + +object AuthorJavadocTag : JavadocTag("author") +object DeprecatedJavadocTag : JavadocTag("deprecated") +object DescriptionJavadocTag : JavadocTag("description") +object ReturnJavadocTag : JavadocTag("return") +object SinceJavadocTag : JavadocTag("since") + +class ParamJavadocTag( + val method: PsiMethod, + val paramName: String, + val paramIndex: Int +) : JavadocTag(name) { + companion object { + const val name: String = "param" + } +} + +class SeeJavadocTag( + val qualifiedReference: String +) : JavadocTag(name) { + companion object { + const val name: String = "see" + } +} + +sealed class ThrowingExceptionJavadocTag( + name: String, + val exceptionQualifiedName: String? +) : JavadocTag(name) + +class ThrowsJavadocTag(exceptionQualifiedName: String?) : ThrowingExceptionJavadocTag(name, exceptionQualifiedName) { + companion object { + const val name: String = "throws" + } +} + +class ExceptionJavadocTag(exceptionQualifiedName: String?) : ThrowingExceptionJavadocTag(name, exceptionQualifiedName) { + companion object { + const val name: String = "exception" + } +} diff --git a/plugins/base/src/main/kotlin/translators/psi/SynheticElementDocumentationProvider.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt index 376c0940..d780bb40 100644 --- a/plugins/base/src/main/kotlin/translators/psi/SynheticElementDocumentationProvider.kt +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/SynheticElementDocumentationProvider.kt @@ -1,17 +1,20 @@ -package org.jetbrains.dokka.base.translators.psi +package org.jetbrains.dokka.analysis.java -import com.intellij.psi.* +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.DokkaResolutionFacade -import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser +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: JavadocParser, - private val resolutionFacade: DokkaResolutionFacade + private val javadocParser: JavaPsiDocCommentParser, + private val project: Project ) { fun isDocumented(psiElement: PsiElement): Boolean = psiElement is PsiMethod && (psiElement.isSyntheticEnumValuesMethod() || psiElement.isSyntheticEnumValueOfMethod()) @@ -24,12 +27,12 @@ internal class SyntheticElementDocumentationProvider( else -> return null } val docComment = loadSyntheticDoc(templatePath) ?: return null - return javadocParser.parseDocComment(docComment, psiElement) + return javadocParser.parsePsiDocComment(docComment, psiElement) } private fun loadSyntheticDoc(path: String): PsiDocComment? { val text = javaClass.getResource(path)?.readText() ?: return null - return JavaPsiFacade.getElementFactory(resolutionFacade.project).createDocCommentFromText(text) + return JavaPsiFacade.getElementFactory(project).createDocCommentFromText(text) } } 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 new file mode 100644 index 00000000..6cc32233 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocComment.kt @@ -0,0 +1,14 @@ +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 +interface DocComment { + fun hasTag(tag: JavadocTag): Boolean + + 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 new file mode 100644 index 00000000..3d7d4247 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentCreator.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.analysis.java.doccomment + +import com.intellij.psi.PsiNamedElement +import org.jetbrains.dokka.InternalDokkaApi + +@InternalDokkaApi +interface DocCommentCreator { + 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 new file mode 100644 index 00000000..96245ac2 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFactory.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.analysis.java.doccomment + +import com.intellij.psi.PsiNamedElement +import org.jetbrains.dokka.InternalDokkaApi + +@InternalDokkaApi +class DocCommentFactory( + private val docCommentCreators: List<DocCommentCreator> +) { + 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 new file mode 100644 index 00000000..32c8dc65 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocCommentFinder.kt @@ -0,0 +1,64 @@ +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 +class DocCommentFinder( + private val logger: DokkaLogger, + private val docCommentFactory: DocCommentFactory, +) { + 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 new file mode 100644 index 00000000..c06ed496 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/DocumentationContent.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka.analysis.java.doccomment + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.analysis.java.JavadocTag + +@InternalDokkaApi +interface DocumentationContent { + val tag: JavadocTag + + 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 new file mode 100644 index 00000000..5c9be887 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocComment.kt @@ -0,0 +1,84 @@ +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 new file mode 100644 index 00000000..00efeb0a --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/JavaDocCommentCreator.kt @@ -0,0 +1,11 @@ +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 new file mode 100644 index 00000000..c36ce50d --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/doccomment/PsiDocumentationContent.kt @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000..1e193648 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/CommentResolutionContext.kt @@ -0,0 +1,9 @@ +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 new file mode 100644 index 00000000..3f691799 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DocCommentParser.kt @@ -0,0 +1,12 @@ +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 +interface DocCommentParser { + fun canParse(docComment: DocComment): Boolean + 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 new file mode 100644 index 00000000..45f44338 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/DokkaPsiParser.kt @@ -0,0 +1,797 @@ +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.extendsBound != PsiType.NULL -> Covariance(getBound(type.extendsBound)) + type.superBound != PsiType.NULL -> Contravariance(getBound(type.superBound)) + 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 new file mode 100644 index 00000000..9c475054 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavaDocCommentParser.kt @@ -0,0 +1,228 @@ +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 new file mode 100644 index 00000000..b8eba878 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/JavadocParser.kt @@ -0,0 +1,24 @@ +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 +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 new file mode 100644 index 00000000..050736f0 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/DocTagParserContext.kt @@ -0,0 +1,47 @@ +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 +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 + */ + fun store(dri: DRI): String { + val id = dri.toString() + driMap[id] = dri + return id + } + + /** + * @return key of the stored documentation node + */ + fun store(documentationNode: DocumentationNode): String { + val id = UUID.randomUUID().toString() + inheritDocSections[id] = documentationNode + return id + } + + fun getDri(id: String): DRI? = driMap[id] + + 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 new file mode 100644 index 00000000..ea1997b8 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/HtmlToDocTagConverter.kt @@ -0,0 +1,114 @@ +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 new file mode 100644 index 00000000..31149898 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagContentProvider.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka.analysis.java.parsers.doctag + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent + +@InternalDokkaApi +interface InheritDocTagContentProvider { + fun canConvert(content: DocumentationContent): Boolean + 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 new file mode 100644 index 00000000..031a7b32 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/InheritDocTagResolver.kt @@ -0,0 +1,114 @@ +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 new file mode 100644 index 00000000..803eabcb --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiDocTagParser.kt @@ -0,0 +1,39 @@ +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 new file mode 100644 index 00000000..0c20a9b7 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/parsers/doctag/PsiElementToHtmlConverter.kt @@ -0,0 +1,214 @@ +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 new file mode 100644 index 00000000..d8702336 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/CoreCopyPaste.kt @@ -0,0 +1,20 @@ +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/plugins/base/src/main/kotlin/utils/NoopIntellijLogger.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/NoopIntellijLogger.kt index 9248f996..82482e35 100644 --- a/plugins/base/src/main/kotlin/utils/NoopIntellijLogger.kt +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/NoopIntellijLogger.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.base.utils +package org.jetbrains.dokka.analysis.java.util import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.DefaultLogger 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 new file mode 100644 index 00000000..137f0792 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PropertiesConventionUtil.kt @@ -0,0 +1,101 @@ +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/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiAccessorConventionUtil.kt index 3c1cb2cf..1424244d 100644 --- a/plugins/base/src/main/kotlin/translators/psi/PsiAccessorConventionUtil.kt +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiAccessorConventionUtil.kt @@ -1,16 +1,11 @@ -package org.jetbrains.dokka.base.translators.psi +package org.jetbrains.dokka.analysis.java.util import com.intellij.psi.PsiField import com.intellij.psi.PsiMethod -import org.jetbrains.dokka.base.translators.firstNotNullOfOrNull +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 -import org.jetbrains.kotlin.load.java.JvmAbi -import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName -import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.DescriptorUtils internal data class PsiFunctionsHolder( @@ -38,6 +33,29 @@ internal fun splitFunctionsAndAccessors(fields: Array<PsiField>, methods: Array< 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 @@ -57,20 +75,6 @@ private fun removeNonAccessorsReturning( return nonAccessors } -internal fun PsiMethod.getPossiblePropertyNamesForFunction(): List<String> { - val jvmName = getAnnotation(DescriptorUtils.JVM_NAME.asString())?.findAttributeValue("name")?.text - return jvmName?.let { listOf(jvmName) } - ?: when { - JvmAbi.isGetterName(name) -> listOfNotNull( - propertyNameByGetMethodName(Name.identifier(name))?.asString() - ) - JvmAbi.isSetterName(name) -> { - propertyNamesBySetMethodName(Name.identifier(name)).map { it.asString() } - } - else -> listOf() - } -} - internal fun PsiMethod.isAccessorFor(field: PsiField): Boolean { return (this.isGetterFor(field) || this.isSetterFor(field)) && !field.getVisibility().isPublicAPI() @@ -85,7 +89,7 @@ internal fun PsiMethod.isSetterFor(field: PsiField): Boolean { return parameterList.getParameter(0)?.type == field.type && parameterList.getParametersCount() == 1 } -internal fun Visibility.isPublicAPI() = when(this) { +private fun Visibility.isPublicAPI() = when(this) { KotlinVisibility.Public, KotlinVisibility.Protected, JavaVisibility.Public, 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 new file mode 100644 index 00000000..10bb79c7 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiCommentsUtils.kt @@ -0,0 +1,49 @@ +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 new file mode 100644 index 00000000..ed58eb56 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/PsiUtil.kt @@ -0,0 +1,119 @@ +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 } + +internal fun DRI.Companion.from(psi: PsiElement) = 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 +class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource { + override val path = 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) + } +} + +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 new file mode 100644 index 00000000..cce76ce6 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/StdlibUtil.kt @@ -0,0 +1,33 @@ +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 new file mode 100644 index 00000000..2972c009 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/kotlin/org/jetbrains/dokka/analysis/java/util/resolveToGetDri.kt @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..51d36899 --- /dev/null +++ b/subprojects/analysis-java-psi/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin diff --git a/subprojects/analysis-kotlin-api/README.md b/subprojects/analysis-kotlin-api/README.md new file mode 100644 index 00000000..5b03b297 --- /dev/null +++ b/subprojects/analysis-kotlin-api/README.md @@ -0,0 +1,10 @@ +# Analysis: Kotlin API + +Public API for interacting with Kotlin analysis, regardless of implementation. Contains no business logic. + +Can be used to request additional information about Kotlin declarations. + +Has to be used as a `compileOnly` dependency as Dokka bundles it by default in all runners. + +The actual implementation (K1/K2/etc) will be resolved and bootstrapped during runtime, so the +user must not think about it. diff --git a/subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api b/subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api new file mode 100644 index 00000000..74bd7da9 --- /dev/null +++ b/subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api @@ -0,0 +1,70 @@ +public final class org/jetbrains/kotlin/analysis/kotlin/KotlinAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V +} + +public final class org/jetbrains/kotlin/analysis/kotlin/internal/DocumentableLanguage : java/lang/Enum { + public static final field JAVA Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableLanguage; + public static final field KOTLIN Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableLanguage; + public static fun valueOf (Ljava/lang/String;)Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableLanguage; + public static fun values ()[Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableLanguage; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser { + public abstract fun getLanguage (Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/kotlin/analysis/kotlin/internal/DocumentableLanguage; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/ExternalDocumentablesProvider { + public abstract fun findClasslike (Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/model/DClasslike; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/FullClassHierarchyBuilder { + public abstract fun build (Lorg/jetbrains/dokka/model/DModule;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder { + public abstract fun build (Ljava/util/Map;)Ljava/util/List; +} + +public final class org/jetbrains/kotlin/analysis/kotlin/internal/InheritanceNode { + public fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun <init> (Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lorg/jetbrains/dokka/links/DRI; + public final fun component2 ()Ljava/util/List; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Z + public final fun copy (Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;Z)Lorg/jetbrains/kotlin/analysis/kotlin/internal/InheritanceNode; + public static synthetic fun copy$default (Lorg/jetbrains/kotlin/analysis/kotlin/internal/InheritanceNode;Lorg/jetbrains/dokka/links/DRI;Ljava/util/List;Ljava/util/List;ZILjava/lang/Object;)Lorg/jetbrains/kotlin/analysis/kotlin/internal/InheritanceNode; + public fun equals (Ljava/lang/Object;)Z + public final fun getChildren ()Ljava/util/List; + public final fun getDri ()Lorg/jetbrains/dokka/links/DRI; + public final fun getInterfaces ()Ljava/util/List; + public fun hashCode ()I + public final fun isInterface ()Z + public fun toString ()Ljava/lang/String; +} + +public final class org/jetbrains/kotlin/analysis/kotlin/internal/InternalKotlinAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V + public final fun getDocumentableSourceLanguageParser ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getExternalDocumentablesProvider ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getFullClassHierarchyBuilder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getInheritanceBuilder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKotlinToJavaService ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getModuleAndPackageDocumentationReader ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getSyntheticDocumentableDetector ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/KotlinToJavaService { + public abstract fun findAsJava (Lorg/jetbrains/dokka/links/DRI;)Lorg/jetbrains/dokka/links/DRI; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/ModuleAndPackageDocumentationReader { + public abstract fun read (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaModuleDescription;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; + public abstract fun read (Lorg/jetbrains/dokka/model/DModule;)Ljava/util/Map; + public abstract fun read (Lorg/jetbrains/dokka/model/DPackage;)Ljava/util/Map; +} + +public abstract interface class org/jetbrains/kotlin/analysis/kotlin/internal/SyntheticDocumentableDetector { + public abstract fun isSynthetic (Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Z +} + diff --git a/subprojects/analysis-kotlin-api/build.gradle.kts b/subprojects/analysis-kotlin-api/build.gradle.kts new file mode 100644 index 00000000..3a10ff55 --- /dev/null +++ b/subprojects/analysis-kotlin-api/build.gradle.kts @@ -0,0 +1,14 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("org.jetbrains.conventions.kotlin-jvm") + id("org.jetbrains.conventions.maven-publish") +} + +dependencies { + compileOnly(projects.core) +} + +registerDokkaArtifactPublication("analysisKotlinApi") { + artifactId = "analysis-kotlin-api" +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/KotlinAnalysisPlugin.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/KotlinAnalysisPlugin.kt new file mode 100644 index 00000000..3138e17b --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/KotlinAnalysisPlugin.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlin.analysis.kotlin + +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +class KotlinAnalysisPlugin : DokkaPlugin() { + + /* + * This is where stable public API will go. + * + * No stable public API for now. + */ + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt new file mode 100644 index 00000000..08a465c0 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt @@ -0,0 +1,16 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithSources + +@InternalDokkaApi +enum class DocumentableLanguage { + JAVA, KOTLIN +} + +@InternalDokkaApi +interface DocumentableSourceLanguageParser { + fun getLanguage(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): DocumentableLanguage? +} diff --git a/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/ExternalDocumentablesProvider.kt index e6d499f4..ea418fba 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/ExternalDocumentablesProvider.kt +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/ExternalDocumentablesProvider.kt @@ -1,6 +1,7 @@ -package org.jetbrains.dokka.base.translators.descriptors +package org.jetbrains.kotlin.analysis.kotlin.internal import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DClasslike @@ -11,6 +12,7 @@ import org.jetbrains.dokka.model.DClasslike * in the project itself but are somehow related to the symbols defined in the documented project (e.g. are supertypes * of classes defined in project). */ +@InternalDokkaApi fun interface ExternalDocumentablesProvider { /** @@ -19,4 +21,4 @@ fun interface ExternalDocumentablesProvider { * Result is null if compiler haven't generated matching class descriptor. */ fun findClasslike(dri: DRI, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike? -}
\ No newline at end of file +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/FullClassHierarchyBuilder.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/FullClassHierarchyBuilder.kt new file mode 100644 index 00000000..91460002 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/FullClassHierarchyBuilder.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.SourceSetDependent + +@InternalDokkaApi +typealias Supertypes = List<DRI> + +@InternalDokkaApi +typealias ClassHierarchy = SourceSetDependent<Map<DRI, Supertypes>> + +@InternalDokkaApi +interface FullClassHierarchyBuilder { + suspend fun build(module: DModule): ClassHierarchy +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder.kt new file mode 100644 index 00000000..7f0313ea --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/InheritanceBuilder.kt @@ -0,0 +1,21 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable + +@InternalDokkaApi +interface InheritanceBuilder { + fun build(documentables: Map<DRI, Documentable>): List<InheritanceNode> +} + +@InternalDokkaApi +data class InheritanceNode( + val dri: DRI, + val children: List<InheritanceNode> = emptyList(), + val interfaces: List<DRI> = emptyList(), + val isInterface: Boolean = false +) { + override fun equals(other: Any?): Boolean = other is InheritanceNode && other.dri == dri + override fun hashCode(): Int = dri.hashCode() +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt new file mode 100644 index 00000000..e2335474 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt @@ -0,0 +1,31 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +/** + * A plugin for internal use, has no stable public API and thus must not be used by third party, + * external plugins. If you need any of the given API stabilized, please create an issue describing your use case. + */ +@InternalDokkaApi +class InternalKotlinAnalysisPlugin : DokkaPlugin() { + + val fullClassHierarchyBuilder by extensionPoint<FullClassHierarchyBuilder>() + + val syntheticDocumentableDetector by extensionPoint<SyntheticDocumentableDetector>() + + val moduleAndPackageDocumentationReader by extensionPoint<ModuleAndPackageDocumentationReader>() + + val kotlinToJavaService by extensionPoint<KotlinToJavaService>() + + val inheritanceBuilder by extensionPoint<InheritanceBuilder>() + + val externalDocumentablesProvider by extensionPoint<ExternalDocumentablesProvider>() + + val documentableSourceLanguageParser by extensionPoint<DocumentableSourceLanguageParser>() + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/KotlinToJavaService.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/KotlinToJavaService.kt new file mode 100644 index 00000000..3631fac2 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/KotlinToJavaService.kt @@ -0,0 +1,9 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI + +@InternalDokkaApi +interface KotlinToJavaService { + fun findAsJava(kotlinDri: DRI): DRI? +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt new file mode 100644 index 00000000..6e641c3a --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt @@ -0,0 +1,15 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.DPackage +import org.jetbrains.dokka.model.SourceSetDependent +import org.jetbrains.dokka.model.doc.DocumentationNode + +@InternalDokkaApi +interface ModuleAndPackageDocumentationReader { + fun read(module: DModule): SourceSetDependent<DocumentationNode> + fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> + fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode? +} diff --git a/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/SyntheticDocumentableDetector.kt b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/SyntheticDocumentableDetector.kt new file mode 100644 index 00000000..3dc8afa5 --- /dev/null +++ b/subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/kotlin/analysis/kotlin/internal/SyntheticDocumentableDetector.kt @@ -0,0 +1,11 @@ +package org.jetbrains.kotlin.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.Documentable + +// TODO [beresnev] isSynthetic could be a property of Documentable +@InternalDokkaApi +interface SyntheticDocumentableDetector { + fun isSynthetic(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean +} diff --git a/subprojects/analysis-kotlin-descriptors/README.md b/subprojects/analysis-kotlin-descriptors/README.md new file mode 100644 index 00000000..fbfd1c8b --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/README.md @@ -0,0 +1,8 @@ +# Analysis: Kotlin descriptors + +An internal descriptor-based implementation for [analysis-kotlin-api](../analysis-kotlin-api), also known as K1 or +"the old compiler". + +Contains no stable public API and must not be used by anyone directly, only via [analysis-kotlin-api](../analysis-kotlin-api). + +Can be added as a runtime dependency by the runner. diff --git a/subprojects/analysis-kotlin-descriptors/api/analysis-kotlin-descriptors.api b/subprojects/analysis-kotlin-descriptors/api/analysis-kotlin-descriptors.api new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/api/analysis-kotlin-descriptors.api diff --git a/subprojects/analysis-kotlin-descriptors/build.gradle.kts b/subprojects/analysis-kotlin-descriptors/build.gradle.kts new file mode 100644 index 00000000..3f028090 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/build.gradle.kts @@ -0,0 +1,43 @@ +import org.jetbrains.DokkaPublicationBuilder +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("org.jetbrains.conventions.kotlin-jvm") + id("org.jetbrains.conventions.maven-publish") + id("com.github.johnrengelman.shadow") +} + +dependencies { + implementation(projects.subprojects.analysisKotlinApi) + implementation(projects.subprojects.analysisKotlinDescriptors.compiler) + implementation(projects.subprojects.analysisKotlinDescriptors.ide) +} + +tasks { + // There are several reasons for shadowing all dependencies in one place: + // 1. Some of the artifacts Dokka depends on, like com.jetbrains.intellij.java:java-psi, are not + // published to Maven Central, so the users would need to add custom repositories to their build scripts. + // 2. There are many intertwining transitive dependencies of different versions, as well as direct copy-paste, + // that can lead to runtime errors due to classpath conflicts, so it's best to let Gradle take care of + // dependency resolution, and then pack everything into a single jar in a single place that can be tuned. + // 3. The compiler and ide modules are internal details that are likely to change, so packing everything into + // a single jar provides some stability for the CLI users, while not exposing too many internals. Publishing + // the compiler, ide and other subprojects separately would make it difficult to refactor the project structure. + shadowJar { + val dokka_version: String by project + + // cannot be named exactly like the artifact (i.e analysis-kotlin-descriptors-VER.jar), + // otherwise leads to obscure test failures when run via CLI, but not via IJ + archiveFileName.set("analysis-kotlin-descriptors-all-$dokka_version.jar") + archiveClassifier.set("") + + // service files are merged to make sure all Dokka plugins + // from the dependencies are loaded, and not just a single one. + mergeServiceFiles() + } +} + +registerDokkaArtifactPublication("analysisKotlinDescriptors") { + artifactId = "analysis-kotlin-descriptors" + component = DokkaPublicationBuilder.Component.Shadow +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/README.md b/subprojects/analysis-kotlin-descriptors/compiler/README.md new file mode 100644 index 00000000..5676fbf2 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/README.md @@ -0,0 +1,9 @@ +# Descriptors: compiler + +An internal module that encapsulates external compiler (`org.jetbrains.kotlin:kotlin-compiler`) dependencies. + +Parses Kotlin sources. + +Exists primarily to make sure that unreliable and coupled external dependencies are somewhat abstracted away, +otherwise everything gets tangled together and breaking changes in such dependencies become very +difficult to resolve. diff --git a/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api b/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api new file mode 100644 index 00000000..6c642ad6 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/api/compiler.api @@ -0,0 +1,68 @@ +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/AnalysisContextCreator { + public abstract fun create (Lcom/intellij/mock/MockProject;Lorg/jetbrains/kotlin/descriptors/ModuleDescriptor;Lorg/jetbrains/kotlin/analyzer/ResolverForModule;Lorg/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment;Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisEnvironment;)Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisContext; +} + +public final class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V + public final fun getAnalysisContextCreator ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getCompilerExtensionPointProvider ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getDescriptorFinder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKdocFinder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKlibService ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getKotlinAnalysis ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; + public final fun getMockApplicationHack ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; +} + +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerExtensionPointProvider { + public abstract fun get ()Ljava/util/List; +} + +public final class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerExtensionPointProvider$CompilerExtensionPoint { + public fun <init> (Lorg/jetbrains/kotlin/extensions/ApplicationExtensionDescriptor;Ljava/util/List;)V + public final fun getExtensionDescriptor ()Lorg/jetbrains/kotlin/extensions/ApplicationExtensionDescriptor; + public final fun getExtensions ()Ljava/util/List; +} + +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/DescriptorFinder { + public abstract fun findDescriptor (Lorg/jetbrains/kotlin/psi/KtDeclaration;)Lorg/jetbrains/kotlin/descriptors/DeclarationDescriptor; +} + +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder { + public abstract fun find (Lorg/jetbrains/kotlin/descriptors/DeclarationDescriptor;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag; + public abstract fun findKDoc (Lorg/jetbrains/kotlin/psi/KtElement;)Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag; + public abstract fun resolveKDocLink (Lorg/jetbrains/kotlin/descriptors/DeclarationDescriptor;Ljava/lang/String;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Z)Ljava/util/Collection; +} + +public final class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder$DefaultImpls { + public static synthetic fun find$default (Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder;Lorg/jetbrains/kotlin/descriptors/DeclarationDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/kotlin/kdoc/psi/impl/KDocTag; + public static synthetic fun resolveKDocLink$default (Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder;Lorg/jetbrains/kotlin/descriptors/DeclarationDescriptor;Ljava/lang/String;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;ZILjava/lang/Object;)Ljava/util/Collection; +} + +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KLibService { + public abstract fun createPackageFragmentProvider (Lorg/jetbrains/kotlin/library/KotlinLibrary;Lorg/jetbrains/kotlin/storage/StorageManager;Lorg/jetbrains/kotlin/backend/common/serialization/metadata/KlibMetadataModuleDescriptorFactory;Lorg/jetbrains/kotlin/config/LanguageVersionSettings;Lorg/jetbrains/kotlin/descriptors/ModuleDescriptor;Lorg/jetbrains/kotlin/incremental/components/LookupTracker;)Lorg/jetbrains/kotlin/descriptors/PackageFragmentProvider; + public abstract fun isAnalysisCompatible (Lorg/jetbrains/kotlin/library/KotlinLibrary;)Z +} + +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/MockApplicationHack { + public abstract fun hack (Lcom/intellij/mock/MockApplication;)V +} + +public abstract interface class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisContext : java/io/Closeable { + public abstract fun getEnvironment ()Lorg/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment; + public abstract fun getModuleDescriptor ()Lorg/jetbrains/kotlin/descriptors/ModuleDescriptor; + public abstract fun getProject ()Lcom/intellij/openapi/project/Project; + public abstract fun getResolveSession ()Lorg/jetbrains/kotlin/resolve/lazy/ResolveSession; +} + +public final class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisEnvironment : com/intellij/openapi/Disposable { + public fun <init> (Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector;Lorg/jetbrains/dokka/Platform;Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerExtensionPointProvider;Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/MockApplicationHack;Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KLibService;)V + public fun dispose ()V +} + +public abstract class org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinAnalysis : java/io/Closeable { + public fun <init> ()V + public fun <init> (Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinAnalysis;)V + public synthetic fun <init> (Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinAnalysis;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun get (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Lorg/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisContext; +} + diff --git a/subprojects/analysis-kotlin-descriptors/compiler/build.gradle.kts b/subprojects/analysis-kotlin-descriptors/compiler/build.gradle.kts new file mode 100644 index 00000000..1b40027d --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("org.jetbrains.conventions.kotlin-jvm") +} + +dependencies { + compileOnly(projects.core) + compileOnly(projects.subprojects.analysisKotlinApi) + + api(libs.kotlin.compiler) + + implementation(projects.subprojects.analysisMarkdownJb) + implementation(projects.subprojects.analysisJavaPsi) + + testImplementation(projects.core.contentMatcherTestUtils) + testImplementation(projects.core.testApi) + testImplementation(platform(libs.junit.bom)) + testImplementation(libs.junit.jupiter) + + // TODO [beresnev] get rid of it + compileOnly(libs.kotlinx.coroutines.core) +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/AnalysisContextCreator.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/AnalysisContextCreator.kt new file mode 100644 index 00000000..dfba2b3a --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/AnalysisContextCreator.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import com.intellij.mock.MockProject +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisContext +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisEnvironment +import org.jetbrains.kotlin.analyzer.ResolverForModule +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.ModuleDescriptor + +@InternalDokkaApi +interface AnalysisContextCreator { + fun create( + project: MockProject, + moduleDescriptor: ModuleDescriptor, + moduleResolver: ResolverForModule, + kotlinEnvironment: KotlinCoreEnvironment, + analysisEnvironment: AnalysisEnvironment, + ): AnalysisContext +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt new file mode 100644 index 00000000..535e628e --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDescriptorAnalysisPlugin.kt @@ -0,0 +1,135 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute +import com.intellij.psi.PsiAnnotation +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.analysis.java.BreakingAbstractionKotlinLightMethodChecker +import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.KotlinAnalysis +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.ProjectKotlinAnalysis +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.* +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentationReader +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java.* +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator.DefaultDescriptorToDocumentableTranslator +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator.DefaultExternalDocumentablesProvider +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.renderers.PostAction +import org.jetbrains.kotlin.analysis.kotlin.internal.InternalKotlinAnalysisPlugin +import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation + +@InternalDokkaApi +class CompilerDescriptorAnalysisPlugin : DokkaPlugin() { + + val kdocFinder by extensionPoint<KDocFinder>() + + val descriptorFinder by extensionPoint<DescriptorFinder>() + + val klibService by extensionPoint<KLibService>() + + val compilerExtensionPointProvider by extensionPoint<CompilerExtensionPointProvider>() + + val mockApplicationHack by extensionPoint<MockApplicationHack>() + + val analysisContextCreator by extensionPoint<AnalysisContextCreator>() + + val kotlinAnalysis by extensionPoint<KotlinAnalysis>() + + internal val documentableAnalyzerImpl by extending { + plugin<InternalKotlinAnalysisPlugin>().documentableSourceLanguageParser providing { CompilerDocumentableSourceLanguageParser() } + } + + internal val defaultKotlinAnalysis by extending { + kotlinAnalysis providing { ctx -> + ProjectKotlinAnalysis( + sourceSets = ctx.configuration.sourceSets, + context = ctx + ) + } + } + + internal val descriptorToDocumentableTranslator by extending { + CoreExtensions.sourceToDocumentableTranslator providing ::DefaultDescriptorToDocumentableTranslator + } + + internal val defaultSamplesTransformer by extending { + CoreExtensions.pageTransformer providing ::DefaultSamplesTransformer + } + + internal val descriptorFullClassHierarchyBuilder by extending { + plugin<InternalKotlinAnalysisPlugin>().fullClassHierarchyBuilder providing { DescriptorFullClassHierarchyBuilder() } + } + + internal val descriptorSyntheticDocumentableDetector by extending { + plugin<InternalKotlinAnalysisPlugin>().syntheticDocumentableDetector providing { DescriptorSyntheticDocumentableDetector() } + } + + internal val moduleAndPackageDocumentationReader by extending { + plugin<InternalKotlinAnalysisPlugin>().moduleAndPackageDocumentationReader providing ::ModuleAndPackageDocumentationReader + } + + internal val kotlinToJavaMapper by extending { + plugin<InternalKotlinAnalysisPlugin>().kotlinToJavaService providing { DescriptorKotlinToJavaMapper() } + } + + internal val descriptorInheritanceBuilder by extending { + plugin<InternalKotlinAnalysisPlugin>().inheritanceBuilder providing { DescriptorInheritanceBuilder() } + } + + internal val defaultExternalDocumentablesProvider by extending { + plugin<InternalKotlinAnalysisPlugin>().externalDocumentablesProvider providing ::DefaultExternalDocumentablesProvider + } + + private val javaAnalysisPlugin by lazy { plugin<JavaAnalysisPlugin>() } + + internal val projectProvider by extending { + javaAnalysisPlugin.projectProvider providing { KotlinAnalysisProjectProvider() } + } + + internal val sourceRootsExtractor by extending { + javaAnalysisPlugin.sourceRootsExtractor providing { KotlinAnalysisSourceRootsExtractor() } + } + + internal val kotlinDocCommentCreator by extending { + javaAnalysisPlugin.docCommentCreators providing { + DescriptorKotlinDocCommentCreator(querySingle { kdocFinder }, querySingle { descriptorFinder }) + } + } + + internal val kotlinDocCommentParser by extending { + javaAnalysisPlugin.docCommentParsers providing { context -> + DescriptorKotlinDocCommentParser( + context, + context.logger + ) + } + } + + internal val inheritDocTagProvider by extending { + javaAnalysisPlugin.inheritDocTagContentProviders providing ::KotlinInheritDocTagContentProvider + } + + internal val kotlinLightMethodChecker by extending { + javaAnalysisPlugin.kotlinLightMethodChecker providing { + object : BreakingAbstractionKotlinLightMethodChecker { + override fun isLightAnnotation(annotation: PsiAnnotation): Boolean { + return annotation is KtLightAbstractAnnotation + } + + override fun isLightAnnotationAttribute(attribute: JvmAnnotationAttribute): Boolean { + return attribute is KtLightAbstractAnnotation + } + } + } + } + + internal val disposeKotlinAnalysisPostAction by extending { + CoreExtensions.postActions with PostAction { querySingle { kotlinAnalysis }.close() } + } + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDocumentableSourceLanguageParser.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDocumentableSourceLanguageParser.kt new file mode 100644 index 00000000..888ccfa9 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerDocumentableSourceLanguageParser.kt @@ -0,0 +1,23 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.DescriptorDocumentableSource +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithSources +import org.jetbrains.kotlin.analysis.kotlin.internal.DocumentableLanguage +import org.jetbrains.kotlin.analysis.kotlin.internal.DocumentableSourceLanguageParser + +internal class CompilerDocumentableSourceLanguageParser : DocumentableSourceLanguageParser { + override fun getLanguage( + documentable: Documentable, + sourceSet: DokkaConfiguration.DokkaSourceSet, + ): DocumentableLanguage? { + val documentableSource = (documentable as? WithSources)?.sources?.get(sourceSet) ?: return null + return when (documentableSource) { + is PsiDocumentableSource -> DocumentableLanguage.JAVA + is DescriptorDocumentableSource -> DocumentableLanguage.KOTLIN + else -> error("Unknown language sources: ${documentableSource::class}") + } + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerExtensionPointProvider.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerExtensionPointProvider.kt new file mode 100644 index 00000000..50bdbb2c --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/CompilerExtensionPointProvider.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.kotlin.extensions.ApplicationExtensionDescriptor + +@InternalDokkaApi +interface CompilerExtensionPointProvider { + fun get(): List<CompilerExtensionPoint> + + class CompilerExtensionPoint( + val extensionDescriptor: ApplicationExtensionDescriptor<Any>, + val extensions: List<Any> + ) +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/DescriptorFinder.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/DescriptorFinder.kt new file mode 100644 index 00000000..eed62fa3 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/DescriptorFinder.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtDeclaration + +@InternalDokkaApi +interface DescriptorFinder { + fun KtDeclaration.findDescriptor(): DeclarationDescriptor? +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder.kt new file mode 100644 index 00000000..23d1acfe --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KDocFinder.kt @@ -0,0 +1,30 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils + +@InternalDokkaApi +interface KDocFinder { + fun KtElement.findKDoc(): KDocTag? + + fun DeclarationDescriptor.find( + descriptorToPsi: (DeclarationDescriptorWithSource) -> PsiElement? = { + DescriptorToSourceUtils.descriptorToDeclaration( + it + ) + } + ): KDocTag? + + fun resolveKDocLink( + fromDescriptor: DeclarationDescriptor, + qualifiedName: String, + sourceSet: DokkaConfiguration.DokkaSourceSet, + emptyBindingContext: Boolean = false + ): Collection<DeclarationDescriptor> +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KLibService.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KLibService.kt new file mode 100644 index 00000000..ceb5536a --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/KLibService.kt @@ -0,0 +1,23 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataModuleDescriptorFactory +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.library.KotlinLibrary +import org.jetbrains.kotlin.storage.StorageManager + +@InternalDokkaApi +interface KLibService { + fun KotlinLibrary.createPackageFragmentProvider( + storageManager: StorageManager, + metadataModuleDescriptorFactory: KlibMetadataModuleDescriptorFactory, + languageVersionSettings: LanguageVersionSettings, + moduleDescriptor: ModuleDescriptor, + lookupTracker: LookupTracker + ): PackageFragmentProvider? + + fun isAnalysisCompatible(kotlinLibrary: KotlinLibrary): Boolean +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/MockApplicationHack.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/MockApplicationHack.kt new file mode 100644 index 00000000..77a4e083 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/MockApplicationHack.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler + +import com.intellij.mock.MockApplication +import org.jetbrains.dokka.InternalDokkaApi + +@InternalDokkaApi +interface MockApplicationHack { // ¯\_(ツ)_/¯ + fun hack(mockApplication: MockApplication) +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AbsolutePathString.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AbsolutePathString.kt new file mode 100644 index 00000000..f1d35752 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AbsolutePathString.kt @@ -0,0 +1,3 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration + +internal typealias AbsolutePathString = String diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisContext.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisContext.kt index ca83d029..89ae8810 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisContext.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisContext.kt @@ -1,18 +1,26 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration +import com.intellij.openapi.project.Project import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.AnalysisContextCreator +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.resolve.lazy.ResolveSession import java.io.Closeable import java.io.File internal fun createAnalysisContext( - logger: DokkaLogger, + context: DokkaContext, sourceSets: List<DokkaConfiguration.DokkaSourceSet>, sourceSet: DokkaConfiguration.DokkaSourceSet, analysisConfiguration: DokkaAnalysisConfiguration @@ -22,7 +30,7 @@ internal fun createAnalysisContext( val sources = sourceSet.sourceRoots + parentSourceSets.flatMap { it.sourceRoots } return createAnalysisContext( - logger = logger, + context = context, classpath = classpath, sourceRoots = sources, sourceSet = sourceSet, @@ -31,13 +39,19 @@ internal fun createAnalysisContext( } internal fun createAnalysisContext( - logger: DokkaLogger, + context: DokkaContext, classpath: List<File>, sourceRoots: Set<File>, sourceSet: DokkaConfiguration.DokkaSourceSet, analysisConfiguration: DokkaAnalysisConfiguration ): AnalysisContext { - val analysisEnvironment = AnalysisEnvironment(DokkaMessageCollector(logger), sourceSet.analysisPlatform).apply { + val analysisEnvironment = AnalysisEnvironment( + DokkaMessageCollector(context.logger), + sourceSet.analysisPlatform, + context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { compilerExtensionPointProvider }, + context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { mockApplicationHack }, + context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { klibService }, + ).apply { if (analysisPlatform == Platform.jvm) { configureJdkClasspathRoots() } @@ -48,15 +62,14 @@ internal fun createAnalysisContext( } val environment = analysisEnvironment.createCoreEnvironment() - val (facade, _) = analysisEnvironment.createResolutionFacade( + return analysisEnvironment.createResolutionFacade( environment, + context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle<CompilerDescriptorAnalysisPlugin, AnalysisContextCreator> { analysisContextCreator }, analysisConfiguration.ignoreCommonBuiltIns ) - - return AnalysisContext(environment, facade, analysisEnvironment) } -class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { +internal class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { override fun clear() { seenErrors = false } @@ -73,22 +86,9 @@ class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector override fun hasErrors() = seenErrors } -// It is not data class due to ill-defined equals -class AnalysisContext( - environment: KotlinCoreEnvironment, - facade: DokkaResolutionFacade, - private val analysisEnvironment: AnalysisEnvironment -) : Closeable { - private var isClosed: Boolean = false - val environment: KotlinCoreEnvironment = environment - get() = field.takeUnless { isClosed } ?: throw IllegalStateException("AnalysisEnvironment is already closed") - val facade: DokkaResolutionFacade = facade - get() = field.takeUnless { isClosed } ?: throw IllegalStateException("AnalysisEnvironment is already closed") - - operator fun component1() = environment - operator fun component2() = facade - override fun close() { - isClosed = true - analysisEnvironment.dispose() - } +interface AnalysisContext : Closeable { + val environment: KotlinCoreEnvironment + val resolveSession: ResolveSession + val moduleDescriptor: ModuleDescriptor + val project: Project } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisEnvironment.kt index bbc6dda6..611d1325 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/AnalysisEnvironment.kt @@ -1,8 +1,9 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.core.CoreApplicationEnvironment import com.intellij.mock.MockApplication import com.intellij.mock.MockComponentManager +import com.intellij.mock.MockProject import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.extensions.Extensions @@ -16,14 +17,21 @@ import com.intellij.psi.javadoc.CustomJavadocTagProvider import com.intellij.psi.javadoc.JavadocManager import com.intellij.psi.javadoc.JavadocTagInfo import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.dokka.InternalDokkaApi import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.analysis.resolve.* +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.AnalysisContextCreator +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerExtensionPointProvider +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KLibService +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.MockApplicationHack +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve.* import org.jetbrains.kotlin.analyzer.* -import org.jetbrains.kotlin.analyzer.common.* +import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters +import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer +import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices +import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns -import org.jetbrains.kotlin.caches.resolve.* import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.config.ContentRoot import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot @@ -42,9 +50,6 @@ import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.extensions.ApplicationExtensionDescriptor -import org.jetbrains.kotlin.ide.konan.NativePlatformKindResolution -import org.jetbrains.kotlin.idea.klib.KlibLoadingMetadataCache -import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION @@ -55,12 +60,7 @@ import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.CommonPlatforms -import org.jetbrains.kotlin.platform.IdePlatformKind import org.jetbrains.kotlin.platform.TargetPlatform -import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind -import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind -import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind -import org.jetbrains.kotlin.platform.impl.NativeIdePlatformKind import org.jetbrains.kotlin.platform.js.JsPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms.unspecifiedJvmPlatform @@ -77,7 +77,7 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager import java.io.File import org.jetbrains.kotlin.konan.file.File as KFile -const val JAR_SEPARATOR = "!/" +internal const val JAR_SEPARATOR = "!/" /** * Kotlin as a service entry point @@ -87,14 +87,21 @@ const val JAR_SEPARATOR = "!/" * $messageCollector: required by compiler infrastructure and will receive all compiler messages * $body: optional and can be used to configure environment without creating local variable */ -class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPlatform: Platform) : Disposable { - val configuration = CompilerConfiguration() +@InternalDokkaApi +class AnalysisEnvironment( + private val messageCollector: MessageCollector, + internal val analysisPlatform: Platform, + private val compilerExtensionPointProvider: CompilerExtensionPointProvider, + private val mockApplicationHack: MockApplicationHack, + private val kLibService: KLibService, +) : Disposable { + private val configuration = CompilerConfiguration() init { configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) } - fun createCoreEnvironment(): KotlinCoreEnvironment { + internal fun createCoreEnvironment(): KotlinCoreEnvironment { System.setProperty("idea.io.use.nio2", "true") System.setProperty("idea.ignore.disabled.plugins", "true") @@ -123,8 +130,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl // TODO: figure out why compilation fails with unresolved `CoreApplicationEnvironment.registerApplicationService(...)` // call, fix it appropriately with(ApplicationManager.getApplication() as MockApplication) { - if (getService(KlibLoadingMetadataCache::class.java) == null) - registerService(KlibLoadingMetadataCache::class.java, KlibLoadingMetadataCache()) + mockApplicationHack.hack(this) } projectComponentManager.registerService( @@ -142,28 +148,9 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl CustomJavadocTagProvider { emptyList() } ) - registerExtensionPoint( - ApplicationExtensionDescriptor("org.jetbrains.kotlin.idePlatformKind", IdePlatformKind::class.java), - listOf( - CommonIdePlatformKind, - JvmIdePlatformKind, - JsIdePlatformKind, - NativeIdePlatformKind - ), - this - ) - - registerExtensionPoint( - IdePlatformKindResolution, - listOf( - CommonPlatformKindResolution(), - JvmPlatformKindResolution(), - JsPlatformKindResolution(), - NativePlatformKindResolution() - ), - this - ) - + compilerExtensionPointProvider.get().forEach { extension -> + registerExtensionPoint(extension.extensionDescriptor, extension.extensions, this) + } return environment } @@ -176,7 +163,11 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl ) } - fun createResolutionFacade(environment: KotlinCoreEnvironment, ignoreCommonBuiltIns: Boolean = false): Pair<DokkaResolutionFacade, DokkaResolutionFacade> { + internal fun createResolutionFacade( + environment: KotlinCoreEnvironment, + analysisContextCreator: AnalysisContextCreator, + ignoreCommonBuiltIns: Boolean = false + ): AnalysisContext { val projectContext = ProjectContext(environment.project, "Dokka") val sourceFiles = environment.getSourceFiles() @@ -269,33 +260,24 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl Platform.native -> createNativeResolverForProject(projectContext, module, modulesContent) } + @Suppress("UNUSED_VARIABLE") // BEWARE!!!! IT's UNUSED, but without it some things don't work val libraryModuleDescriptor = resolverForProject.descriptorForModule(library) + val moduleDescriptor = resolverForProject.descriptorForModule(module) builtIns?.initialize(moduleDescriptor, true) - val resolverForLibrary = - resolverForProject.resolverForModule(library) // Required before module to initialize library properly + @Suppress("UNUSED_VARIABLE") // BEWARE!!!! IT's UNUSED, but without it some things don't work + val resolverForLibrary = resolverForProject.resolverForModule(library) // Required before module to initialize library properly + val resolverForModule = resolverForProject.resolverForModule(module) - val libraryResolutionFacade = - DokkaResolutionFacade( - environment.project, - libraryModuleDescriptor, - resolverForLibrary - ) - val created = - DokkaResolutionFacade( - environment.project, - moduleDescriptor, - resolverForModule - ) - val projectComponentManager = environment.project as MockComponentManager - projectComponentManager.registerService( - KotlinCacheService:: - class.java, - CoreKotlinCacheService(created) - ) - return created to libraryResolutionFacade + return analysisContextCreator.create( + environment.project as MockProject, + moduleDescriptor, + resolverForModule, + environment, + this + ) } private fun Platform.analyzerServices() = when (this) { @@ -305,7 +287,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl Platform.jvm -> JvmPlatformAnalyzerServices } - fun Collection<KotlinLibrary>.registerLibraries(): List<DokkaKlibLibraryInfo> { + private fun Collection<KotlinLibrary>.registerLibraries(): List<DokkaKlibLibraryInfo> { if (analysisPlatform != Platform.native && analysisPlatform != Platform.js && analysisPlatform != Platform.wasm) return emptyList() val dependencyResolver = DokkaKlibLibraryDependencyResolver() val analyzerServices = analysisPlatform.analyzerServices() @@ -331,8 +313,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl libraryFile = KFile(libraryFile.absolutePath), strategy = ToolingSingleFileKlibResolveStrategy ) - - if (kotlinLibrary.getCompatibilityInfo().isCompatible) { + if (kLibService.isAnalysisCompatible(kotlinLibrary)) { // exists, is KLIB, has compatible format put( libraryFile.absolutePath, @@ -404,7 +385,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl override fun createResolverForModule( descriptor: ModuleDescriptor, moduleInfo: ModuleInfo - ): ResolverForModule = DokkaJsResolverForModuleFactory(CompilerEnvironment).createResolverForModule( + ): ResolverForModule = DokkaJsResolverForModuleFactory(CompilerEnvironment, kLibService).createResolverForModule( descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), modulesContent(moduleInfo), @@ -435,7 +416,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl moduleInfo: ModuleInfo ): ResolverForModule { - return DokkaNativeResolverForModuleFactory(CompilerEnvironment).createResolverForModule( + return DokkaNativeResolverForModuleFactory(CompilerEnvironment, kLibService).createResolverForModule( descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), modulesContent(moduleInfo), @@ -518,7 +499,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl } } - fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) { + internal fun loadLanguageVersionSettings(languageVersionString: String?, apiVersionString: String?) { val languageVersion = LanguageVersion.fromVersionString(languageVersionString) ?: LanguageVersion.LATEST_STABLE val apiVersion = apiVersionString?.let { ApiVersion.parse(it) } ?: ApiVersion.createByLanguageVersion(languageVersion) @@ -534,7 +515,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl /** * Classpath for this environment. */ - val classpath: List<File> + private val classpath: List<File> get() = configuration.jvmClasspathRoots + configuration.getList(JSConfigurationKeys.LIBRARIES) .mapNotNull { File(it) } @@ -542,7 +523,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl * Adds list of paths to classpath. * $paths: collection of files to add */ - fun addClasspath(paths: List<File>) { + internal fun addClasspath(paths: List<File>) { if (analysisPlatform == Platform.js || analysisPlatform == Platform.wasm) { configuration.addAll(JSConfigurationKeys.LIBRARIES, paths.map { it.absolutePath }) } else { @@ -551,12 +532,12 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl } // Set up JDK classpath roots explicitly because of https://github.com/JetBrains/kotlin/commit/f89765eb33dd95c8de33a919cca83651b326b246 - fun configureJdkClasspathRoots() = configuration.configureJdkClasspathRoots() + internal fun configureJdkClasspathRoots() = configuration.configureJdkClasspathRoots() /** * Adds path to classpath. * $path: path to add */ - fun addClasspath(path: File) { + internal fun addClasspath(path: File) { if (analysisPlatform == Platform.js || analysisPlatform == Platform.wasm) { configuration.add(JSConfigurationKeys.LIBRARIES, path.absolutePath) } else { @@ -567,7 +548,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl /** * List of source roots for this environment. */ - val sources: List<String> + internal val sources: List<String> get() = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) ?.filterIsInstance<KotlinSourceRoot>() ?.map { it.path } ?: emptyList() @@ -576,7 +557,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl * Adds list of paths to source roots. * $list: collection of files to add */ - fun addSources(sourceDirectories: Iterable<File>) { + internal fun addSources(sourceDirectories: Iterable<File>) { sourceDirectories.forEach { directory -> configuration.addKotlinSourceRoot(directory.path) if (directory.isDirectory || directory.extension == "java") { @@ -585,7 +566,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl } } - fun addRoots(list: List<ContentRoot>) { + internal fun addRoots(list: List<ContentRoot>) { configuration.addAll(CLIConfigurationKeys.CONTENT_ROOTS, list) } @@ -596,7 +577,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl Disposer.dispose(this) } - companion object { + private companion object { private fun <T : Any> registerExtensionPoint( appExtension: ApplicationExtensionDescriptor<T>, instances: List<T>, diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CallableFactory.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/CallableFactory.kt index de48cfae..0cc17219 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CallableFactory.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/CallableFactory.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.psi.PsiField import com.intellij.psi.PsiMethod @@ -7,7 +7,7 @@ import org.jetbrains.dokka.links.JavaClassReference import org.jetbrains.dokka.links.TypeReference import org.jetbrains.kotlin.descriptors.CallableDescriptor -fun Callable.Companion.from(descriptor: CallableDescriptor, name: String? = null) = with(descriptor) { +internal fun Callable.Companion.from(descriptor: CallableDescriptor, name: String? = null) = with(descriptor) { Callable( name ?: descriptor.name.asString(), extensionReceiverParameter?.let { TypeReference.from(it) }, @@ -15,14 +15,14 @@ fun Callable.Companion.from(descriptor: CallableDescriptor, name: String? = null ) } -fun Callable.Companion.from(psi: PsiMethod) = with(psi) { +internal fun Callable.Companion.from(psi: PsiMethod) = with(psi) { Callable( name, null, parameterList.parameters.map { param -> JavaClassReference(param.type.canonicalText) }) } -fun Callable.Companion.from(psi: PsiField): Callable { +internal fun Callable.Companion.from(psi: PsiField): Callable { return Callable( name = psi.name, receiver = null, diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/DRIFactory.kt index 73b20885..726d6dbc 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/DRIFactory.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.psi.* import org.jetbrains.dokka.links.* @@ -7,11 +7,10 @@ import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWithSelf.run { +internal fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWithSelf.run { val parameter = firstIsInstanceOrNull<ValueParameterDescriptor>() - val callable = parameter?.containingDeclaration ?: firstIsInstanceOrNull() + val callable = parameter?.containingDeclaration ?: firstIsInstanceOrNull<CallableDescriptor>() DRI( packageName = firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString() ?: "", @@ -21,13 +20,13 @@ fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWi ?.joinToString(separator = ".") { it.name.asString() }, callable = callable?.let { Callable.from(it) }, target = DriTarget.from(parameter ?: descriptor), - extra = if (descriptor is EnumEntrySyntheticClassDescriptor || descriptor.safeAs<ClassDescriptor>()?.kind == ClassKind.ENUM_ENTRY) + extra = if (descriptor is EnumEntrySyntheticClassDescriptor || (descriptor as? ClassDescriptor)?.kind == ClassKind.ENUM_ENTRY) DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() else null ) } -fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run { +internal fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run { val psiMethod = firstIsInstanceOrNull<PsiMethod>() val psiField = firstIsInstanceOrNull<PsiField>() val classes = filterIsInstance<PsiClass>().filterNot { it is PsiTypeParameter } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRITargetFactory.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/DRITargetFactory.kt index e1e93962..7f39cf94 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRITargetFactory.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/DRITargetFactory.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -fun DriTarget.Companion.from(descriptor: DeclarationDescriptor): DriTarget = descriptor.parentsWithSelf.run { +internal fun DriTarget.Companion.from(descriptor: DeclarationDescriptor): DriTarget = descriptor.parentsWithSelf.run { return when (descriptor) { is TypeParameterDescriptor -> PointingToGenericParameters(descriptor.index) is ValueParameterDescriptor -> PointingToCallableParameters(descriptor.index) @@ -30,7 +30,7 @@ fun DriTarget.Companion.from(descriptor: DeclarationDescriptor): DriTarget = des } -fun DriTarget.Companion.from(psi: PsiElement): DriTarget = psi.parentsWithSelf.run { +internal fun DriTarget.Companion.from(psi: PsiElement): DriTarget = psi.parentsWithSelf.run { return when (psi) { is PsiTypeParameter -> PointingToGenericParameters(psi.index) else -> firstIsInstanceOrNull<PsiParameter>()?.let { diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/Documentable.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/Documentable.kt new file mode 100644 index 00000000..55a93ad5 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/Documentable.kt @@ -0,0 +1,24 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration + +import com.intellij.psi.PsiDocumentManager +import org.jetbrains.dokka.model.DocumentableSource +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.load.kotlin.toSourceElement +import org.jetbrains.kotlin.resolve.source.getPsi + +internal class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource { + override val path = descriptor.toSourceElement.containingFile.toString() + + override fun computeLineNumber(): Int? { + return (this.descriptor as DeclarationDescriptorWithSource) + .source.getPsi() + ?.let { + val range = it.node?.findChildByType(KtTokens.IDENTIFIER)?.textRange ?: it.textRange + val doc = PsiDocumentManager.getInstance(it.project).getDocument(it.containingFile) + // IJ uses 0-based line-numbers; external source browsers use 1-based + doc?.getLineNumber(range.startOffset)?.plus(1) + } + } +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/JvmDependenciesIndexImpl.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/JvmDependenciesIndexImpl.kt index 1075665e..42fda615 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/JvmDependenciesIndexImpl.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/JvmDependenciesIndexImpl.kt @@ -14,22 +14,23 @@ * limitations under the License. */ -package org.jetbrains.kotlin.cli.jvm.index +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.ide.highlighter.JavaClassFileType import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile -import it.unimi.dsi.fastutil.ints.IntArrayList import gnu.trove.THashMap +import it.unimi.dsi.fastutil.ints.IntArrayList +import org.jetbrains.kotlin.cli.jvm.index.JavaRoot +import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName -import java.util.* // speeds up finding files/classes in classpath/java source roots // NOT THREADSAFE, needs to be adapted/removed if we want compiler to be multithreaded // the main idea of this class is for each package to store roots which contains it to avoid excessive file system traversal -class JvmDependenciesIndexImpl(_roots: List<JavaRoot>) : JvmDependenciesIndex { +internal class JvmDependenciesIndexImpl(_roots: List<JavaRoot>) : JvmDependenciesIndex { //these fields are computed based on _roots passed to constructor which are filled in later private val roots: List<JavaRoot> by lazy { _roots.toList() } @@ -54,7 +55,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>) : JvmDependenciesIndex { Cache().apply { roots.indices.forEach(rootIndices::add) rootIndices.add(maxIndex) - rootIndices.trim() + rootIndices.trimToSize(0) } } @@ -139,7 +140,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>) : JvmDependenciesIndex { } } processedRootsUpTo = - if (cacheRootIndices.isEmpty) { + if (cacheRootIndices.isEmpty()) { processedRootsUpTo } else { cacheRootIndices.getInt(cacheRootIndices.size - 1) @@ -165,7 +166,7 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>) : JvmDependenciesIndex { for (i in (fillCachesAfter + 1) until cachesPath.size) { // we all know roots that contain this package by now cachesPath[i].rootIndices.add(maxIndex) - cachesPath[i].rootIndices.trim() + cachesPath[i].rootIndices.trimToSize(0) } return null } @@ -235,7 +236,12 @@ class JvmDependenciesIndexImpl(_roots: List<JavaRoot>) : JvmDependenciesIndex { return caches } - private data class FindClassRequest(val classId: ClassId, override val acceptedRootTypes: Set<JavaRoot.RootType>) : SearchRequest { + private fun <E> MutableList<E>.trimToSize(newSize: Int) { + subList(newSize, size).clear() + } + + private data class FindClassRequest(val classId: ClassId, override val acceptedRootTypes: Set<JavaRoot.RootType>) : + SearchRequest { override val packageFqName: FqName get() = classId.packageFqName } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinAnalysis.kt index 27328a6c..b4a1b8f7 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinAnalysis.kt @@ -1,21 +1,21 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.InternalDokkaApi import org.jetbrains.dokka.model.SourceSetDependent import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.utilities.DokkaLogger import java.io.Closeable @Suppress("FunctionName") -fun ProjectKotlinAnalysis( +internal fun ProjectKotlinAnalysis( sourceSets: List<DokkaSourceSet>, - logger: DokkaLogger, + context: DokkaContext, analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() ): KotlinAnalysis { val environments = sourceSets.associateWith { sourceSet -> createAnalysisContext( - logger = logger, + context = context, sourceSets = sourceSets, sourceSet = sourceSet, analysisConfiguration = analysisConfiguration @@ -30,9 +30,9 @@ fun ProjectKotlinAnalysis( * it's been used, there's no need to wait for [projectKotlinAnalysis] to be closed as it must be handled separately. */ @Suppress("FunctionName") -fun SamplesKotlinAnalysis( +internal fun SamplesKotlinAnalysis( sourceSets: List<DokkaSourceSet>, - logger: DokkaLogger, + context: DokkaContext, projectKotlinAnalysis: KotlinAnalysis, analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() ): KotlinAnalysis { @@ -40,7 +40,7 @@ fun SamplesKotlinAnalysis( .filter { it.samples.isNotEmpty() } .associateWith { sourceSet -> createAnalysisContext( - logger = logger, + context = context, classpath = sourceSet.classpath, sourceRoots = sourceSet.samples, sourceSet = sourceSet, @@ -51,7 +51,7 @@ fun SamplesKotlinAnalysis( return EnvironmentKotlinAnalysis(environments, projectKotlinAnalysis) } -class DokkaAnalysisConfiguration( +internal class DokkaAnalysisConfiguration( /** * Only for common platform ignore BuiltIns for StdLib since it can cause a conflict * between BuiltIns from a compiler and ones from source code. @@ -59,42 +59,25 @@ class DokkaAnalysisConfiguration( val ignoreCommonBuiltIns: Boolean = false ) -@Deprecated( - message = "Construct using list of DokkaSourceSets and logger", - replaceWith = ReplaceWith("KotlinAnalysis(context.configuration.sourceSets, context.logger)") -) -fun KotlinAnalysis(context: DokkaContext): KotlinAnalysis = - ProjectKotlinAnalysis(context.configuration.sourceSets, context.logger) - -@Deprecated( - message = "It was renamed to `ProjectKotlinAnalysis`", - replaceWith = ReplaceWith("ProjectKotlinAnalysis(sourceSets, logger, analysisConfiguration)") -) -fun KotlinAnalysis( - sourceSets: List<DokkaSourceSet>, - logger: DokkaLogger, - analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() -) = ProjectKotlinAnalysis(sourceSets, logger, analysisConfiguration) - - /** * First child delegation. It does not close [parent]. */ +@InternalDokkaApi abstract class KotlinAnalysis( - val parent: KotlinAnalysis? = null + private val parent: KotlinAnalysis? = null ) : Closeable { operator fun get(key: DokkaSourceSet): AnalysisContext { return get(key.sourceSetID) } - operator fun get(key: DokkaSourceSetID): AnalysisContext { + internal operator fun get(key: DokkaSourceSetID): AnalysisContext { return find(key) ?: parent?.get(key) ?: throw IllegalStateException("Missing EnvironmentAndFacade for sourceSet $key") } - protected abstract fun find(sourceSetID: DokkaSourceSetID): AnalysisContext? + internal abstract fun find(sourceSetID: DokkaSourceSetID): AnalysisContext? } internal open class EnvironmentKotlinAnalysis( diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinCliJavaFileManagerImpl.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinCliJavaFileManagerImpl.kt index 37a5e3f7..ac120b62 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinCliJavaFileManagerImpl.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/KotlinCliJavaFileManagerImpl.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.cli.jvm.compiler +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.core.CoreJavaFileManager import com.intellij.openapi.diagnostic.Logger @@ -25,6 +25,7 @@ import com.intellij.psi.impl.file.PsiPackageImpl import com.intellij.psi.search.GlobalSearchScope import gnu.trove.THashMap import gnu.trove.THashSet +import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex import org.jetbrains.kotlin.cli.jvm.index.SingleJavaFileRootsIndex @@ -39,13 +40,11 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager import org.jetbrains.kotlin.util.PerformanceCounter -import org.jetbrains.kotlin.utils.addIfNotNull -import java.util.* // TODO: do not inherit from CoreJavaFileManager to avoid accidental usage of its methods which do not use caches/indices // Currently, the only relevant usage of this class as CoreJavaFileManager is at CoreJavaDirectoryService.getPackage, // which is indirectly invoked from PsiPackage.getSubPackages -class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager { +internal class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager { private val perfCounter = PerformanceCounter.create("Find Java class") private lateinit var index: JvmDependenciesIndex private lateinit var singleJavaFileRootsIndex: SingleJavaFileRootsIndex @@ -175,11 +174,10 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ true } - result.addIfNotNull( - singleJavaFileRootsIndex.findJavaSourceClass(classId) - ?.takeIf { it in scope } - ?.findPsiClassInVirtualFile(relativeClassName) - ) + singleJavaFileRootsIndex.findJavaSourceClass(classId) + ?.takeIf { it in scope } + ?.findPsiClassInVirtualFile(relativeClassName) + ?.let { result.add(it) } if (result.isNotEmpty()) { return@time result.toTypedArray() diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/TypeReferenceFactory.kt index 091e54ce..f271e1f1 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/TypeReferenceFactory.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration import com.intellij.psi.PsiClass import org.jetbrains.dokka.links.* @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.types.error.ErrorType import org.jetbrains.kotlin.types.error.ErrorTypeConstructor import org.jetbrains.kotlin.types.error.ErrorTypeKind -fun TypeReference.Companion.from(d: ReceiverParameterDescriptor): TypeReference? = +internal fun TypeReference.Companion.from(d: ReceiverParameterDescriptor): TypeReference? = when (d.value) { is ExtensionReceiver -> fromPossiblyNullable(d.type, emptyList()) else -> run { @@ -22,10 +22,10 @@ fun TypeReference.Companion.from(d: ReceiverParameterDescriptor): TypeReference? } } -fun TypeReference.Companion.from(d: ValueParameterDescriptor): TypeReference = +internal fun TypeReference.Companion.from(d: ValueParameterDescriptor): TypeReference = fromPossiblyNullable(d.type, emptyList()) -fun TypeReference.Companion.from(@Suppress("UNUSED_PARAMETER") p: PsiClass) = TypeReference +internal fun TypeReference.Companion.from(@Suppress("UNUSED_PARAMETER") p: PsiClass) = TypeReference private fun TypeReference.Companion.fromPossiblyNullable(t: KotlinType, paramTrace: List<KotlinType>): TypeReference = fromPossiblyRecursive(t, paramTrace).let { if (t.isMarkedNullable) Nullable(it) else it } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/CommonKlibModuleInfo.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/CommonKlibModuleInfo.kt index 22c86dd7..a7667009 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/CommonKlibModuleInfo.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/CommonKlibModuleInfo.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices @@ -22,4 +22,4 @@ internal class CommonKlibModuleInfo( override val analyzerServices: PlatformDependentAnalyzerServices get() = CommonPlatformAnalyzerServices -}
\ No newline at end of file +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaJsKlibLibraryInfo.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaJsKlibLibraryInfo.kt index 9d28cc3c..675bf28e 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaJsKlibLibraryInfo.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaJsKlibLibraryInfo.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.library.KotlinLibrary @@ -27,4 +27,4 @@ internal class DokkaJsKlibLibraryInfo( override val platform: TargetPlatform = JsPlatforms.defaultJsPlatform override fun dependencies(): List<ModuleInfo> = listOf(this) + dependencyResolver.resolveDependencies(this) override fun getLibraryRoots(): Collection<String> = listOf(libraryRoot) -}
\ No newline at end of file +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaJsResolverForModuleFactory.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaJsResolverForModuleFactory.kt index 353c71fb..b409441b 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaJsResolverForModuleFactory.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaJsResolverForModuleFactory.kt @@ -1,5 +1,6 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KLibService import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.config.LanguageVersionSettings @@ -10,7 +11,6 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve -import org.jetbrains.kotlin.idea.klib.createKlibPackageFragmentProvider import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices import org.jetbrains.kotlin.konan.util.KlibMetadataFactories @@ -28,7 +28,8 @@ import java.io.File /** TODO: replace by [org.jetbrains.kotlin.caches.resolve.JsResolverForModuleFactory] after fix of KT-40734 */ internal class DokkaJsResolverForModuleFactory( - private val targetEnvironment: TargetEnvironment + private val targetEnvironment: TargetEnvironment, + private val kLibService: KLibService ) : ResolverForModuleFactory() { companion object { private val metadataFactories = KlibMetadataFactories({ DefaultBuiltIns.Instance }, DynamicTypeDeserializer) @@ -86,16 +87,18 @@ internal class DokkaJsResolverForModuleFactory( languageVersionSettings: LanguageVersionSettings ): List<PackageFragmentProvider> = when (moduleInfo) { is DokkaJsKlibLibraryInfo -> { - listOfNotNull( - moduleInfo.kotlinLibrary - .createKlibPackageFragmentProvider( - storageManager = moduleContext.storageManager, - metadataModuleDescriptorFactory = metadataModuleDescriptorFactory, - languageVersionSettings = languageVersionSettings, - moduleDescriptor = moduleDescriptor, - lookupTracker = LookupTracker.DO_NOTHING - ) - ) + with(kLibService) { + listOfNotNull( + moduleInfo.kotlinLibrary + .createPackageFragmentProvider( + storageManager = moduleContext.storageManager, + metadataModuleDescriptorFactory = metadataModuleDescriptorFactory, + languageVersionSettings = languageVersionSettings, + moduleDescriptor = moduleDescriptor, + lookupTracker = LookupTracker.DO_NOTHING + ) + ) + } } is LibraryModuleInfo -> { moduleInfo.getLibraryRoots() @@ -119,4 +122,4 @@ internal class DokkaJsResolverForModuleFactory( } else -> emptyList() } -}
\ No newline at end of file +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaKlibLibraryDependencyResolver.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaKlibLibraryDependencyResolver.kt index 9259deff..a07bdc35 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaKlibLibraryDependencyResolver.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaKlibLibraryDependencyResolver.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve import org.jetbrains.kotlin.library.uniqueName import org.jetbrains.kotlin.library.unresolvedDependencies diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaKlibLibraryInfo.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaKlibLibraryInfo.kt index e4e12b66..3362633d 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaKlibLibraryInfo.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaKlibLibraryInfo.kt @@ -1,10 +1,10 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve import org.jetbrains.kotlin.analyzer.LibraryModuleInfo import org.jetbrains.kotlin.library.KotlinLibrary -abstract class DokkaKlibLibraryInfo : LibraryModuleInfo { +internal abstract class DokkaKlibLibraryInfo : LibraryModuleInfo { abstract val kotlinLibrary: KotlinLibrary internal val libraryRoot: String get() = kotlinLibrary.libraryFile.path -}
\ No newline at end of file +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaKlibMetadataCommonDependencyContainer.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaKlibMetadataCommonDependencyContainer.kt index 1a987a1f..95f5486d 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaKlibMetadataCommonDependencyContainer.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaKlibMetadataCommonDependencyContainer.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer @@ -20,12 +20,11 @@ import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration import org.jetbrains.kotlin.serialization.konan.impl.KlibMetadataModuleDescriptorFactoryImpl import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.StorageManager -import org.jetbrains.kotlin.utils.keysToMap /** * Adapted from org.jetbrains.kotlin.cli.metadata.KlibMetadataDependencyContainer */ -class DokkaKlibMetadataCommonDependencyContainer( +internal class DokkaKlibMetadataCommonDependencyContainer( kotlinLibraries: List<KotlinLibrary>, private val configuration: CompilerConfiguration, private val storageManager: StorageManager @@ -41,7 +40,7 @@ class DokkaKlibMetadataCommonDependencyContainer( private val mutableDependenciesForAllModules = mutableListOf<ModuleInfo>() private val moduleDescriptorsForKotlinLibraries: Map<KotlinLibrary, ModuleDescriptorImpl> = - kotlinLibraries.keysToMap { library -> + kotlinLibraries.associateBy({ it }) { library -> val moduleHeader = parseModuleHeader(library.moduleHeaderData) val moduleName = Name.special(moduleHeader.moduleName) val moduleOrigin = DeserializedKlibModuleOrigin(library) @@ -137,4 +136,4 @@ private val MetadataFactories = { DefaultBuiltIns.Instance }, NullFlexibleTypeDeserializer, NativeTypeTransformer() - )
\ No newline at end of file + ) diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaNativeKlibLibraryInfo.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaNativeKlibLibraryInfo.kt index e2b388a9..526815d3 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaNativeKlibLibraryInfo.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaNativeKlibLibraryInfo.kt @@ -1,10 +1,9 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.descriptors.ModuleCapability import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin -import org.jetbrains.kotlin.idea.klib.safeRead import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.isInterop import org.jetbrains.kotlin.library.shortName @@ -14,6 +13,7 @@ import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.konan.NativePlatforms import org.jetbrains.kotlin.resolve.ImplicitIntegerCoercion import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices +import java.io.IOException /** TODO: replace by [NativeKlibLibraryInfo] after fix of KT-40734 */ internal class DokkaNativeKlibLibraryInfo( @@ -41,4 +41,10 @@ internal class DokkaNativeKlibLibraryInfo( capabilities[ImplicitIntegerCoercion.MODULE_CAPABILITY] = kotlinLibrary.safeRead(false) { isInterop } return capabilities } + + private fun <T> KotlinLibrary.safeRead(defaultValue: T, action: KotlinLibrary.() -> T) = try { + action() + } catch (_: IOException) { + defaultValue + } } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaNativeResolverForModuleFactory.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaNativeResolverForModuleFactory.kt index 0114f1ac..db86b82f 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/resolve/DokkaNativeResolverForModuleFactory.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/configuration/resolve/DokkaNativeResolverForModuleFactory.kt @@ -1,5 +1,6 @@ -package org.jetbrains.dokka.analysis.resolve +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.resolve +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KLibService import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.config.LanguageVersionSettings @@ -8,7 +9,6 @@ import org.jetbrains.kotlin.context.ModuleContext import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.frontend.di.createContainerForLazyResolve -import org.jetbrains.kotlin.idea.klib.createKlibPackageFragmentProvider import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.konan.util.KlibMetadataFactories import org.jetbrains.kotlin.library.metadata.NullFlexibleTypeDeserializer @@ -21,7 +21,8 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory /** TODO: replace by [NativeResolverForModuleFactory] after fix of KT-40734 */ internal class DokkaNativeResolverForModuleFactory( - private val targetEnvironment: TargetEnvironment + private val targetEnvironment: TargetEnvironment, + private val kLibService: KLibService, ) : ResolverForModuleFactory() { companion object { private val metadataFactories = KlibMetadataFactories(::KonanBuiltIns, NullFlexibleTypeDeserializer) @@ -56,15 +57,17 @@ internal class DokkaNativeResolverForModuleFactory( var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider - val klibPackageFragmentProvider = (moduleContent.moduleInfo as? DokkaNativeKlibLibraryInfo) - ?.kotlinLibrary - ?.createKlibPackageFragmentProvider( - storageManager = moduleContext.storageManager, - metadataModuleDescriptorFactory = metadataFactories.DefaultDeserializedDescriptorFactory, - languageVersionSettings = languageVersionSettings, - moduleDescriptor = moduleDescriptor, - lookupTracker = LookupTracker.DO_NOTHING - ) + val klibPackageFragmentProvider = with(kLibService) { + (moduleContent.moduleInfo as? DokkaNativeKlibLibraryInfo) + ?.kotlinLibrary + ?.createPackageFragmentProvider( + storageManager = moduleContext.storageManager, + metadataModuleDescriptorFactory = metadataFactories.DefaultDeserializedDescriptorFactory, + languageVersionSettings = languageVersionSettings, + moduleDescriptor = moduleDescriptor, + lookupTracker = LookupTracker.DO_NOTHING + ) + } if (klibPackageFragmentProvider != null) { packageFragmentProvider = diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/DefaultSamplesTransformer.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DefaultSamplesTransformer.kt index 49ddd0a5..22fecdf8 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/DefaultSamplesTransformer.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DefaultSamplesTransformer.kt @@ -1,13 +1,12 @@ -package org.jetbrains.dokka.base.transformers.pages.samples +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl import com.intellij.psi.PsiElement import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtDeclarationWithBody import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -class DefaultSamplesTransformer(context: DokkaContext) : SamplesTransformer(context) { +internal class DefaultSamplesTransformer(context: DokkaContext) : SamplesTransformerImpl(context) { override fun processBody(psiElement: PsiElement): String { val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() @@ -28,9 +27,9 @@ class DefaultSamplesTransformer(context: DokkaContext) : SamplesTransformer(cont override fun processImports(psiElement: PsiElement): String { val psiFile = psiElement.containingFile - return when(val text = psiFile.safeAs<KtFile>()?.importList?.text) { + return when(val text = (psiFile as? KtFile)?.importList?.text) { is String -> text else -> "" } } -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/utils/FullClassHierarchyBuilder.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorFullClassHierarchyBuilder.kt index d657fa32..13645762 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/utils/FullClassHierarchyBuilder.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorFullClassHierarchyBuilder.kt @@ -1,26 +1,27 @@ -package org.jetbrains.dokka.base.transformers.documentables.utils +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl import com.intellij.psi.PsiClass -import kotlinx.coroutines.* -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource -import org.jetbrains.dokka.analysis.PsiDocumentableSource -import org.jetbrains.dokka.analysis.from +import kotlinx.coroutines.coroutineScope +import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.DescriptorDocumentableSource +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.from import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.utilities.parallelForEach +import org.jetbrains.kotlin.analysis.kotlin.internal.ClassHierarchy +import org.jetbrains.kotlin.analysis.kotlin.internal.FullClassHierarchyBuilder +import org.jetbrains.kotlin.analysis.kotlin.internal.Supertypes import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import java.util.concurrent.ConcurrentHashMap -typealias Supertypes = List<DRI> -typealias ClassHierarchy = SourceSetDependent<Map<DRI, Supertypes>> +internal class DescriptorFullClassHierarchyBuilder : FullClassHierarchyBuilder { -class FullClassHierarchyBuilder { - suspend operator fun invoke(original: DModule): ClassHierarchy = coroutineScope { - val map = original.sourceSets.associateWith { ConcurrentHashMap<DRI, List<DRI>>() } - original.packages.parallelForEach { visitDocumentable(it, map) } + override suspend fun build(module: DModule): ClassHierarchy = coroutineScope { + val map = module.sourceSets.associateWith { ConcurrentHashMap<DRI, List<DRI>>() } + module.packages.parallelForEach { visitDocumentable(it, map) } map } @@ -81,4 +82,4 @@ class FullClassHierarchyBuilder { } } } -}
\ No newline at end of file +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorInheritanceBuilder.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorInheritanceBuilder.kt new file mode 100644 index 00000000..15dd8f1d --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorInheritanceBuilder.kt @@ -0,0 +1,91 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl + +import com.intellij.psi.PsiClass +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.java.util.PsiDocumentableSource +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.DescriptorDocumentableSource +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.from +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithSources +import org.jetbrains.kotlin.analysis.kotlin.internal.InheritanceBuilder +import org.jetbrains.kotlin.analysis.kotlin.internal.InheritanceNode +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType + +internal class DescriptorInheritanceBuilder : InheritanceBuilder { + + override fun build(documentables: Map<DRI, Documentable>): List<InheritanceNode> { + val descriptorMap = getDescriptorMap(documentables) + + val psiInheritanceTree = + documentables.flatMap { (_, v) -> (v as? WithSources)?.sources?.values.orEmpty() } + .filterIsInstance<PsiDocumentableSource>().mapNotNull { it.psi as? PsiClass } + .flatMap(::gatherPsiClasses) + .flatMap { entry -> entry.second.map { it to entry.first } } + .let { + it + it.map { it.second to null } + } + .groupBy({ it.first }) { it.second } + .map { it.key to it.value.filterNotNull().distinct() } + .map { (k, v) -> + InheritanceNode( + DRI.from(k), + v.map { InheritanceNode(DRI.from(it)) }, + k.supers.filter { it.isInterface }.map { DRI.from(it) }, + k.isInterface + ) + + } + + val descriptorInheritanceTree = descriptorMap.flatMap { (_, v) -> + v.typeConstructor.supertypes + .map { getClassDescriptorForType(it) to v } + } + .let { + it + it.map { it.second to null } + } + .groupBy({ it.first }) { it.second } + .map { it.key to it.value.filterNotNull().distinct() } + .map { (k, v) -> + InheritanceNode( + DRI.from(k), + v.map { InheritanceNode(DRI.from(it)) }, + k.typeConstructor.supertypes.map { getClassDescriptorForType(it) } + .mapNotNull { cd -> cd.takeIf { it.kind == ClassKind.INTERFACE }?.let { DRI.from(it) } }, + isInterface = k.kind == ClassKind.INTERFACE + ) + } + + return psiInheritanceTree + descriptorInheritanceTree + } + + private fun gatherPsiClasses(psi: PsiClass): List<Pair<PsiClass, List<PsiClass>>> = psi.supers.toList().let { l -> + listOf(psi to l) + l.flatMap { gatherPsiClasses(it) } + } + + private fun getDescriptorMap(documentables: Map<DRI, Documentable>): Map<DRI, ClassDescriptor> { + val map: MutableMap<DRI, ClassDescriptor> = mutableMapOf() + documentables + .mapNotNull { (k, v) -> + v.descriptorForPlatform()?.let { k to it }?.also { (k, v) -> map[k] = v } + }.map { it.second }.forEach { gatherSupertypes(it, map) } + + return map.toMap() + } + + private fun gatherSupertypes(descriptor: ClassDescriptor, map: MutableMap<DRI, ClassDescriptor>) { + map.putIfAbsent(DRI.from(descriptor), descriptor) + descriptor.typeConstructor.supertypes.map { getClassDescriptorForType(it) } + .forEach { gatherSupertypes(it, map) } + } + + + private fun Documentable?.descriptorForPlatform(platform: Platform = Platform.jvm) = + (this as? WithSources).descriptorForPlatform(platform) + + private fun WithSources?.descriptorForPlatform(platform: Platform = Platform.jvm) = this?.let { + it.sources.entries.find { it.key.analysisPlatform == platform }?.value?.let { it as? DescriptorDocumentableSource }?.descriptor as? ClassDescriptor + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorKotlinToJavaMapper.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorKotlinToJavaMapper.kt new file mode 100644 index 00000000..394a9863 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorKotlinToJavaMapper.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.kotlin.analysis.kotlin.internal.KotlinToJavaService +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName + +internal class DescriptorKotlinToJavaMapper : KotlinToJavaService { + + override fun findAsJava(kotlinDri: DRI): DRI? { + return kotlinDri.partialFqName().mapToJava()?.toDRI(kotlinDri) + } + + private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames + + private fun String.mapToJava(): ClassId? = + JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) + + private fun ClassId.toDRI(dri: DRI?): DRI = DRI( + packageName = packageFqName.asString(), + classNames = classNames(), + callable = dri?.callable,//?.asJava(), TODO: check this + extra = null, + target = PointingToDeclaration + ) + + private fun ClassId.classNames(): String = + shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorSyntheticDocumentableDetector.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorSyntheticDocumentableDetector.kt new file mode 100644 index 00000000..34f9bba1 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/DescriptorSyntheticDocumentableDetector.kt @@ -0,0 +1,33 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.DescriptorDocumentableSource +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithSources +import org.jetbrains.kotlin.analysis.kotlin.internal.SyntheticDocumentableDetector +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor + +internal class DescriptorSyntheticDocumentableDetector : SyntheticDocumentableDetector { + override fun isSynthetic(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean { + return isFakeOverride(documentable, sourceSet) || isSynthesized(documentable, sourceSet) + } + + private fun isFakeOverride(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean { + return callableMemberDescriptorOrNull(documentable, sourceSet)?.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE + } + + private fun isSynthesized(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean { + return callableMemberDescriptorOrNull(documentable, sourceSet)?.kind == CallableMemberDescriptor.Kind.SYNTHESIZED + } + + private fun callableMemberDescriptorOrNull( + documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet + ): CallableMemberDescriptor? { + if (documentable is WithSources) { + return documentable.sources[sourceSet] + .let { it as? DescriptorDocumentableSource }?.descriptor as? CallableMemberDescriptor + } + + return null + } +} diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/SamplesTransformerImpl.kt index e72700e0..f1924708 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/SamplesTransformerImpl.kt @@ -1,12 +1,13 @@ -package org.jetbrains.dokka.base.transformers.pages.samples +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl import com.intellij.psi.PsiElement import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.* -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.renderers.sourceSets +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.KotlinAnalysis +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.SamplesKotlinAnalysis import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DisplaySourceSet import org.jetbrains.dokka.model.doc.Sample @@ -16,13 +17,15 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.transformers.pages.PageTransformer -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.lazy.ResolveSession -internal const val KOTLIN_PLAYGROUND_SCRIPT = "<script src=\"https://unpkg.com/kotlin-playground@1\"></script>" -abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { +internal const val KOTLIN_PLAYGROUND_SCRIPT = "<script src=\"`https://unpkg.com/kotlin-playground@1`\"></script>" + +internal abstract class SamplesTransformerImpl(val context: DokkaContext) : PageTransformer { + + private val kDocFinder: KDocFinder = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kdocFinder } abstract fun processBody(psiElement: PsiElement): String abstract fun processImports(psiElement: PsiElement): String @@ -36,8 +39,8 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { runBlocking(Dispatchers.Default) { val analysis = SamplesKotlinAnalysis( sourceSets = context.configuration.sourceSets, - logger = context.logger, - projectKotlinAnalysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } + context = context, + projectKotlinAnalysis = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kotlinAnalysis } ) analysis.use { input.transformContentPagesTree { page -> @@ -63,13 +66,13 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { fqName: String, analysis: KotlinAnalysis ): ContentNode { - val facade = analysis[sourceSet].facade - val psiElement = fqNameToPsiElement(facade, fqName) + val resolveSession = analysis[sourceSet].resolveSession + val psiElement = fqNameToPsiElement(resolveSession, fqName, sourceSet) ?: return this.also { context.logger.warn("Cannot find PsiElement corresponding to $fqName") } val imports = processImports(psiElement) val body = processBody(psiElement) - val node = contentCode(contentPage.sourceSets(), contentPage.dri, createSampleBody(imports, body), "kotlin") + val node = contentCode(contentPage.content.sourceSets, contentPage.dri, createSampleBody(imports, body), "kotlin") return dfs(fqName, node) } @@ -107,18 +110,20 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { } } - private fun fqNameToPsiElement(resolutionFacade: DokkaResolutionFacade, functionName: String): PsiElement? { + private fun fqNameToPsiElement(resolveSession: ResolveSession, functionName: String, dokkaSourceSet: DokkaSourceSet): PsiElement? { val packageName = functionName.takeWhile { it != '.' } - val descriptor = resolutionFacade.resolveSession.getPackageFragment(FqName(packageName)) + val descriptor = resolveSession.getPackageFragment(FqName(packageName)) ?: return null.also { context.logger.warn("Cannot find descriptor for package $packageName") } - val symbol = resolveKDocLink( - BindingContext.EMPTY, - resolutionFacade, - descriptor, - null, - functionName.split(".") - ).firstOrNull() ?: return null.also { context.logger.warn("Unresolved function $functionName in @sample") } - return DescriptorToSourceUtils.descriptorToDeclaration(symbol) + + with (kDocFinder) { + val symbol = resolveKDocLink( + descriptor, + functionName, + dokkaSourceSet, + emptyBindingContext = true + ).firstOrNull() ?: return null.also { context.logger.warn("Unresolved function $functionName in @sample") } + return DescriptorToSourceUtils.descriptorToDeclaration(symbol) + } } private fun contentCode( diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/IllegalModuleAndPackageDocumentation.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/IllegalModuleAndPackageDocumentation.kt index f642c374..0d5fe5c5 100644 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/IllegalModuleAndPackageDocumentation.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/IllegalModuleAndPackageDocumentation.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs import org.jetbrains.dokka.DokkaException diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentation.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentation.kt index ee67fad1..0aaea9c8 100644 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentation.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentation.kt @@ -1,8 +1,8 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs import org.jetbrains.dokka.model.doc.DocumentationNode -data class ModuleAndPackageDocumentation( +internal data class ModuleAndPackageDocumentation( val name: String, val classifier: Classifier, val documentation: DocumentationNode diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationFragment.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationFragment.kt new file mode 100644 index 00000000..c0df713b --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationFragment.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs + + +internal data class ModuleAndPackageDocumentationFragment( + val name: String, + val classifier: ModuleAndPackageDocumentation.Classifier, + val documentation: String, + val source: ModuleAndPackageDocumentationSource +) diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContext.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationParsingContext.kt index fa6c653e..f6ce66d6 100644 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationParsingContext.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationParsingContext.kt @@ -1,21 +1,22 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.parsers.MarkdownParser -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.Module -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.Package +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.from +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentation.Classifier.Module +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentation.Classifier.Package +import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -fun interface ModuleAndPackageDocumentationParsingContext { +internal fun interface ModuleAndPackageDocumentationParsingContext { fun markdownParserFor(fragment: ModuleAndPackageDocumentationFragment, location: String): MarkdownParser } @@ -25,25 +26,31 @@ internal fun ModuleAndPackageDocumentationParsingContext.parse( return markdownParserFor(fragment, fragment.source.sourceDescription).parse(fragment.documentation) } -fun ModuleAndPackageDocumentationParsingContext( +internal fun ModuleAndPackageDocumentationParsingContext( logger: DokkaLogger, - facade: DokkaResolutionFacade? = null + moduleDescriptor: ModuleDescriptor? = null, + kDocFinder: KDocFinder? = null, + sourceSet: DokkaConfiguration.DokkaSourceSet? = null ) = ModuleAndPackageDocumentationParsingContext { fragment, sourceLocation -> val descriptor = when (fragment.classifier) { - Module -> facade?.moduleDescriptor?.getPackage(FqName.topLevel(Name.identifier(""))) - Package -> facade?.moduleDescriptor?.getPackage(FqName(fragment.name)) + Module -> moduleDescriptor?.getPackage(FqName.topLevel(Name.identifier(""))) + Package -> moduleDescriptor?.getPackage(FqName(fragment.name)) } val externalDri = { link: String -> try { - if (facade != null && descriptor != null) { - resolveKDocLink( - facade.resolveSession.bindingContext, - facade, - descriptor, - null, - link.split('.') - ).sorted().firstOrNull()?.let { DRI.from(it) } + if (kDocFinder != null && descriptor != null && sourceSet != null) { + with(kDocFinder) { + resolveKDocLink( + descriptor, + link, + sourceSet + ).sorted().firstOrNull()?.let { + DRI.from( + it + ) + } + } } else null } catch (e1: IllegalArgumentException) { logger.warn("Couldn't resolve link for $link") diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationReader.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationReader.kt index faf94db2..66bbf1a8 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationReader.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationReader.kt @@ -1,13 +1,10 @@ -package org.jetbrains.dokka.base.transformers.documentables +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.analysis.KotlinAnalysis -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentationFragment -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentationParsingContext -import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentation -import org.jetbrains.dokka.base.parsers.moduleAndPackage.parseModuleAndPackageDocumentationFragments +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.KotlinAnalysis +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentation.Classifier import org.jetbrains.dokka.model.DModule import org.jetbrains.dokka.model.DPackage import org.jetbrains.dokka.model.SourceSetDependent @@ -17,11 +14,7 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.utilities.associateWithNotNull - -internal interface ModuleAndPackageDocumentationReader { - operator fun get(module: DModule): SourceSetDependent<DocumentationNode> - operator fun get(pkg: DPackage): SourceSetDependent<DocumentationNode> -} +import org.jetbrains.kotlin.analysis.kotlin.internal.ModuleAndPackageDocumentationReader internal fun ModuleAndPackageDocumentationReader(context: DokkaContext): ModuleAndPackageDocumentationReader = ContextModuleAndPackageDocumentationReader(context) @@ -30,7 +23,8 @@ private class ContextModuleAndPackageDocumentationReader( private val context: DokkaContext ) : ModuleAndPackageDocumentationReader { - private val kotlinAnalysis: KotlinAnalysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } + private val kotlinAnalysis: KotlinAnalysis = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kotlinAnalysis } + private val kdocFinder: KDocFinder = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kdocFinder } private val documentationFragments: SourceSetDependent<List<ModuleAndPackageDocumentationFragment>> = context.configuration.sourceSets.associateWith { sourceSet -> @@ -43,10 +37,10 @@ private class ContextModuleAndPackageDocumentationReader( ): SourceSetDependent<DocumentationNode> { return sourceSets.associateWithNotNull { sourceSet -> val fragments = documentationFragments[sourceSet].orEmpty().filter(predicate) - val resolutionFacade = kotlinAnalysis[sourceSet].facade + val moduleDescriptor = kotlinAnalysis[sourceSet].moduleDescriptor val documentations = fragments.map { fragment -> parseModuleAndPackageDocumentation( - context = ModuleAndPackageDocumentationParsingContext(context.logger, resolutionFacade), + context = ModuleAndPackageDocumentationParsingContext(context.logger, moduleDescriptor, kdocFinder, sourceSet), fragment = fragment ) } @@ -66,18 +60,30 @@ private class ContextModuleAndPackageDocumentationReader( return name } - override fun get(module: DModule): SourceSetDependent<DocumentationNode> { + override fun read(module: DModule): SourceSetDependent<DocumentationNode> { return findDocumentationNodes(module.sourceSets) { fragment -> fragment.classifier == Classifier.Module && (fragment.name == module.name) } } - override fun get(pkg: DPackage): SourceSetDependent<DocumentationNode> { + override fun read(pkg: DPackage): SourceSetDependent<DocumentationNode> { return findDocumentationNodes(pkg.sourceSets) { fragment -> fragment.classifier == Classifier.Package && fragment.canonicalPackageName == pkg.dri.packageName } } + override fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode? { + val parsingContext = ModuleAndPackageDocumentationParsingContext(context.logger) + + val documentationFragment = module.includes + .flatMap { include -> parseModuleAndPackageDocumentationFragments(include) } + .firstOrNull { fragment -> fragment.classifier == Classifier.Module && fragment.name == module.name } + ?: return null + + val moduleDocumentation = parseModuleAndPackageDocumentation(parsingContext, documentationFragment) + return moduleDocumentation.documentation + } + private fun List<TagWrapper>.mergeDocumentationNodes(): List<TagWrapper> = groupBy { it::class }.values.map { it.reduce { acc, tagWrapper -> diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationSource.kt index 9514adb4..18105be0 100644 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/ModuleAndPackageDocumentationSource.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/ModuleAndPackageDocumentationSource.kt @@ -1,8 +1,8 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs import java.io.File -abstract class ModuleAndPackageDocumentationSource { +internal abstract class ModuleAndPackageDocumentationSource { abstract val sourceDescription: String abstract val documentation: String override fun toString(): String = sourceDescription diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/parseModuleAndPackageDocumentation.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/parseModuleAndPackageDocumentation.kt index db342042..59b7d2e9 100644 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/parseModuleAndPackageDocumentation.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/parseModuleAndPackageDocumentation.kt @@ -1,6 +1,6 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs -fun parseModuleAndPackageDocumentation( +internal fun parseModuleAndPackageDocumentation( context: ModuleAndPackageDocumentationParsingContext, fragment: ModuleAndPackageDocumentationFragment ): ModuleAndPackageDocumentation { diff --git a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/parseModuleAndPackageDocumentationFragments.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/parseModuleAndPackageDocumentationFragments.kt index d3381901..32f636ff 100644 --- a/plugins/base/src/main/kotlin/parsers/moduleAndPackage/parseModuleAndPackageDocumentationFragments.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/impl/moduledocs/parseModuleAndPackageDocumentationFragments.kt @@ -1,14 +1,14 @@ -package org.jetbrains.dokka.base.parsers.moduleAndPackage +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.* +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentation.Classifier.Module +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentation.Classifier.Package import java.io.File - -fun parseModuleAndPackageDocumentationFragments(source: File): List<ModuleAndPackageDocumentationFragment> { +internal fun parseModuleAndPackageDocumentationFragments(source: File): List<ModuleAndPackageDocumentationFragment> { return parseModuleAndPackageDocumentationFragments(ModuleAndPackageDocumentationFile(source)) } -fun parseModuleAndPackageDocumentationFragments( +internal fun parseModuleAndPackageDocumentationFragments( source: ModuleAndPackageDocumentationSource ): List<ModuleAndPackageDocumentationFragment> { val fragmentStrings = source.documentation.split(Regex("(|^)#\\s*(?=(Module|Package))")) diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorDocumentationContent.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorDocumentationContent.kt new file mode 100644 index 00000000..5adf1194 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorDocumentationContent.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent +import org.jetbrains.dokka.analysis.java.JavadocTag +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag + +internal data class DescriptorDocumentationContent( + val descriptor: DeclarationDescriptor, + val element: KDocTag, + override val tag: JavadocTag, +) : DocumentationContent { + override fun resolveSiblings(): List<DocumentationContent> { + return listOf(this) + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocComment.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocComment.kt new file mode 100644 index 00000000..da7d5140 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocComment.kt @@ -0,0 +1,79 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import org.jetbrains.dokka.analysis.java.* +import org.jetbrains.dokka.analysis.java.doccomment.DocComment +import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag + +internal class DescriptorKotlinDocComment( + val comment: KDocTag, + val descriptor: DeclarationDescriptor +) : DocComment { + + private val tagsWithContent: List<KDocTag> = comment.children.mapNotNull { (it as? KDocTag) } + + override fun hasTag(tag: JavadocTag): Boolean { + return when (tag) { + is DescriptionJavadocTag -> comment.getContent().isNotEmpty() + is ThrowingExceptionJavadocTag -> tagsWithContent.any { it.hasException(tag) } + else -> tagsWithContent.any { it.text.startsWith("@${tag.name}") } + } + } + + private fun KDocTag.hasException(tag: ThrowingExceptionJavadocTag) = + text.startsWith("@${tag.name}") && getSubjectName() == tag.exceptionQualifiedName + + override fun resolveTag(tag: JavadocTag): List<DocumentationContent> { + return when (tag) { + is DescriptionJavadocTag -> listOf(DescriptorDocumentationContent(descriptor, comment, tag)) + is ParamJavadocTag -> { + val resolvedContent = resolveGeneric(tag) + listOf(resolvedContent[tag.paramIndex]) + } + + is ThrowsJavadocTag -> resolveThrowingException(tag) + is ExceptionJavadocTag -> resolveThrowingException(tag) + else -> resolveGeneric(tag) + } + } + + private fun resolveThrowingException(tag: ThrowingExceptionJavadocTag): List<DescriptorDocumentationContent> { + val exceptionName = tag.exceptionQualifiedName ?: return resolveGeneric(tag) + + return comment.children + .filterIsInstance<KDocTag>() + .filter { it.name == tag.name && it.getSubjectName() == exceptionName } + .map { DescriptorDocumentationContent(descriptor, it, tag) } + } + + private fun resolveGeneric(tag: JavadocTag): List<DescriptorDocumentationContent> { + return comment.children.mapNotNull { element -> + if (element is KDocTag && element.name == tag.name) { + DescriptorDocumentationContent(descriptor, element, tag) + } else { + null + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DescriptorKotlinDocComment + + if (comment != other.comment) return false + if (descriptor.name != other.descriptor.name) return false + if (tagsWithContent != other.tagsWithContent) return false + + return true + } + + override fun hashCode(): Int { + var result = comment.hashCode() + result = 31 * result + descriptor.name.hashCode() + result = 31 * result + tagsWithContent.hashCode() + return result + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocCommentCreator.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocCommentCreator.kt new file mode 100644 index 00000000..b191355d --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocCommentCreator.kt @@ -0,0 +1,26 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import com.intellij.psi.PsiNamedElement +import org.jetbrains.dokka.analysis.java.doccomment.DocComment +import org.jetbrains.dokka.analysis.java.doccomment.DocCommentCreator +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.DescriptorFinder +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtElement + +internal class DescriptorKotlinDocCommentCreator( + private val kdocFinder: KDocFinder, + private val descriptorFinder: DescriptorFinder +) : DocCommentCreator { + override fun create(element: PsiNamedElement): DocComment? { + val ktElement = element.navigationElement as? KtElement ?: return null + val kdoc = with (kdocFinder) { + ktElement.findKDoc() + } ?: return null + val descriptor = with (descriptorFinder) { + (element.navigationElement as? KtDeclaration)?.findDescriptor() + } ?: return null + + return DescriptorKotlinDocComment(kdoc, descriptor) + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocCommentParser.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocCommentParser.kt new file mode 100644 index 00000000..28565f2d --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/DescriptorKotlinDocCommentParser.kt @@ -0,0 +1,54 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import com.intellij.psi.PsiNamedElement +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.java.doccomment.DocComment +import org.jetbrains.dokka.analysis.java.parsers.DocCommentParser +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.from +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator.parseFromKDocTag +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.utilities.DokkaLogger + +internal class DescriptorKotlinDocCommentParser( + private val context: DokkaContext, + private val logger: DokkaLogger +) : DocCommentParser { + + override fun canParse(docComment: DocComment): Boolean { + return docComment is DescriptorKotlinDocComment + } + + override fun parse(docComment: DocComment, context: PsiNamedElement): DocumentationNode { + val kotlinDocComment = docComment as DescriptorKotlinDocComment + return parseDocumentation(kotlinDocComment) + } + + fun parseDocumentation(element: DescriptorKotlinDocComment, parseWithChildren: Boolean = true): DocumentationNode { + val sourceSet = context.configuration.sourceSets.let { sourceSets -> + sourceSets.firstOrNull { it.sourceSetID.sourceSetName == "jvmMain" } + ?: sourceSets.first { it.analysisPlatform == Platform.jvm } + } + val kdocFinder = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kdocFinder } + return parseFromKDocTag( + kDocTag = element.comment, + externalDri = { link: String -> + try { + kdocFinder.resolveKDocLink(element.descriptor, link, sourceSet) + .firstOrNull() + ?.let { DRI.from(it) } + } catch (e1: IllegalArgumentException) { + logger.warn("Couldn't resolve link for $link") + null + } + }, + kdocLocation = null, + parseWithChildren = parseWithChildren + ) + } +} + diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinAnalysisProjectProvider.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinAnalysisProjectProvider.kt new file mode 100644 index 00000000..72151a72 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinAnalysisProjectProvider.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import com.intellij.openapi.project.Project +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.java.ProjectProvider +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle + +internal class KotlinAnalysisProjectProvider : ProjectProvider { + override fun getProject(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): Project { + val kotlinAnalysis = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kotlinAnalysis } + return kotlinAnalysis[sourceSet].project + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinAnalysisSourceRootsExtractor.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinAnalysisSourceRootsExtractor.kt new file mode 100644 index 00000000..ed6b8c53 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinAnalysisSourceRootsExtractor.kt @@ -0,0 +1,27 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.java.SourceRootsExtractor +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import java.io.File + +internal class KotlinAnalysisSourceRootsExtractor : SourceRootsExtractor { + + override fun extract(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): List<File> { + val kotlinAnalysis = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kotlinAnalysis } + val environment = kotlinAnalysis[sourceSet].environment + return environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) + ?.filterIsInstance<JavaSourceRoot>() + ?.mapNotNull { it.file.takeIf { isFileInSourceRoots(it, sourceSet) } } + ?: listOf() + } + + private fun isFileInSourceRoots(file: File, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean = + sourceSet.sourceRoots.any { root -> file.startsWith(root) } + +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinInheritDocTagContentProvider.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinInheritDocTagContentProvider.kt new file mode 100644 index 00000000..3c92fc65 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/java/KotlinInheritDocTagContentProvider.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.java + +import org.jetbrains.dokka.analysis.java.doccomment.DocumentationContent +import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin +import org.jetbrains.dokka.analysis.java.parsers.doctag.DocTagParserContext +import org.jetbrains.dokka.analysis.java.parsers.doctag.InheritDocTagContentProvider +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query + +internal class KotlinInheritDocTagContentProvider( + context: DokkaContext +) : InheritDocTagContentProvider { + + val parser: DescriptorKotlinDocCommentParser by lazy { + context.plugin<JavaAnalysisPlugin>().query { docCommentParsers } + .single { it is DescriptorKotlinDocCommentParser } as DescriptorKotlinDocCommentParser + } + + override fun canConvert(content: DocumentationContent): Boolean = content is DescriptorDocumentationContent + + override fun convertToHtml(content: DocumentationContent, docTagParserContext: DocTagParserContext): String { + val descriptorContent = content as DescriptorDocumentationContent + val inheritedDocNode = parser.parseDocumentation( + DescriptorKotlinDocComment(descriptorContent.element, descriptorContent.descriptor), + parseWithChildren = false + ) + val id = docTagParserContext.store(inheritedDocNode) + return """<inheritdoc id="$id"/>""" + } +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/CollectionExtensions.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/CollectionExtensions.kt new file mode 100644 index 00000000..e1dec28c --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/CollectionExtensions.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator + +// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5 +internal inline 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 +} diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt index ce4776e7..7633a93f 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.base.translators.descriptors +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator import com.intellij.psi.PsiElement import com.intellij.psi.PsiNamedElement @@ -8,15 +8,14 @@ import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.analysis.DescriptorDocumentableSource -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.KotlinAnalysis -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.parsers.MarkdownParser -import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser -import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions -import org.jetbrains.dokka.base.translators.unquotedValue +import org.jetbrains.dokka.analysis.java.JavaAnalysisPlugin +import org.jetbrains.dokka.analysis.java.parsers.JavadocParser +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisContext +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.DescriptorDocumentableSource +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.KotlinAnalysis +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.from import org.jetbrains.dokka.links.* import org.jetbrains.dokka.links.Callable import org.jetbrains.dokka.model.* @@ -27,6 +26,7 @@ import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.properties.PropertyContainer 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.DokkaLogger @@ -43,8 +43,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.java.JavaVisibilities -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor import org.jetbrains.kotlin.load.kotlin.toSourceElement @@ -69,7 +67,6 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.nio.file.Paths import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue import org.jetbrains.kotlin.resolve.constants.ArrayValue as ConstantsArrayValue @@ -84,22 +81,30 @@ import org.jetbrains.kotlin.resolve.constants.NullValue as ConstantsNullValue import org.jetbrains.kotlin.resolve.constants.UIntValue as ConstantsUIntValue import org.jetbrains.kotlin.resolve.constants.ULongValue as ConstantsULongValue -class DefaultDescriptorToDocumentableTranslator( +internal class DefaultDescriptorToDocumentableTranslator( private val context: DokkaContext ) : AsyncSourceToDocumentableTranslator, ExternalClasslikesTranslator { - private val kotlinAnalysis: KotlinAnalysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } + private val kotlinAnalysis: KotlinAnalysis = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kotlinAnalysis } + private val kdocFinder: KDocFinder = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kdocFinder } override suspend fun invokeSuspending(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { - val (environment, facade) = kotlinAnalysis[sourceSet] + val analysisContext = kotlinAnalysis[sourceSet] + val environment = analysisContext.environment val packageFragments = environment.getSourceFiles().asSequence() .map { it.packageFqName } .distinct() - .mapNotNull { facade.resolveSession.getPackageFragment(it) } + .mapNotNull { analysisContext.resolveSession.getPackageFragment(it) } .toList() - return DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger).run { - packageFragments.mapNotNull { it.safeAs<PackageFragmentDescriptor>() }.parallelMap { + val javadocParser = JavadocParser( + docCommentParsers = context.plugin<JavaAnalysisPlugin>().query { docCommentParsers }, + docCommentFinder = context.plugin<JavaAnalysisPlugin>().docCommentFinder + ) + + + return DokkaDescriptorVisitor(sourceSet, kdocFinder, kotlinAnalysis[sourceSet], context.logger, javadocParser).run { + packageFragments.parallelMap { visitPackageFragmentDescriptor( it ) @@ -118,27 +123,33 @@ class DefaultDescriptorToDocumentableTranslator( override fun translateClassDescriptor(descriptor: ClassDescriptor, sourceSet: DokkaSourceSet): DClasslike { val driInfo = DRI.from(descriptor.parents.first()).withEmptyInfo() + val javadocParser = JavadocParser( + docCommentParsers = context.plugin<JavaAnalysisPlugin>().query { docCommentParsers }, + docCommentFinder = context.plugin<JavaAnalysisPlugin>().docCommentFinder + ) + return runBlocking(Dispatchers.Default) { - DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger) + DokkaDescriptorVisitor(sourceSet, kdocFinder, kotlinAnalysis[sourceSet], context.logger, javadocParser) .visitClassDescriptor(descriptor, driInfo) } } } -data class DRIWithPlatformInfo( +internal data class DRIWithPlatformInfo( val dri: DRI, val actual: SourceSetDependent<DocumentableSource> ) -fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, emptyMap()) +internal fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, emptyMap()) private class DokkaDescriptorVisitor( private val sourceSet: DokkaSourceSet, - private val resolutionFacade: DokkaResolutionFacade, - private val logger: DokkaLogger + private val kDocFinder: KDocFinder, + private val analysisContext: AnalysisContext, + private val logger: DokkaLogger, + private val javadocParser: JavadocParser ) { - private val javadocParser = JavadocParser(logger, resolutionFacade) - private val syntheticDocProvider = SyntheticDescriptorDocumentationProvider(resolutionFacade) + private val syntheticDocProvider = SyntheticDescriptorDocumentationProvider(kDocFinder, sourceSet) private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter { it.toSourceElement.containingFile.toString().let { path -> @@ -512,6 +523,7 @@ private class DokkaDescriptorVisitor( descriptor.getDefaultValue()?.let { DefaultValue(it.toSourceSetDependent()) }, inheritedFrom?.let { InheritedMember(it.toSourceSetDependent()) }, takeIf { descriptor.isVar(getter, setter) }?.let { IsVar }, + takeIf { descriptor.findPsi() is KtParameter }?.let { IsAlsoParameter(listOf(sourceSet)) } ) ) ) @@ -959,7 +971,7 @@ private class DokkaDescriptorVisitor( private fun org.jetbrains.kotlin.descriptors.annotations.Annotations.getPresentableName(): String? = mapNotNull { it.toAnnotation() }.singleOrNull { it.dri.classNames == "ParameterName" }?.params?.get("name") - .safeAs<StringValue>()?.value?.let { unquotedValue(it) } + .let { it as? StringValue }?.value?.let { unquotedValue(it) } private suspend fun KotlinType.toBound(): Bound { suspend fun <T : AnnotationTarget> annotations(): PropertyContainer<T> = @@ -1026,30 +1038,41 @@ private class DokkaDescriptorVisitor( return effectiveReferencedDescriptors.firstOrNull()?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) } } - private fun DeclarationDescriptor.getDocumentation() = (findKDoc(::descriptorToAnyDeclaration)?.let { - MarkdownParser.parseFromKDocTag( - kDocTag = it, - externalDri = { link: String -> - try { - resolveKDocLink( - context = resolutionFacade.resolveSession.bindingContext, - resolutionFacade = resolutionFacade, - fromDescriptor = this, - fromSubjectOfTag = null, - qualifiedName = link.split('.') - ).firstOrNull()?.let { DRI.from(it) } - } catch (e1: IllegalArgumentException) { - logger.warn("Couldn't resolve link for $link") - null - } - }, - kdocLocation = toSourceElement.containingFile.name?.let { - val fqName = fqNameOrNull()?.asString() - if (fqName != null) "$it/$fqName" - else it - } - ) - } ?: getJavaDocs())?.takeIf { it.children.isNotEmpty() } + private fun DeclarationDescriptor.getDocumentation(): DocumentationNode? { + val find = with(kDocFinder) { + find(::descriptorToAnyDeclaration) + } + + return (find?.let { + parseFromKDocTag( + kDocTag = it, + externalDri = { link: String -> + try { + val kdocLink = with(kDocFinder) { + resolveKDocLink( + fromDescriptor = this@getDocumentation, + qualifiedName = link, + sourceSet = sourceSet + ) + } + kdocLink.firstOrNull()?.let { + DRI.from( + it + ) + } + } catch (e1: IllegalArgumentException) { + logger.warn("Couldn't resolve link for $link") + null + } + }, + kdocLocation = toSourceElement.containingFile.name?.let { + val fqName = fqNameOrNull()?.asString() + if (fqName != null) "$it/$fqName" + else it + } + ) + } ?: getJavaDocs())?.takeIf { it.children.isNotEmpty() } + } private fun DeclarationDescriptor.getJavaDocs(): DocumentationNode? { val overriddenDescriptors = (this as? CallableDescriptor)?.overriddenDescriptors ?: emptyList() @@ -1185,12 +1208,12 @@ private class DokkaDescriptorVisitor( ((source as? KotlinSourceElement)?.psi as? KtParameter)?.defaultValue?.toDefaultValueExpression() private fun PropertyDescriptor.getDefaultValue(): Expression? = - (source as? KotlinSourceElement)?.psi?.children?.filterIsInstance<KtConstantExpression>()?.firstOrNull() + (source as? KotlinSourceElement)?.psi?.children?.firstIsInstanceOrNull<KtConstantExpression>() ?.toDefaultValueExpression() private fun ClassDescriptor.getAppliedConstructorParameters() = (source as PsiSourceElement).psi?.children?.flatMap { - it.safeAs<KtInitializerList>()?.initializersAsExpression().orEmpty() + (it as? KtInitializerList)?.initializersAsExpression().orEmpty() }.orEmpty() private fun KtInitializerList.initializersAsExpression() = @@ -1239,7 +1262,9 @@ private class DokkaDescriptorVisitor( (source.containingFile as? PsiSourceFile)?.psiFile as? KtFile private suspend fun DeclarationDescriptorWithSource.fileLevelAnnotations() = ktFile() - ?.let { file -> resolutionFacade.resolveSession.getFileAnnotations(file) } + ?.let { file -> + analysisContext.resolveSession.getFileAnnotations(file) + } ?.toList() ?.parallelMap { it.toAnnotation(scope = Annotations.AnnotationScope.FILE) } .orEmpty() diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultExternalDocumentablesProvider.kt index 05982301..3c29b61d 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultExternalDocumentablesProvider.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultExternalDocumentablesProvider.kt @@ -1,12 +1,13 @@ -package org.jetbrains.dokka.base.translators.descriptors +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.DClasslike import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.querySingle import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.kotlin.analysis.kotlin.internal.ExternalDocumentablesProvider import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.PackageViewDescriptor @@ -14,16 +15,16 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -class DefaultExternalDocumentablesProvider(context: DokkaContext) : ExternalDocumentablesProvider { - private val analysis = context.plugin<DokkaBase>().querySingle { kotlinAnalysis } +internal class DefaultExternalDocumentablesProvider(context: DokkaContext) : ExternalDocumentablesProvider { + private val analysis = context.plugin<CompilerDescriptorAnalysisPlugin>().querySingle { kotlinAnalysis } - private val translator = context.plugin<DokkaBase>().querySingle { externalClasslikesTranslator } + private val translator: ExternalClasslikesTranslator = DefaultDescriptorToDocumentableTranslator(context) override fun findClasslike(dri: DRI, sourceSet: DokkaSourceSet): DClasslike? { val pkg = dri.packageName?.let { FqName(it) } ?: FqName.ROOT val names = dri.classNames?.split('.') ?: return null - val packageDsc = analysis[sourceSet].facade.moduleDescriptor.getPackage(pkg) + val packageDsc = analysis[sourceSet].moduleDescriptor.getPackage(pkg) val classDsc = names.fold<String, DeclarationDescriptor?>(packageDsc) { dsc, name -> dsc?.scope?.getDescriptorsFiltered { it.identifier == name } ?.filterIsInstance<ClassDescriptor>() @@ -39,4 +40,4 @@ class DefaultExternalDocumentablesProvider(context: DokkaContext) : ExternalDocu is ClassDescriptor -> unsubstitutedMemberScope else -> throw IllegalArgumentException("Unexpected type of descriptor: ${this::class}") } -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DescriptorAccessorConventionUtil.kt index 292dbfca..fcb0b83d 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DescriptorAccessorConventionUtil.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DescriptorAccessorConventionUtil.kt @@ -1,6 +1,5 @@ -package org.jetbrains.dokka.base.translators.descriptors +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator -import org.jetbrains.dokka.base.translators.firstNotNullOfOrNull import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.load.java.JvmAbi diff --git a/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/ExternalClasslikesTranslator.kt index a5385c46..0b4b4442 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/ExternalClasslikesTranslator.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/ExternalClasslikesTranslator.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.base.translators.descriptors +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.model.DClasslike @@ -7,6 +7,6 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor /** * Service translating [ClassDescriptor]s of symbols defined outside of documented project to [DClasslike]s. */ -fun interface ExternalClasslikesTranslator { +internal fun interface ExternalClasslikesTranslator { fun translateClassDescriptor(descriptor: ClassDescriptor, sourceSet: DokkaSourceSet): DClasslike -}
\ No newline at end of file +} diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/KdocMarkdownParser.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/KdocMarkdownParser.kt new file mode 100644 index 00000000..e47b9ba2 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/KdocMarkdownParser.kt @@ -0,0 +1,101 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator + +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser +import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser.Companion.fqDeclarationName +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.doc.Suppress +import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag + +internal fun parseFromKDocTag( + kDocTag: KDocTag?, + externalDri: (String) -> DRI?, + kdocLocation: String?, + parseWithChildren: Boolean = true +): DocumentationNode { + return if (kDocTag == null) { + DocumentationNode(emptyList()) + } else { + fun parseStringToDocNode(text: String) = + MarkdownParser(externalDri, kdocLocation).parseStringToDocNode(text) + + fun pointedLink(tag: KDocTag): DRI? = (parseStringToDocNode("[${tag.getSubjectName()}]")).let { + val link = it.children[0].children[0] + if (link is DocumentationLink) link.dri else null + } + + val allTags = + listOf(kDocTag) + if (kDocTag.canHaveParent() && parseWithChildren) getAllKDocTags(findParent(kDocTag)) else emptyList() + DocumentationNode( + allTags.map { + when (it.knownTag) { + null -> if (it.name == null) Description(parseStringToDocNode(it.getContent())) else CustomTagWrapper( + parseStringToDocNode(it.getContent()), + it.name!! + ) + KDocKnownTag.AUTHOR -> Author(parseStringToDocNode(it.getContent())) + KDocKnownTag.THROWS -> { + val dri = pointedLink(it) + Throws( + parseStringToDocNode(it.getContent()), + dri?.fqDeclarationName() ?: it.getSubjectName().orEmpty(), + dri, + ) + } + KDocKnownTag.EXCEPTION -> { + val dri = pointedLink(it) + Throws( + parseStringToDocNode(it.getContent()), + dri?.fqDeclarationName() ?: it.getSubjectName().orEmpty(), + dri + ) + } + KDocKnownTag.PARAM -> Param( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.RECEIVER -> Receiver(parseStringToDocNode(it.getContent())) + KDocKnownTag.RETURN -> Return(parseStringToDocNode(it.getContent())) + KDocKnownTag.SEE -> { + val dri = pointedLink(it) + See( + parseStringToDocNode(it.getContent()), + dri?.fqDeclarationName() ?: it.getSubjectName().orEmpty(), + dri, + ) + } + KDocKnownTag.SINCE -> Since(parseStringToDocNode(it.getContent())) + KDocKnownTag.CONSTRUCTOR -> Constructor(parseStringToDocNode(it.getContent())) + KDocKnownTag.PROPERTY -> Property( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.SAMPLE -> Sample( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.SUPPRESS -> Suppress(parseStringToDocNode(it.getContent())) + } + } + ) + } +} + +//Horrible hack but since link resolution is passed as a function i am not able to resolve them otherwise +@kotlin.Suppress("DeprecatedCallableAddReplaceWith") +@Deprecated("This function makes wrong assumptions and is missing a lot of corner cases related to generics, " + + "parameters and static members. This is not supposed to be public API and will not be supported in the future") +internal fun DRI.fqName(): String? = "$packageName.$classNames".takeIf { packageName != null && classNames != null } + +private fun findParent(kDoc: PsiElement): PsiElement = + if (kDoc.canHaveParent()) findParent(kDoc.parent) else kDoc + +private fun PsiElement.canHaveParent(): Boolean = this is KDocSection && knownTag != KDocKnownTag.PROPERTY + +private fun getAllKDocTags(kDocImpl: PsiElement): List<KDocTag> = + kDocImpl.children.filterIsInstance<KDocTag>().filterNot { it is KDocSection } + kDocImpl.children.flatMap { + getAllKDocTags(it) + } diff --git a/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/SyntheticDescriptorDocumentationProvider.kt index c96b888a..3b21f771 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/SyntheticDescriptorDocumentationProvider.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/SyntheticDescriptorDocumentationProvider.kt @@ -1,20 +1,21 @@ -package org.jetbrains.dokka.base.translators.descriptors +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.analysis.from -import org.jetbrains.dokka.base.parsers.MarkdownParser +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.from +import org.jetbrains.dokka.analysis.markdown.jb.MarkdownParser import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.resolve.DescriptorFactory private const val ENUM_VALUEOF_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValueOf.kt.template" private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValues.kt.template" - internal class SyntheticDescriptorDocumentationProvider( - private val resolutionFacade: DokkaResolutionFacade +internal class SyntheticDescriptorDocumentationProvider( + private val kDocFinder: KDocFinder, + private val sourceSet: DokkaConfiguration.DokkaSourceSet ) { fun isDocumented(descriptor: DeclarationDescriptor): Boolean = descriptor is FunctionDescriptor && (DescriptorFactory.isEnumValuesMethod(descriptor) || DescriptorFactory.isEnumValueOfMethod(descriptor)) @@ -37,11 +38,9 @@ private const val ENUM_VALUES_TEMPLATE_PATH = "/dokka/docs/kdoc/EnumValues.kt.te private fun loadContent(filePath: String): String? = javaClass.getResource(filePath)?.readText() private fun resolveLink(descriptor: DeclarationDescriptor, link: String): DRI? = - resolveKDocLink( - resolutionFacade.resolveSession.bindingContext, - resolutionFacade, - descriptor, - null, - link.split('.') + kDocFinder.resolveKDocLink( + fromDescriptor = descriptor, + qualifiedName = link, + sourceSet = sourceSet ).firstOrNull()?.let { DRI.from(it) } } diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/annotationsValue.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/annotationsValue.kt new file mode 100644 index 00000000..70254566 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/annotationsValue.kt @@ -0,0 +1,3 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator + +internal fun unquotedValue(value: String): String = value.removeSurrounding("\"") diff --git a/plugins/base/src/main/kotlin/translators/isException.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/isException.kt index d148cd34..710846b3 100644 --- a/plugins/base/src/main/kotlin/translators/isException.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/isException.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.base.translators +package org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.translator import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.AncestryNode diff --git a/subprojects/analysis-kotlin-descriptors/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-descriptors/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..c7a8a233 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin diff --git a/plugins/base/src/test/kotlin/parsers/ParseModuleAndPackageDocumentationFragmentsTest.kt b/subprojects/analysis-kotlin-descriptors/compiler/src/test/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ParseModuleAndPackageDocumentationFragmentsTest.kt index b6f9307f..321aba45 100644 --- a/plugins/base/src/test/kotlin/parsers/ParseModuleAndPackageDocumentationFragmentsTest.kt +++ b/subprojects/analysis-kotlin-descriptors/compiler/src/test/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ParseModuleAndPackageDocumentationFragmentsTest.kt @@ -1,17 +1,17 @@ -package parsers +package org.jetbrains.dokka.analysis.kotlin.descriptors -import org.intellij.markdown.MarkdownElementTypes -import org.jetbrains.dokka.base.parsers.moduleAndPackage.* -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.Module -import org.jetbrains.dokka.base.parsers.moduleAndPackage.ModuleAndPackageDocumentation.Classifier.Package + +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.* +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.impl.moduledocs.ModuleAndPackageDocumentation.Classifier.* +import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.utilities.DokkaLogger -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.io.TempDir import java.nio.file.Path +import kotlin.test.assertEquals class ParseModuleAndPackageDocumentationFragmentsTest { @@ -257,14 +257,14 @@ class ParseModuleAndPackageDocumentationFragmentsTest { Text("@Smth") ) ) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ), Author( CustomDocTag( listOf( P(listOf(Text("Smb"))) - ), name = MarkdownElementTypes.MARKDOWN_FILE.name + ), name = MARKDOWN_ELEMENT_FILE_NAME ) ) ) diff --git a/subprojects/analysis-kotlin-descriptors/ide/README.md b/subprojects/analysis-kotlin-descriptors/ide/README.md new file mode 100644 index 00000000..14ed5baa --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/README.md @@ -0,0 +1,11 @@ +# Descriptors: IDE + +An internal module that encapsulates external IDE (`org.jetbrains.kotlin:idea`) dependencies. + +IDE artifacts are reused for things that are not possible to do with the Kotlin compiler API, such +as KDoc or KLib parsing/processing, because Dokka is very similar to an IDE when it comes to analyzing +source code and docs. + +Exists primarily to make sure that unreliable and coupled external dependencies are somewhat abstracted away, +otherwise everything gets tangled together and breaking changes in such dependencies become very +difficult to resolve. diff --git a/subprojects/analysis-kotlin-descriptors/ide/api/ide.api b/subprojects/analysis-kotlin-descriptors/ide/api/ide.api new file mode 100644 index 00000000..a59658a3 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/api/ide.api @@ -0,0 +1,4 @@ +public final class org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V +} + diff --git a/subprojects/analysis-kotlin-descriptors/ide/build.gradle.kts b/subprojects/analysis-kotlin-descriptors/ide/build.gradle.kts new file mode 100644 index 00000000..79777256 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("org.jetbrains.conventions.kotlin-jvm") +} + +dependencies { + compileOnly(projects.core) + compileOnly(projects.subprojects.analysisKotlinApi) + + implementation(projects.subprojects.analysisKotlinDescriptors.compiler) + + api(libs.kotlin.idePlugin.common) + api(libs.kotlin.idePlugin.idea) + api(libs.kotlin.idePlugin.core) + api(libs.kotlin.idePlugin.native) + + // TODO [beresnev] needed for CommonIdePlatformKind, describe + implementation(libs.kotlin.jps.common) + + // TODO [beresnev] get rid of it + compileOnly(libs.kotlinx.coroutines.core) +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CoreKotlinCacheService.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/CoreKotlinCacheService.kt index e2cd328a..d7069656 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CoreKotlinCacheService.kt +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/CoreKotlinCacheService.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide import com.intellij.psi.PsiFile import org.jetbrains.kotlin.analyzer.ModuleInfo @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache -class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService { +internal class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade) : KotlinCacheService { override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade { return resolutionFacade } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DokkaResolutionFacade.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/DokkaResolutionFacade.kt index b278ef6e..e2253b99 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DokkaResolutionFacade.kt +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/DokkaResolutionFacade.kt @@ -1,6 +1,6 @@ @file:OptIn(FrontendInternals::class) -package org.jetbrains.dokka.analysis +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide import com.google.common.collect.ImmutableMap import com.intellij.openapi.project.Project @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice import org.jetbrains.kotlin.util.slicedMap.WritableSlice -class DokkaResolutionFacade( +internal class DokkaResolutionFacade( override val project: Project, override val moduleDescriptor: ModuleDescriptor, val resolverForModule: ResolverForModule diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeAnalysisContextCreator.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeAnalysisContextCreator.kt new file mode 100644 index 00000000..166e25fa --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeAnalysisContextCreator.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import com.intellij.mock.MockComponentManager +import com.intellij.mock.MockProject +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.AnalysisContextCreator +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisContext +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisEnvironment +import org.jetbrains.kotlin.analyzer.ResolverForModule +import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.ModuleDescriptor + +internal class IdeAnalysisContextCreator : AnalysisContextCreator { + override fun create( + project: MockProject, + moduleDescriptor: ModuleDescriptor, + moduleResolver: ResolverForModule, + kotlinEnvironment: KotlinCoreEnvironment, + analysisEnvironment: AnalysisEnvironment, + ): AnalysisContext { + val facade = DokkaResolutionFacade(project, moduleDescriptor, moduleResolver) + val projectComponentManager = project as MockComponentManager + projectComponentManager.registerService( + KotlinCacheService::class.java, + CoreKotlinCacheService(facade) + ) + return ResolutionFacadeAnalysisContext(facade, kotlinEnvironment, analysisEnvironment) + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeCompilerExtensionPointProvider.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeCompilerExtensionPointProvider.kt new file mode 100644 index 00000000..73d908e9 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeCompilerExtensionPointProvider.kt @@ -0,0 +1,46 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerExtensionPointProvider +import org.jetbrains.kotlin.caches.resolve.CommonPlatformKindResolution +import org.jetbrains.kotlin.caches.resolve.IdePlatformKindResolution +import org.jetbrains.kotlin.caches.resolve.JsPlatformKindResolution +import org.jetbrains.kotlin.caches.resolve.JvmPlatformKindResolution +import org.jetbrains.kotlin.extensions.ApplicationExtensionDescriptor +import org.jetbrains.kotlin.ide.konan.NativePlatformKindResolution +import org.jetbrains.kotlin.platform.IdePlatformKind +import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind +import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind +import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind +import org.jetbrains.kotlin.platform.impl.NativeIdePlatformKind + +internal class IdeCompilerExtensionPointProvider : CompilerExtensionPointProvider { + override fun get(): List<CompilerExtensionPointProvider.CompilerExtensionPoint> { + + @Suppress("UNCHECKED_CAST") + val idePlatformKind = CompilerExtensionPointProvider.CompilerExtensionPoint( + ApplicationExtensionDescriptor( + "org.jetbrains.kotlin.idePlatformKind", + IdePlatformKind::class.java + ) as ApplicationExtensionDescriptor<Any>, + listOf( + CommonIdePlatformKind, + JvmIdePlatformKind, + JsIdePlatformKind, + NativeIdePlatformKind + ) + ) + + @Suppress("UNCHECKED_CAST") + val resolution = CompilerExtensionPointProvider.CompilerExtensionPoint( + IdePlatformKindResolution as ApplicationExtensionDescriptor<Any>, + listOf( + CommonPlatformKindResolution(), + JvmPlatformKindResolution(), + JsPlatformKindResolution(), + NativePlatformKindResolution() + ) + ) + + return listOf(idePlatformKind, resolution) + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorAnalysisPlugin.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorAnalysisPlugin.kt new file mode 100644 index 00000000..930e4a3f --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorAnalysisPlugin.kt @@ -0,0 +1,38 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +@InternalDokkaApi +class IdeDescriptorAnalysisPlugin : DokkaPlugin() { + + internal val ideKdocFinder by extending { + plugin<CompilerDescriptorAnalysisPlugin>().kdocFinder providing ::IdePluginKDocFinder + } + + internal val ideDescriptorFinder by extending { + plugin<CompilerDescriptorAnalysisPlugin>().descriptorFinder providing { IdeDescriptorFinder() } + } + + internal val ideKlibService by extending { + plugin<CompilerDescriptorAnalysisPlugin>().klibService providing { IdeKLibService() } + } + + internal val ideCompilerExtensionPointProvider by extending { + plugin<CompilerDescriptorAnalysisPlugin>().compilerExtensionPointProvider providing { IdeCompilerExtensionPointProvider() } + } + + internal val ideApplicationHack by extending { + plugin<CompilerDescriptorAnalysisPlugin>().mockApplicationHack providing { IdeMockApplicationHack() } + } + + internal val ideAnalysisContextCreator by extending { + plugin<CompilerDescriptorAnalysisPlugin>().analysisContextCreator providing { IdeAnalysisContextCreator() } + } + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorFinder.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorFinder.kt new file mode 100644 index 00000000..bc591151 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeDescriptorFinder.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.DescriptorFinder +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor +import org.jetbrains.kotlin.psi.KtDeclaration + +internal class IdeDescriptorFinder : DescriptorFinder { + override fun KtDeclaration.findDescriptor(): DeclarationDescriptor? { + return this.descriptor + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeKLibService.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeKLibService.kt new file mode 100644 index 00000000..c3422f56 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeKLibService.kt @@ -0,0 +1,30 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KLibService +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataModuleDescriptorFactory +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.idea.klib.createKlibPackageFragmentProvider +import org.jetbrains.kotlin.idea.klib.getCompatibilityInfo +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.library.KotlinLibrary +import org.jetbrains.kotlin.storage.StorageManager + +internal class IdeKLibService : KLibService { + override fun KotlinLibrary.createPackageFragmentProvider( + storageManager: StorageManager, + metadataModuleDescriptorFactory: KlibMetadataModuleDescriptorFactory, + languageVersionSettings: LanguageVersionSettings, + moduleDescriptor: ModuleDescriptor, + lookupTracker: LookupTracker, + ): PackageFragmentProvider? { + return this.createKlibPackageFragmentProvider( + storageManager, metadataModuleDescriptorFactory, languageVersionSettings, moduleDescriptor, lookupTracker + ) + } + + override fun isAnalysisCompatible(kotlinLibrary: KotlinLibrary): Boolean { + return kotlinLibrary.getCompatibilityInfo().isCompatible + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeMockApplicationHack.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeMockApplicationHack.kt new file mode 100644 index 00000000..a582572d --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdeMockApplicationHack.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import com.intellij.mock.MockApplication +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.MockApplicationHack +import org.jetbrains.kotlin.idea.klib.KlibLoadingMetadataCache + +internal class IdeMockApplicationHack : MockApplicationHack { + override fun hack(mockApplication: MockApplication) { + if (mockApplication.getService(KlibLoadingMetadataCache::class.java) == null) + mockApplication.registerService(KlibLoadingMetadataCache::class.java, KlibLoadingMetadataCache()) + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdePluginKDocFinder.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdePluginKDocFinder.kt new file mode 100644 index 00000000..d0d217f6 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/IdePluginKDocFinder.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.CompilerDescriptorAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.KDocFinder +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.idea.kdoc.findKDoc +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils + +internal class IdePluginKDocFinder( + private val context: DokkaContext +) : KDocFinder { + + override fun KtElement.findKDoc(): KDocTag? { + return this.findKDoc { DescriptorToSourceUtils.descriptorToDeclaration(it) } + } + + override fun DeclarationDescriptor.find(descriptorToPsi: (DeclarationDescriptorWithSource) -> PsiElement?): KDocTag? { + return this.findKDoc(descriptorToPsi) + } + + override fun resolveKDocLink( + fromDescriptor: DeclarationDescriptor, + qualifiedName: String, + sourceSet: DokkaConfiguration.DokkaSourceSet, + emptyBindingContext: Boolean + ): Collection<DeclarationDescriptor> { + val facadeAnalysisContext = context + .plugin<CompilerDescriptorAnalysisPlugin>() + .querySingle { kotlinAnalysis }[sourceSet] as ResolutionFacadeAnalysisContext + + return org.jetbrains.kotlin.idea.kdoc.resolveKDocLink( + context = if (emptyBindingContext) BindingContext.EMPTY else facadeAnalysisContext.resolveSession.bindingContext, + resolutionFacade = facadeAnalysisContext.facade, + fromDescriptor = fromDescriptor, + fromSubjectOfTag = null, + qualifiedName = qualifiedName.split('.') + ) + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/ResolutionFacadeAnalysisContext.kt b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/ResolutionFacadeAnalysisContext.kt new file mode 100644 index 00000000..d16ee7d2 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/ide/ResolutionFacadeAnalysisContext.kt @@ -0,0 +1,30 @@ +package org.jetbrains.dokka.analysis.kotlin.descriptors.ide + +import com.intellij.openapi.project.Project +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisContext +import org.jetbrains.dokka.analysis.kotlin.descriptors.compiler.configuration.AnalysisEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.resolve.lazy.ResolveSession + +internal class ResolutionFacadeAnalysisContext( + val facade: DokkaResolutionFacade, + + private val kotlinEnvironment: KotlinCoreEnvironment, + private val analysisEnvironment: AnalysisEnvironment +) : AnalysisContext { + private var isClosed: Boolean = false + + override val environment: KotlinCoreEnvironment + get() = kotlinEnvironment.takeUnless { isClosed } + ?: throw IllegalStateException("AnalysisEnvironment is already closed") + + override val resolveSession: ResolveSession = facade.resolveSession + override val moduleDescriptor: ModuleDescriptor = facade.moduleDescriptor + override val project: Project = facade.project + + override fun close() { + isClosed = true + analysisEnvironment.dispose() + } +} diff --git a/subprojects/analysis-kotlin-descriptors/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-descriptors/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..f993dcb1 --- /dev/null +++ b/subprojects/analysis-kotlin-descriptors/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.analysis.kotlin.descriptors.ide.IdeDescriptorAnalysisPlugin diff --git a/subprojects/analysis-kotlin-symbols/README.md b/subprojects/analysis-kotlin-symbols/README.md new file mode 100644 index 00000000..12e3041c --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/README.md @@ -0,0 +1,8 @@ +# Analysis: Kotlin symbols + +An internal symbols-based implementation for [analysis-kotlin-api](../analysis-kotlin-api), also known as K2 or +"the new compiler". + +Contains no stable public API and must not be used by anyone directly, only via [analysis-kotlin-api](../analysis-kotlin-api). + +Can be added as a runtime dependency by the runner. diff --git a/subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api b/subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/api/analysis-kotlin-symbols.api diff --git a/subprojects/analysis-kotlin-symbols/build.gradle.kts b/subprojects/analysis-kotlin-symbols/build.gradle.kts new file mode 100644 index 00000000..c000df58 --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/build.gradle.kts @@ -0,0 +1,34 @@ +import org.jetbrains.DokkaPublicationBuilder +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("org.jetbrains.conventions.kotlin-jvm") + id("org.jetbrains.conventions.maven-publish") + id("com.github.johnrengelman.shadow") +} + +dependencies { + implementation(projects.subprojects.analysisKotlinApi) + implementation(projects.subprojects.analysisKotlinSymbols.compiler) + implementation(projects.subprojects.analysisKotlinSymbols.ide) +} + +tasks { + shadowJar { + val dokka_version: String by project + + // cannot be named exactly like the artifact (i.e analysis-kotlin-symbols-VER.jar), + // otherwise leads to obscure test failures when run via CLI, but not via IJ + archiveFileName.set("analysis-kotlin-symbols-all-$dokka_version.jar") + archiveClassifier.set("") + + // service files are merged to make sure all Dokka plugins + // from the dependencies are loaded, and not just a single one. + mergeServiceFiles() + } +} + +registerDokkaArtifactPublication("analysisKotlinSymbols") { + artifactId = "analysis-kotlin-symbols" + component = DokkaPublicationBuilder.Component.Shadow +} diff --git a/subprojects/analysis-kotlin-symbols/compiler/api/compiler.api b/subprojects/analysis-kotlin-symbols/compiler/api/compiler.api new file mode 100644 index 00000000..39870f57 --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/compiler/api/compiler.api @@ -0,0 +1,4 @@ +public final class org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V +} + diff --git a/subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts b/subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts new file mode 100644 index 00000000..876d87ca --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/compiler/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("org.jetbrains.conventions.kotlin-jvm") +} + +dependencies { + compileOnly(projects.core) + compileOnly(projects.subprojects.analysisKotlinApi) + + // TODO + + // TODO [beresnev] get rid of it + compileOnly(libs.kotlinx.coroutines.core) +} diff --git a/subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt b/subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt new file mode 100644 index 00000000..4a39e0d8 --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/compiler/CompilerSymbolsAnalysisPlugin.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka.analysis.kotlin.symbols.compiler + +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +class CompilerSymbolsAnalysisPlugin : DokkaPlugin() { + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..47163f6e --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/compiler/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.analysis.kotlin.symbols.compiler.CompilerSymbolsAnalysisPlugin diff --git a/subprojects/analysis-kotlin-symbols/ide/api/ide.api b/subprojects/analysis-kotlin-symbols/ide/api/ide.api new file mode 100644 index 00000000..9be46c0b --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/ide/api/ide.api @@ -0,0 +1,4 @@ +public final class org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { + public fun <init> ()V +} + diff --git a/subprojects/analysis-kotlin-symbols/ide/build.gradle.kts b/subprojects/analysis-kotlin-symbols/ide/build.gradle.kts new file mode 100644 index 00000000..876d87ca --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/ide/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("org.jetbrains.conventions.kotlin-jvm") +} + +dependencies { + compileOnly(projects.core) + compileOnly(projects.subprojects.analysisKotlinApi) + + // TODO + + // TODO [beresnev] get rid of it + compileOnly(libs.kotlinx.coroutines.core) +} diff --git a/subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt b/subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt new file mode 100644 index 00000000..33f555cb --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/ide/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/ide/IdeSymbolsAnalysisPlugin.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka.analysis.kotlin.symbols.ide + +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +class IdeSymbolsAnalysisPlugin : DokkaPlugin() { + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..59245578 --- /dev/null +++ b/subprojects/analysis-kotlin-symbols/ide/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.analysis.kotlin.symbols.ide.IdeSymbolsAnalysisPlugin diff --git a/subprojects/analysis-markdown-jb/README.md b/subprojects/analysis-markdown-jb/README.md new file mode 100644 index 00000000..2922abc8 --- /dev/null +++ b/subprojects/analysis-markdown-jb/README.md @@ -0,0 +1,7 @@ +# Ananlysis: Markdown (JetBrains) + +An internal module that encapsulates Markdown file and format parsing by using `org.jetbrains:markdown` +as the primary implementation dependency. + +Used by other Dokka modules, but it must not be used by external users directly until stable public API +is provided. diff --git a/subprojects/analysis-markdown-jb/api/analysis-markdown-jb.api b/subprojects/analysis-markdown-jb/api/analysis-markdown-jb.api new file mode 100644 index 00000000..3a8c37c5 --- /dev/null +++ b/subprojects/analysis-markdown-jb/api/analysis-markdown-jb.api @@ -0,0 +1,28 @@ +public final class org/jetbrains/dokka/analysis/markdown/jb/MarkdownApiKt { + public static final fun getMARKDOWN_ELEMENT_FILE_NAME ()Ljava/lang/String; +} + +public class org/jetbrains/dokka/analysis/markdown/jb/MarkdownParser : org/jetbrains/dokka/analysis/markdown/jb/Parser { + public static final field Companion Lorg/jetbrains/dokka/analysis/markdown/jb/MarkdownParser$Companion; + public fun <init> (Lkotlin/jvm/functions/Function1;Ljava/lang/String;)V + public fun parseStringToDocNode (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocTag; + protected fun parseTagWithBody (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/TagWrapper; + protected fun preparse (Ljava/lang/String;)Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/markdown/jb/MarkdownParser$Companion { + public final fun fqDeclarationName (Lorg/jetbrains/dokka/links/DRI;)Ljava/lang/String; +} + +public final class org/jetbrains/dokka/analysis/markdown/jb/ParseUtilsKt { + public static final fun parseHtmlEncodedWithNormalisedSpaces (Ljava/lang/String;Z)Ljava/util/List; +} + +public abstract class org/jetbrains/dokka/analysis/markdown/jb/Parser { + public fun <init> ()V + public fun parse (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocumentationNode; + public abstract fun parseStringToDocNode (Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/DocTag; + protected fun parseTagWithBody (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/model/doc/TagWrapper; + protected abstract fun preparse (Ljava/lang/String;)Ljava/lang/String; +} + diff --git a/subprojects/analysis-markdown-jb/build.gradle.kts b/subprojects/analysis-markdown-jb/build.gradle.kts new file mode 100644 index 00000000..c67ee53c --- /dev/null +++ b/subprojects/analysis-markdown-jb/build.gradle.kts @@ -0,0 +1,17 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("org.jetbrains.conventions.kotlin-jvm") + id("org.jetbrains.conventions.maven-publish") +} + +dependencies { + compileOnly(projects.core) + + implementation(libs.jsoup) + implementation(libs.jetbrains.markdown) +} + +registerDokkaArtifactPublication("analysisMarkdown") { + artifactId = "analysis-markdown" +} diff --git a/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/MarkdownApi.kt b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/MarkdownApi.kt new file mode 100644 index 00000000..e8738190 --- /dev/null +++ b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/MarkdownApi.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.analysis.markdown.jb + +import org.intellij.markdown.MarkdownElementTypes +import org.jetbrains.dokka.InternalDokkaApi + +// TODO [beresnev] move/rename if it's only used for CustomDocTag. for now left as is for compatibility +@InternalDokkaApi +val MARKDOWN_ELEMENT_FILE_NAME = MarkdownElementTypes.MARKDOWN_FILE.name diff --git a/plugins/base/src/main/kotlin/parsers/MarkdownParser.kt b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/MarkdownParser.kt index d49e7c7a..165ca4c6 100644 --- a/plugins/base/src/main/kotlin/parsers/MarkdownParser.kt +++ b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/MarkdownParser.kt @@ -1,6 +1,5 @@ -package org.jetbrains.dokka.base.parsers +package org.jetbrains.dokka.analysis.markdown.jb -import com.intellij.psi.PsiElement import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.MarkdownTokenTypes import org.intellij.markdown.ast.ASTNode @@ -11,18 +10,16 @@ import org.intellij.markdown.flavours.gfm.GFMElementTypes import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor import org.intellij.markdown.flavours.gfm.GFMTokenTypes import org.intellij.markdown.html.HtmlGenerator -import org.jetbrains.dokka.base.parsers.factories.DocTagsFromIElementFactory +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.analysis.markdown.jb.factories.DocTagsFromIElementFactory import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.links.PointingToDeclaration import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.doc.Suppress -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag import java.net.MalformedURLException import java.net.URL import org.intellij.markdown.parser.MarkdownParser as IntellijMarkdownParser +@InternalDokkaApi open class MarkdownParser( private val externalDri: (String) -> DRI?, private val kdocLocation: String?, @@ -501,87 +498,7 @@ open class MarkdownParser( companion object { - fun parseFromKDocTag( - kDocTag: KDocTag?, - externalDri: (String) -> DRI?, - kdocLocation: String?, - parseWithChildren: Boolean = true - ): DocumentationNode { - return if (kDocTag == null) { - DocumentationNode(emptyList()) - } else { - fun parseStringToDocNode(text: String) = - MarkdownParser(externalDri, kdocLocation).parseStringToDocNode(text) - - fun pointedLink(tag: KDocTag): DRI? = (parseStringToDocNode("[${tag.getSubjectName()}]")).let { - val link = it.children[0].children[0] - if (link is DocumentationLink) link.dri else null - } - - val allTags = - listOf(kDocTag) + if (kDocTag.canHaveParent() && parseWithChildren) getAllKDocTags(findParent(kDocTag)) else emptyList() - DocumentationNode( - allTags.map { - when (it.knownTag) { - null -> if (it.name == null) Description(parseStringToDocNode(it.getContent())) else CustomTagWrapper( - parseStringToDocNode(it.getContent()), - it.name!! - ) - KDocKnownTag.AUTHOR -> Author(parseStringToDocNode(it.getContent())) - KDocKnownTag.THROWS -> { - val dri = pointedLink(it) - Throws( - parseStringToDocNode(it.getContent()), - dri?.fqDeclarationName() ?: it.getSubjectName().orEmpty(), - dri, - ) - } - KDocKnownTag.EXCEPTION -> { - val dri = pointedLink(it) - Throws( - parseStringToDocNode(it.getContent()), - dri?.fqDeclarationName() ?: it.getSubjectName().orEmpty(), - dri - ) - } - KDocKnownTag.PARAM -> Param( - parseStringToDocNode(it.getContent()), - it.getSubjectName().orEmpty() - ) - KDocKnownTag.RECEIVER -> Receiver(parseStringToDocNode(it.getContent())) - KDocKnownTag.RETURN -> Return(parseStringToDocNode(it.getContent())) - KDocKnownTag.SEE -> { - val dri = pointedLink(it) - See( - parseStringToDocNode(it.getContent()), - dri?.fqDeclarationName() ?: it.getSubjectName().orEmpty(), - dri, - ) - } - KDocKnownTag.SINCE -> Since(parseStringToDocNode(it.getContent())) - KDocKnownTag.CONSTRUCTOR -> Constructor(parseStringToDocNode(it.getContent())) - KDocKnownTag.PROPERTY -> Property( - parseStringToDocNode(it.getContent()), - it.getSubjectName().orEmpty() - ) - KDocKnownTag.SAMPLE -> Sample( - parseStringToDocNode(it.getContent()), - it.getSubjectName().orEmpty() - ) - KDocKnownTag.SUPPRESS -> Suppress(parseStringToDocNode(it.getContent())) - } - } - ) - } - } - - //Horrible hack but since link resolution is passed as a function i am not able to resolve them otherwise - @kotlin.Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated("This function makes wrong assumptions and is missing a lot of corner cases related to generics, " + - "parameters and static members. This is not supposed to be public API and will not be supported in the future") - fun DRI.fqName(): String? = "$packageName.$classNames".takeIf { packageName != null && classNames != null } - - private fun DRI.fqDeclarationName(): String? { + fun DRI.fqDeclarationName(): String? { if (this.target !is PointingToDeclaration) { return null } @@ -589,16 +506,6 @@ open class MarkdownParser( .joinToString(separator = ".") .takeIf { it.isNotBlank() } } - - private fun findParent(kDoc: PsiElement): PsiElement = - if (kDoc.canHaveParent()) findParent(kDoc.parent) else kDoc - - private fun PsiElement.canHaveParent(): Boolean = this is KDocSection && knownTag != KDocKnownTag.PROPERTY - - private fun getAllKDocTags(kDocImpl: PsiElement): List<KDocTag> = - kDocImpl.children.filterIsInstance<KDocTag>().filterNot { it is KDocSection } + kDocImpl.children.flatMap { - getAllKDocTags(it) - } } } diff --git a/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/ParseUtils.kt b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/ParseUtils.kt new file mode 100644 index 00000000..7f520591 --- /dev/null +++ b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/ParseUtils.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.analysis.markdown.jb + +import org.intellij.markdown.lexer.Compat +import org.intellij.markdown.lexer.Compat.forEachCodePoint +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.doc.Text +import org.jsoup.internal.StringUtil +import org.jsoup.nodes.Entities + +@InternalDokkaApi +fun String.parseHtmlEncodedWithNormalisedSpaces( + renderWhiteCharactersAsSpaces: Boolean +): List<DocTag> { + val accum = StringBuilder() + val tags = mutableListOf<DocTag>() + var lastWasWhite = false + + forEachCodePoint { c -> + if (renderWhiteCharactersAsSpaces && StringUtil.isWhitespace(c)) { + if (!lastWasWhite) { + accum.append(' ') + lastWasWhite = true + } + } else if (Compat.codePointToString(c).let { it != Entities.escape(it) }) { + accum.toString().takeIf { it.isNotBlank() }?.let { tags.add(Text(it)) } + accum.delete(0, accum.length) + + accum.appendCodePoint(c) + tags.add(Text(accum.toString(), params = DocTag.contentTypeParam("html"))) + accum.delete(0, accum.length) + } else if (!StringUtil.isInvisibleChar(c)) { + accum.appendCodePoint(c) + lastWasWhite = false + } + } + accum.toString().takeIf { it.isNotBlank() }?.let { tags.add(Text(it)) } + return tags +} diff --git a/plugins/base/src/main/kotlin/parsers/Parser.kt b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/Parser.kt index af07ec53..09650b32 100644 --- a/plugins/base/src/main/kotlin/parsers/Parser.kt +++ b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/Parser.kt @@ -1,19 +1,19 @@ -package org.jetbrains.dokka.base.parsers +package org.jetbrains.dokka.analysis.markdown.jb +import org.jetbrains.dokka.InternalDokkaApi import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.doc.Deprecated -import org.jetbrains.dokka.model.doc.Suppress +@InternalDokkaApi abstract class Parser { abstract fun parseStringToDocNode(extractedString: String): DocTag - abstract fun preparse(text: String): String + protected abstract fun preparse(text: String): String open fun parse(text: String): DocumentationNode = DocumentationNode(extractTagsToListOfPairs(preparse(text)).map { (tag, content) -> parseTagWithBody(tag, content) }) - open fun parseTagWithBody(tagName: String, content: String): TagWrapper = + protected open fun parseTagWithBody(tagName: String, content: String): TagWrapper = when (tagName) { "description" -> Description(parseStringToDocNode(content)) "author" -> Author(parseStringToDocNode(content)) diff --git a/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromIElementFactory.kt b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/factories/DocTagsFromIElementFactory.kt index fed3f7eb..86de8000 100644 --- a/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromIElementFactory.kt +++ b/subprojects/analysis-markdown-jb/src/main/kotlin/org/jetbrains/dokka/analysis/markdown/jb/factories/DocTagsFromIElementFactory.kt @@ -1,16 +1,18 @@ -package org.jetbrains.dokka.base.parsers.factories +package org.jetbrains.dokka.analysis.markdown.jb.factories -import org.jetbrains.dokka.model.doc.* import org.intellij.markdown.IElementType import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.MarkdownTokenTypes import org.intellij.markdown.flavours.gfm.GFMElementTypes import org.intellij.markdown.flavours.gfm.GFMTokenTypes -import org.jetbrains.dokka.base.translators.parseWithNormalisedSpaces +import org.jetbrains.dokka.analysis.markdown.jb.MARKDOWN_ELEMENT_FILE_NAME +import org.jetbrains.dokka.analysis.markdown.jb.parseHtmlEncodedWithNormalisedSpaces import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.model.doc.DocTag.Companion.contentTypeParam +import org.jsoup.Jsoup -object DocTagsFromIElementFactory { +internal object DocTagsFromIElementFactory { @Suppress("IMPLICIT_CAST_TO_ANY") fun getInstance(type: IElementType, children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap(), body: String? = null, dri: DRI? = null, keepFormatting: Boolean = false) = @@ -52,7 +54,7 @@ object DocTagsFromIElementFactory { GFMElementTypes.HEADER -> Th(children, params) GFMElementTypes.ROW -> Tr(children, params) GFMTokenTypes.CELL -> Td(children, params) - MarkdownElementTypes.MARKDOWN_FILE -> CustomDocTag(children, params, MarkdownElementTypes.MARKDOWN_FILE.name) + MarkdownElementTypes.MARKDOWN_FILE -> CustomDocTag(children, params, MARKDOWN_ELEMENT_FILE_NAME) MarkdownElementTypes.HTML_BLOCK, MarkdownTokenTypes.HTML_TAG, MarkdownTokenTypes.HTML_BLOCK_CONTENT -> Text(body.orEmpty(), params = params + contentTypeParam("html")) @@ -64,4 +66,21 @@ object DocTagsFromIElementFactory { else -> listOf(it as DocTag) } } + + /** + * Parses string into [Text] doc tags that can have either value of the string or html-encoded value with content-type=html parameter. + * Content type is added when dealing with html entries like ` ` + */ + private fun String.parseWithNormalisedSpaces( + renderWhiteCharactersAsSpaces: Boolean + ): List<DocTag> { + if (!requiresHtmlEncoding()) { + return parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces) + } + // parsing it using jsoup is required to get codePoints, otherwise they are interpreted separately, as chars + // But we dont need to do it for java as it is already parsed with jsoup + return Jsoup.parseBodyFragment(this).body().wholeText().parseHtmlEncodedWithNormalisedSpaces(renderWhiteCharactersAsSpaces) + } + + private fun String.requiresHtmlEncoding(): Boolean = indexOf('&') != -1 } |