From 8e5c63d035ef44a269b8c43430f43f5c8eebfb63 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Fri, 10 Nov 2023 11:46:54 +0100 Subject: Restructure the project to utilize included builds (#3174) * Refactor and simplify artifact publishing * Update Gradle to 8.4 * Refactor and simplify convention plugins and build scripts Fixes #3132 --------- Co-authored-by: Adam <897017+aSemy@users.noreply.github.com> Co-authored-by: Oleg Yukhnevich --- .../org/jetbrains/dokka/ConfigurationJsonUtils.kt | 54 +++ .../kotlin/org/jetbrains/dokka/CoreExtensions.kt | 43 ++ .../kotlin/org/jetbrains/dokka/DokkaBootstrap.kt | 15 + .../org/jetbrains/dokka/DokkaBootstrapImpl.kt | 69 +++ .../kotlin/org/jetbrains/dokka/DokkaException.kt | 7 + .../kotlin/org/jetbrains/dokka/DokkaGenerator.kt | 89 ++++ .../kotlin/org/jetbrains/dokka/DokkaVersion.kt | 15 + .../kotlin/org/jetbrains/dokka/InternalDokkaApi.kt | 28 ++ .../kotlin/org/jetbrains/dokka/configuration.kt | 280 +++++++++++ .../org/jetbrains/dokka/defaultConfiguration.kt | 100 ++++ .../org/jetbrains/dokka/defaultExternalLinks.kt | 33 ++ .../org/jetbrains/dokka/generation/Generation.kt | 19 + .../main/kotlin/org/jetbrains/dokka/links/DRI.kt | 146 ++++++ .../jetbrains/dokka/model/CompositeSourceSetID.kt | 46 ++ .../org/jetbrains/dokka/model/DisplaySourceSet.kt | 61 +++ .../org/jetbrains/dokka/model/Documentable.kt | 540 +++++++++++++++++++++ .../kotlin/org/jetbrains/dokka/model/JvmField.kt | 14 + .../org/jetbrains/dokka/model/WithChildren.kt | 98 ++++ .../org/jetbrains/dokka/model/additionalExtras.kt | 142 ++++++ .../org/jetbrains/dokka/model/ancestryNode.kt | 18 + .../kotlin/org/jetbrains/dokka/model/classKinds.kt | 24 + .../org/jetbrains/dokka/model/defaultValues.kt | 34 ++ .../kotlin/org/jetbrains/dokka/model/doc/DocTag.kt | 372 ++++++++++++++ .../jetbrains/dokka/model/doc/DocumentationNode.kt | 9 + .../org/jetbrains/dokka/model/doc/TagWrapper.kt | 36 ++ .../dokka/model/documentableProperties.kt | 71 +++ .../org/jetbrains/dokka/model/documentableUtils.kt | 27 ++ .../org/jetbrains/dokka/model/extraModifiers.kt | 68 +++ .../kotlin/org/jetbrains/dokka/model/jvmName.kt | 11 + .../dokka/model/properties/PropertyContainer.kt | 69 +++ .../jetbrains/dokka/model/properties/properties.kt | 36 ++ .../org/jetbrains/dokka/pages/ContentNodes.kt | 436 +++++++++++++++++ .../kotlin/org/jetbrains/dokka/pages/PageNodes.kt | 200 ++++++++ .../main/kotlin/org/jetbrains/dokka/pages/Pages.kt | 15 + .../jetbrains/dokka/pages/RendererSpecificPage.kt | 52 ++ .../jetbrains/dokka/pages/contentNodeProperties.kt | 37 ++ .../main/kotlin/org/jetbrains/dokka/pages/utils.kt | 63 +++ .../jetbrains/dokka/plugability/DokkaContext.kt | 235 +++++++++ .../jetbrains/dokka/plugability/DokkaJavaPlugin.kt | 78 +++ .../org/jetbrains/dokka/plugability/DokkaPlugin.kt | 114 +++++ .../jetbrains/dokka/plugability/LazyEvaluated.kt | 21 + .../org/jetbrains/dokka/plugability/extensions.kt | 110 +++++ .../org/jetbrains/dokka/renderers/PostAction.kt | 7 + .../org/jetbrains/dokka/renderers/Renderer.kt | 11 + .../documentation/DefaultDocumentableMerger.kt | 307 ++++++++++++ .../documentation/DocumentableMerger.kt | 12 + .../documentation/DocumentableToPageTranslator.kt | 13 + .../documentation/DocumentableTransformer.kt | 12 + .../PreMergeDocumentableTransformer.kt | 39 ++ .../dokka/transformers/pages/PageCreator.kt | 15 + .../dokka/transformers/pages/PageTransformer.kt | 11 + .../transformers/pages/PageTransformerBuilders.kt | 27 ++ .../sources/AsyncSourceToDocumentableTranslator.kt | 20 + .../sources/SourceToDocumentableTranslator.kt | 13 + .../org/jetbrains/dokka/utilities/Collections.kt | 29 ++ .../org/jetbrains/dokka/utilities/DokkaLogging.kt | 90 ++++ .../kotlin/org/jetbrains/dokka/utilities/Html.kt | 23 + .../utilities/SelfRepresentingSingletonSet.kt | 27 ++ .../jetbrains/dokka/utilities/ServiceLocator.kt | 99 ++++ .../kotlin/org/jetbrains/dokka/utilities/Uri.kt | 46 ++ .../dokka/utilities/associateWithNotNull.kt | 13 + .../kotlin/org/jetbrains/dokka/utilities/cast.kt | 12 + .../kotlin/org/jetbrains/dokka/utilities/json.kt | 65 +++ .../utilities/parallelCollectionOperations.kt | 26 + .../dokka/validity/PreGenerationChecker.kt | 17 + .../core/src/main/resources/META-INF/MANIFEST.MF | 1 + .../META-INF/dokka/dokka-version.properties | 5 + .../test/kotlin/model/CompositeSourceSetIDTest.kt | 76 +++ .../src/test/kotlin/model/DisplaySourceSetTest.kt | 63 +++ .../core/src/test/kotlin/model/DocumentableTest.kt | 115 +++++ .../kotlin/utilities/DokkaConfigurationJsonTest.kt | 72 +++ .../core/src/test/kotlin/utilities/JsonKtTest.kt | 80 +++ 72 files changed, 5281 insertions(+) create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/ConfigurationJsonUtils.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/CoreExtensions.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrapImpl.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaException.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaGenerator.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaVersion.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/InternalDokkaApi.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/configuration.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultConfiguration.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultExternalLinks.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/generation/Generation.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/links/DRI.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/CompositeSourceSetID.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/DisplaySourceSet.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/Documentable.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/JvmField.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/WithChildren.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/additionalExtras.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/ancestryNode.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/classKinds.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/defaultValues.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/doc/DocTag.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/doc/DocumentationNode.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/doc/TagWrapper.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/documentableProperties.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/documentableUtils.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/extraModifiers.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/jvmName.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/properties/PropertyContainer.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/properties/properties.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/pages/ContentNodes.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/pages/PageNodes.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/pages/Pages.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/pages/RendererSpecificPage.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/pages/contentNodeProperties.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/pages/utils.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/plugability/DokkaContext.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/plugability/DokkaJavaPlugin.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/plugability/DokkaPlugin.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/plugability/LazyEvaluated.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/plugability/extensions.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/renderers/PostAction.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/renderers/Renderer.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/documentation/DefaultDocumentableMerger.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/documentation/DocumentableMerger.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/documentation/DocumentableToPageTranslator.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/documentation/DocumentableTransformer.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/documentation/PreMergeDocumentableTransformer.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/pages/PageCreator.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/pages/PageTransformer.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/pages/PageTransformerBuilders.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/sources/AsyncSourceToDocumentableTranslator.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/transformers/sources/SourceToDocumentableTranslator.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/Collections.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/DokkaLogging.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/Html.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/SelfRepresentingSingletonSet.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/ServiceLocator.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/Uri.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/associateWithNotNull.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/cast.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/json.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/utilities/parallelCollectionOperations.kt create mode 100644 dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/validity/PreGenerationChecker.kt create mode 100644 dokka-subprojects/core/src/main/resources/META-INF/MANIFEST.MF create mode 100644 dokka-subprojects/core/src/main/resources/META-INF/dokka/dokka-version.properties create mode 100644 dokka-subprojects/core/src/test/kotlin/model/CompositeSourceSetIDTest.kt create mode 100644 dokka-subprojects/core/src/test/kotlin/model/DisplaySourceSetTest.kt create mode 100644 dokka-subprojects/core/src/test/kotlin/model/DocumentableTest.kt create mode 100644 dokka-subprojects/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt create mode 100644 dokka-subprojects/core/src/test/kotlin/utilities/JsonKtTest.kt (limited to 'dokka-subprojects/core/src') diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/ConfigurationJsonUtils.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/ConfigurationJsonUtils.kt new file mode 100644 index 00000000..e693f4ef --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/ConfigurationJsonUtils.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import org.jetbrains.dokka.plugability.ConfigurableBlock +import org.jetbrains.dokka.utilities.parseJson +import org.jetbrains.dokka.utilities.serializeAsCompactJson +import org.jetbrains.dokka.utilities.serializeAsPrettyJson + +public fun DokkaConfigurationImpl(json: String): DokkaConfigurationImpl = parseJson(json) + +public fun GlobalDokkaConfiguration(json: String): GlobalDokkaConfiguration = parseJson(json) + +@Deprecated("Renamed to better distinguish between compact/pretty prints", ReplaceWith("this.toCompactJsonString()")) +public fun DokkaConfiguration.toJsonString(): String = this.toCompactJsonString() + +@Deprecated("Renamed to better distinguish between compact/pretty prints", ReplaceWith("this.toCompactJsonString()")) +public fun T.toJsonString(): String = this.toCompactJsonString() + +/** + * Serializes [DokkaConfiguration] as a machine-readable and compact JSON string. + * + * The returned string is not very human friendly as it will be difficult to parse by eyes due to it + * being compact and in one line. If you want to show the output to a human being, see [toPrettyJsonString]. + */ +public fun DokkaConfiguration.toCompactJsonString(): String = serializeAsCompactJson(this) + +/** + * Serializes [DokkaConfiguration] as a human-readable (pretty printed) JSON string. + * + * The returned string will have excessive line breaks and indents, which might not be + * desirable when passing this value between API consumers/producers. If you want + * a machine-readable and compact json string, see [toCompactJsonString]. + */ +public fun DokkaConfiguration.toPrettyJsonString(): String = serializeAsPrettyJson(this) + +/** + * Serializes a [ConfigurableBlock] as a machine-readable and compact JSON string. + * + * The returned string is not very human friendly as it will be difficult to parse by eyes due to it + * being compact and in one line. If you want to show the output to a human being, see [toPrettyJsonString]. + */ +public fun T.toCompactJsonString(): String = serializeAsCompactJson(this) + +/** + * Serializes a [ConfigurableBlock] as a human-readable (pretty printed) JSON string. + * + * The returned string will have excessive line breaks and indents, which might not be + * desirable when passing this value between API consumers/producers. If you want + * a machine-readable and compact json string, see [toCompactJsonString]. + */ +public fun T.toPrettyJsonString(): String = serializeAsCompactJson(this) diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/CoreExtensions.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/CoreExtensions.kt new file mode 100644 index 00000000..ca2504e2 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/CoreExtensions.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import org.jetbrains.dokka.generation.Generation +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 +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import org.jetbrains.dokka.validity.PreGenerationChecker +import kotlin.reflect.KProperty + +public object CoreExtensions { + + public val preGenerationCheck: ExtensionPoint by coreExtensionPoint() + + public val generation: ExtensionPoint by coreExtensionPoint() + + public val sourceToDocumentableTranslator: ExtensionPoint by coreExtensionPoint() + + public val documentableMerger: ExtensionPoint by coreExtensionPoint() + + public val documentableTransformer: ExtensionPoint by coreExtensionPoint() + + public val documentableToPageTranslator: ExtensionPoint by coreExtensionPoint() + + public val pageTransformer: ExtensionPoint by coreExtensionPoint() + + public val renderer: ExtensionPoint by coreExtensionPoint() + + public val postActions: ExtensionPoint by coreExtensionPoint() + + private fun coreExtensionPoint() = object { + operator fun provideDelegate(thisRef: CoreExtensions, property: KProperty<*>): Lazy> = + lazy { ExtensionPoint(thisRef::class.qualifiedName!!, property.name) } + } +} diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt new file mode 100644 index 00000000..d3d82e39 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import java.util.function.BiConsumer + +public interface DokkaBootstrap { + @Throws(Throwable::class) + public fun configure(serializedConfigurationJSON: String, logger: BiConsumer) + + @Throws(Throwable::class) + public fun generate() +} diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrapImpl.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrapImpl.kt new file mode 100644 index 00000000..65f0ef72 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrapImpl.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import org.jetbrains.dokka.utilities.DokkaLogger +import java.util.concurrent.atomic.AtomicInteger + +import java.util.function.BiConsumer + +/** + * Accessed with reflection + */ +@Suppress("unused") +public class DokkaBootstrapImpl : DokkaBootstrap { + + public class DokkaProxyLogger( + public val consumer: BiConsumer + ) : DokkaLogger { + private val warningsCounter = AtomicInteger() + private val errorsCounter = AtomicInteger() + + override var warningsCount: Int + get() = warningsCounter.get() + set(value) = warningsCounter.set(value) + + override var errorsCount: Int + get() = errorsCounter.get() + set(value) = errorsCounter.set(value) + + override fun debug(message: String) { + consumer.accept("debug", message) + } + + override fun info(message: String) { + consumer.accept("info", message) + } + + override fun progress(message: String) { + consumer.accept("progress", message) + } + + override fun warn(message: String) { + consumer.accept("warn", message).also { warningsCounter.incrementAndGet() } + } + + override fun error(message: String) { + consumer.accept("error", message).also { errorsCounter.incrementAndGet() } + } + } + + private lateinit var generator: DokkaGenerator + + public fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) { + generator = DokkaGenerator(configuration, logger) + } + + override fun configure(serializedConfigurationJSON: String, logger: BiConsumer) { + configure( + DokkaProxyLogger(logger), + DokkaConfigurationImpl(serializedConfigurationJSON) + ) + } + + override fun generate() { + generator.generate() + } +} diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaException.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaException.kt new file mode 100644 index 00000000..f16a2649 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaException.kt @@ -0,0 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +public open class DokkaException(message: String) : RuntimeException(message) diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaGenerator.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaGenerator.kt new file mode 100644 index 00000000..9ae3adb4 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaGenerator.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("SameParameterValue") + +package org.jetbrains.dokka + +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import org.jetbrains.dokka.generation.GracefulGenerationExit +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.utilities.DokkaLogger + +/** + * DokkaGenerator is the main entry point for generating documentation + * + * [generate] method has been split into submethods for test reasons + */ +public class DokkaGenerator( + private val configuration: DokkaConfiguration, + private val logger: DokkaLogger +) { + + public fun generate() { + timed(logger) { + report("Initializing plugins") + val context = initializePlugins(configuration, logger) + + runCatching { + context.single(CoreExtensions.generation).run { + logger.progress("Dokka is performing: $generationName") + generate() + } + }.exceptionOrNull()?.let { e -> + finalizeCoroutines() + throw e + } + + finalizeCoroutines() + }.dump("\n\n === TIME MEASUREMENT ===\n") + } + + public fun initializePlugins( + configuration: DokkaConfiguration, + logger: DokkaLogger, + additionalPlugins: List = emptyList() + ): DokkaContext = DokkaContext.create(configuration, logger, additionalPlugins) + + @OptIn(DelicateCoroutinesApi::class) + private fun finalizeCoroutines() { + if (configuration.finalizeCoroutines) { + Dispatchers.shutdown() + } + } +} + +public class Timer internal constructor(startTime: Long, private val logger: DokkaLogger?) { + private val steps = mutableListOf("" to startTime) + + public fun report(name: String) { + logger?.progress(name) + steps += (name to System.currentTimeMillis()) + } + + public fun dump(prefix: String = "") { + logger?.info(prefix) + val namePad = steps.map { it.first.length }.maxOrNull() ?: 0 + val timePad = steps.windowed(2).map { (p1, p2) -> p2.second - p1.second }.maxOrNull()?.toString()?.length ?: 0 + steps.windowed(2).forEach { (p1, p2) -> + if (p1.first.isNotBlank()) { + logger?.info("${p1.first.padStart(namePad)}: ${(p2.second - p1.second).toString().padStart(timePad)}") + } + } + } +} + +private fun timed(logger: DokkaLogger? = null, block: Timer.() -> Unit): Timer = + Timer(System.currentTimeMillis(), logger).apply { + try { + block() + } catch (exit: GracefulGenerationExit) { + report("Exiting Generation: ${exit.reason}") + } finally { + report("") + } + } + diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaVersion.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaVersion.kt new file mode 100644 index 00000000..d846988b --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/DokkaVersion.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import java.util.* + +public object DokkaVersion { + public val version: String by lazy { + javaClass.getResourceAsStream("/META-INF/dokka/dokka-version.properties").use { stream -> + Properties().apply { load(stream) }.getProperty("dokka-version") + } + } +} diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/InternalDokkaApi.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/InternalDokkaApi.kt new file mode 100644 index 00000000..65c0427c --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/InternalDokkaApi.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + + +/** + * Marks declarations that are **internal** to Dokka core artifact. + * It means that this API is marked as **public** either for historical or technical reasons. + * It is not intended to be used outside of the Dokka project, has no behaviour guarantees, + * and may lack clear semantics, documentation and backward compatibility. + * + * If you are using such API, it is strongly suggested to migrate from it in order + * to keep backwards compatibility with future Dokka versions. + * Typically, the easiest way to do so is to copy-paste the corresponding utility into + * your own project. + */ +@RequiresOptIn( + level = RequiresOptIn.Level.ERROR, + message = "This is an internal Dokka API not intended for public use" +) +@Target( + AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.FIELD, + AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS +) +@Retention(AnnotationRetention.BINARY) +public annotation class InternalDokkaApi() diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/configuration.kt new file mode 100644 index 00000000..65035d04 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/configuration.kt @@ -0,0 +1,280 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import org.jetbrains.dokka.utilities.cast +import java.io.File +import java.io.Serializable +import java.net.URL + +public object DokkaDefaults { + public val moduleName: String = "root" + public val moduleVersion: String? = null + public val outputDir: File = File("./dokka") + public const val failOnWarning: Boolean = false + public const val suppressObviousFunctions: Boolean = true + public const val suppressInheritedMembers: Boolean = false + public const val offlineMode: Boolean = false + + public const val sourceSetDisplayName: String = "JVM" + public const val sourceSetName: String = "main" + public val analysisPlatform: Platform = Platform.DEFAULT + + public const val suppress: Boolean = false + public const val suppressGeneratedFiles: Boolean = true + + public const val skipEmptyPackages: Boolean = true + public const val skipDeprecated: Boolean = false + + public const val reportUndocumented: Boolean = false + + public const val noStdlibLink: Boolean = false + public const val noAndroidSdkLink: Boolean = false + public const val noJdkLink: Boolean = false + public const val jdkVersion: Int = 8 + + public const val includeNonPublic: Boolean = false + public val documentedVisibilities: Set = setOf(DokkaConfiguration.Visibility.PUBLIC) + + public val pluginsConfiguration: List = mutableListOf() + + public const val delayTemplateSubstitution: Boolean = false + + public val cacheRoot: File? = null +} + +public enum class Platform( + public val key: String +) { + jvm("jvm"), + js("js"), + wasm("wasm"), + native("native"), + common("common"); + + public companion object { + public val DEFAULT: Platform = jvm + + public fun fromString(key: String): Platform { + return when (key.toLowerCase()) { + jvm.key -> jvm + js.key -> js + wasm.key -> wasm + native.key -> native + common.key -> common + "androidjvm", "android" -> jvm + "metadata" -> common + else -> throw IllegalArgumentException("Unrecognized platform: $key") + } + } + } +} + +public fun interface DokkaConfigurationBuilder { + public fun build(): T +} + +public fun Iterable>.build(): List = this.map { it.build() } + +public data class DokkaSourceSetID( + /** + * Unique identifier of the scope that this source set is placed in. + * Each scope provide only unique source set names. + * + * E.g. One DokkaTask inside the Gradle plugin represents one source set scope, since there cannot be multiple + * source sets with the same name. However, a Gradle project will not be a proper scope, since there can be + * multple DokkaTasks that contain source sets with the same name (but different configuration) + */ + val scopeId: String, + val sourceSetName: String +) : Serializable { + override fun toString(): String { + return "$scopeId/$sourceSetName" + } +} + +/** + * Global options can be configured and applied to all packages and modules at once, overwriting package configuration. + * + * These are handy if we have multiple source sets sharing the same global options as it reduces the size of the + * boilerplate. Otherwise, the user would be forced to repeat all these options for each source set. + * + * @see [apply] to learn how to apply global configuration + */ +public data class GlobalDokkaConfiguration( + val perPackageOptions: List?, + val externalDocumentationLinks: List?, + val sourceLinks: List? +) + +public fun DokkaConfiguration.apply(globals: GlobalDokkaConfiguration): DokkaConfiguration = this.apply { + sourceSets.forEach { + it.perPackageOptions.cast>() + .addAll(globals.perPackageOptions ?: emptyList()) + } + + sourceSets.forEach { + it.externalDocumentationLinks.cast>() + .addAll(globals.externalDocumentationLinks ?: emptyList()) + } + + sourceSets.forEach { + it.sourceLinks.cast>().addAll(globals.sourceLinks ?: emptyList()) + } +} + +public interface DokkaConfiguration : Serializable { + public val moduleName: String + public val moduleVersion: String? + public val outputDir: File + public val cacheRoot: File? + public val offlineMode: Boolean + public val failOnWarning: Boolean + public val sourceSets: List + public val modules: List + public val pluginsClasspath: List + public val pluginsConfiguration: List + public val delayTemplateSubstitution: Boolean + public val suppressObviousFunctions: Boolean + public val includes: Set + public val suppressInheritedMembers: Boolean + + /** + * Whether coroutines dispatchers should be shutdown after + * generating documentation via [DokkaGenerator.generate]. + * + * It effectively stops all background threads associated with + * coroutines in order to make classes unloadable by the JVM, + * and rejects all new tasks with [RejectedExecutionException] + * + * This is primarily useful for multi-module builds where coroutines + * can be shut down after each module's partial task to avoid + * possible memory leaks. + * + * However, this can lead to problems in specific lifecycles where + * coroutines are shared and will be reused after documentation generation, + * and closing it down will leave the build in an inoperable state. + * One such example is unit tests, for which finalization should be disabled. + */ + public val finalizeCoroutines: Boolean + + public enum class SerializationFormat : Serializable { + JSON, XML + } + + public interface PluginConfiguration : Serializable { + public val fqPluginName: String + public val serializationFormat: SerializationFormat + public val values: String + } + + public interface DokkaSourceSet : Serializable { + public val sourceSetID: DokkaSourceSetID + public val displayName: String + public val classpath: List + public val sourceRoots: Set + public val dependentSourceSets: Set + public val samples: Set + public val includes: Set + + @Deprecated(message = "Use [documentedVisibilities] property for a more flexible control over documented visibilities") + public val includeNonPublic: Boolean + public val reportUndocumented: Boolean + public val skipEmptyPackages: Boolean + public val skipDeprecated: Boolean + public val jdkVersion: Int + public val sourceLinks: Set + public val perPackageOptions: List + public val externalDocumentationLinks: Set + public val languageVersion: String? + public val apiVersion: String? + public val noStdlibLink: Boolean + public val noJdkLink: Boolean + public val suppressedFiles: Set + public val analysisPlatform: Platform + public val documentedVisibilities: Set + } + + public enum class Visibility { + /** + * `public` modifier for Java, default visibility for Kotlin + */ + PUBLIC, + + /** + * `private` modifier for both Kotlin and Java + */ + PRIVATE, + + /** + * `protected` modifier for both Kotlin and Java + */ + PROTECTED, + + /** + * Kotlin-specific `internal` modifier + */ + INTERNAL, + + /** + * Java-specific package-private visibility (no modifier) + */ + PACKAGE; + + public companion object { + public fun fromString(value: String): Visibility = valueOf(value.toUpperCase()) + } + } + + public interface SourceLinkDefinition : Serializable { + public val localDirectory: String + public val remoteUrl: URL + public val remoteLineSuffix: String? + } + + public interface DokkaModuleDescription : Serializable { + public val name: String + public val relativePathToOutputDirectory: File + public val sourceOutputDirectory: File + public val includes: Set + } + + public interface PackageOptions : Serializable { + public val matchingRegex: String + + @Deprecated("Use [documentedVisibilities] property for a more flexible control over documented visibilities") + public val includeNonPublic: Boolean + public val reportUndocumented: Boolean? + public val skipDeprecated: Boolean + public val suppress: Boolean + public val documentedVisibilities: Set + } + + public interface ExternalDocumentationLink : Serializable { + public val url: URL + public val packageListUrl: URL + + public companion object + } +} + +@Suppress("FunctionName") +public fun ExternalDocumentationLink( + url: URL? = null, + packageListUrl: URL? = null +): ExternalDocumentationLinkImpl { + return if (packageListUrl != null && url != null) + ExternalDocumentationLinkImpl(url, packageListUrl) + else if (url != null) + ExternalDocumentationLinkImpl(url, URL(url, "package-list")) + else + throw IllegalArgumentException("url or url && packageListUrl must not be null for external documentation link") +} + +@Suppress("FunctionName") +public fun ExternalDocumentationLink( + url: String, packageListUrl: String? = null +): ExternalDocumentationLinkImpl = + ExternalDocumentationLink(url.let(::URL), packageListUrl?.let(::URL)) diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultConfiguration.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultConfiguration.kt new file mode 100644 index 00000000..f858d8c0 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultConfiguration.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import java.io.File +import java.net.URL + +public data class DokkaConfigurationImpl( + override val moduleName: String = DokkaDefaults.moduleName, + override val moduleVersion: String? = DokkaDefaults.moduleVersion, + override val outputDir: File = DokkaDefaults.outputDir, + override val cacheRoot: File? = DokkaDefaults.cacheRoot, + override val offlineMode: Boolean = DokkaDefaults.offlineMode, + override val sourceSets: List = emptyList(), + override val pluginsClasspath: List = emptyList(), + override val pluginsConfiguration: List = DokkaDefaults.pluginsConfiguration, + override val modules: List = emptyList(), + override val failOnWarning: Boolean = DokkaDefaults.failOnWarning, + override val delayTemplateSubstitution: Boolean = false, + override val suppressObviousFunctions: Boolean = DokkaDefaults.suppressObviousFunctions, + override val includes: Set = emptySet(), + override val suppressInheritedMembers: Boolean = DokkaDefaults.suppressInheritedMembers, + override val finalizeCoroutines: Boolean = true, +) : DokkaConfiguration + +public data class PluginConfigurationImpl( + override val fqPluginName: String, + override val serializationFormat: DokkaConfiguration.SerializationFormat, + override val values: String +) : DokkaConfiguration.PluginConfiguration + + +public data class DokkaSourceSetImpl( + override val displayName: String = DokkaDefaults.sourceSetDisplayName, + override val sourceSetID: DokkaSourceSetID, + override val classpath: List = emptyList(), + override val sourceRoots: Set = emptySet(), + override val dependentSourceSets: Set = emptySet(), + override val samples: Set = emptySet(), + override val includes: Set = emptySet(), + @Deprecated("Use [documentedVisibilities] property for a more flexible control over documented visibilities") + override val includeNonPublic: Boolean = DokkaDefaults.includeNonPublic, + override val reportUndocumented: Boolean = DokkaDefaults.reportUndocumented, + override val skipEmptyPackages: Boolean = DokkaDefaults.skipEmptyPackages, + override val skipDeprecated: Boolean = DokkaDefaults.skipDeprecated, + override val jdkVersion: Int = DokkaDefaults.jdkVersion, + override val sourceLinks: Set = mutableSetOf(), + override val perPackageOptions: List = mutableListOf(), + override val externalDocumentationLinks: Set = mutableSetOf(), + override val languageVersion: String? = null, + override val apiVersion: String? = null, + override val noStdlibLink: Boolean = DokkaDefaults.noStdlibLink, + override val noJdkLink: Boolean = DokkaDefaults.noJdkLink, + override val suppressedFiles: Set = emptySet(), + override val analysisPlatform: Platform = DokkaDefaults.analysisPlatform, + override val documentedVisibilities: Set = DokkaDefaults.documentedVisibilities, +) : DokkaSourceSet + +public data class DokkaModuleDescriptionImpl( + override val name: String, + override val relativePathToOutputDirectory: File, + override val includes: Set, + override val sourceOutputDirectory: File +) : DokkaConfiguration.DokkaModuleDescription + +public data class SourceLinkDefinitionImpl( + override val localDirectory: String, + override val remoteUrl: URL, + override val remoteLineSuffix: String?, +) : DokkaConfiguration.SourceLinkDefinition { + + public companion object { + public fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinitionImpl { + val (path, urlAndLine) = srcLink.split('=') + return SourceLinkDefinitionImpl( + localDirectory = File(path).canonicalPath, + remoteUrl = URL(urlAndLine.substringBefore("#")), + remoteLineSuffix = urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" }) + } + } +} + +public data class PackageOptionsImpl( + override val matchingRegex: String, + @Deprecated("Use [documentedVisibilities] property for a more flexible control over documented visibilities") + override val includeNonPublic: Boolean, + override val reportUndocumented: Boolean?, + override val skipDeprecated: Boolean, + override val suppress: Boolean, + override val documentedVisibilities: Set, // TODO add default to DokkaDefaults.documentedVisibilities +) : DokkaConfiguration.PackageOptions + + +public data class ExternalDocumentationLinkImpl( + override val url: URL, + override val packageListUrl: URL, +) : DokkaConfiguration.ExternalDocumentationLink diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultExternalLinks.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultExternalLinks.kt new file mode 100644 index 00000000..26e3e0ae --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/defaultExternalLinks.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink +import java.net.URL + + +public fun ExternalDocumentationLink.Companion.jdk(jdkVersion: Int): ExternalDocumentationLinkImpl = + ExternalDocumentationLink( + url = + if (jdkVersion < 11) "https://docs.oracle.com/javase/${jdkVersion}/docs/api/" + else "https://docs.oracle.com/en/java/javase/${jdkVersion}/docs/api/", + packageListUrl = + if (jdkVersion < 11) "https://docs.oracle.com/javase/${jdkVersion}/docs/api/package-list" + else "https://docs.oracle.com/en/java/javase/${jdkVersion}/docs/api/element-list" + ) + + +public fun ExternalDocumentationLink.Companion.kotlinStdlib(): ExternalDocumentationLinkImpl = + ExternalDocumentationLink("https://kotlinlang.org/api/latest/jvm/stdlib/") + + +public fun ExternalDocumentationLink.Companion.androidSdk(): ExternalDocumentationLinkImpl = + ExternalDocumentationLink("https://developer.android.com/reference/kotlin/") + + +public fun ExternalDocumentationLink.Companion.androidX(): ExternalDocumentationLinkImpl = ExternalDocumentationLink( + url = URL("https://developer.android.com/reference/kotlin/"), + packageListUrl = URL("https://developer.android.com/reference/kotlin/androidx/package-list") +) diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/generation/Generation.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/generation/Generation.kt new file mode 100644 index 00000000..9f14912f --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/generation/Generation.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.generation + +import org.jetbrains.dokka.Timer + +public interface Generation { + public fun Timer.generate() + public val generationName: String +} + +// This needs to be public for now but in the future it should be replaced with system of checks provided by EP +public fun exitGenerationGracefully(reason: String): Nothing { + throw GracefulGenerationExit(reason) +} + +public class GracefulGenerationExit(public val reason: String) : Throwable() diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/links/DRI.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/links/DRI.kt new file mode 100644 index 00000000..180d9eb8 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/links/DRI.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.links + +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue + +/** + * [DRI] stands for DokkaResourceIdentifier + */ +public data class DRI( + val packageName: String? = null, + val classNames: String? = null, + val callable: Callable? = null, + val target: DriTarget = PointingToDeclaration, + val extra: String? = null +) { + override fun toString(): String = + "${packageName.orEmpty()}/${classNames.orEmpty()}/${callable?.name.orEmpty()}/${callable?.signature() + .orEmpty()}/$target/${extra.orEmpty()}" + + public companion object { + public val topLevel: DRI = DRI() + } +} + +public object EnumEntryDRIExtra: DRIExtraProperty() + +public abstract class DRIExtraProperty { + public val key: String = this::class.qualifiedName + ?: (this.javaClass.let { it.`package`.name + "." + it.simpleName.ifEmpty { "anonymous" } }) +} + + +public class DRIExtraContainer(public val data: String? = null) { + public val map: MutableMap = if (data != null) OBJECT_MAPPER.readValue(data) else mutableMapOf() + public inline operator fun get(prop: DRIExtraProperty): T? = + map[prop.key]?.let { prop as? T } + + public inline operator fun set(prop: DRIExtraProperty, value: T) { + map[prop.key] = value as Any + } + + public fun encode(): String = OBJECT_MAPPER.writeValueAsString(map) + + private companion object { + private val OBJECT_MAPPER = ObjectMapper() + } +} + +public val DriOfUnit: DRI = DRI("kotlin", "Unit") +public val DriOfAny: DRI = DRI("kotlin", "Any") + +public fun DRI.withClass(name: String): DRI = copy(classNames = if (classNames.isNullOrBlank()) name else "$classNames.$name") + +public fun DRI.withTargetToDeclaration(): DRI = copy(target = PointingToDeclaration) + +public fun DRI.withEnumEntryExtra(): DRI = copy( + extra = DRIExtraContainer(this.extra).also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode() +) + +public val DRI.parent: DRI + get() = when { + extra != null -> when { + DRIExtraContainer(extra)[EnumEntryDRIExtra] != null -> copy( + classNames = classNames?.substringBeforeLast(".", "")?.takeIf { it.isNotBlank() }, + extra = null + ) + else -> copy(extra = null) + } + target != PointingToDeclaration -> copy(target = PointingToDeclaration) + callable != null -> copy(callable = null) + classNames != null -> copy(classNames = classNames.substringBeforeLast(".", "").takeIf { it.isNotBlank() }) + else -> DRI.topLevel + } + +public val DRI.sureClassNames: String + get() = classNames ?: throw IllegalStateException("Malformed DRI. It requires classNames in this context.") + +public data class Callable( + val name: String, + val receiver: TypeReference? = null, + val params: List +) { + public fun signature(): String = "${receiver?.toString().orEmpty()}#${params.joinToString("#")}" + + public companion object +} + +@JsonTypeInfo(use = CLASS) +public sealed class TypeReference { + public companion object +} + +public data class JavaClassReference(val name: String) : TypeReference() { + override fun toString(): String = name +} + +public data class TypeParam(val bounds: List) : TypeReference() + +public data class TypeConstructor( + val fullyQualifiedName: String, + val params: List +) : TypeReference() { + override fun toString(): String = fullyQualifiedName + + (if (params.isNotEmpty()) "[${params.joinToString(",")}]" else "") +} + +public data class RecursiveType(val rank: Int): TypeReference() { + override fun toString(): String = "^".repeat(rank + 1) +} + +public data class Nullable(val wrapped: TypeReference) : TypeReference() { + override fun toString(): String = "$wrapped?" +} + +public object StarProjection : TypeReference() { + override fun toString(): String = "*" +} + +@JsonTypeInfo(use = CLASS) +public sealed class DriTarget { + override fun toString(): String = this.javaClass.simpleName + + public companion object +} + +public data class PointingToGenericParameters(val parameterIndex: Int) : DriTarget() { + override fun toString(): String = "PointingToGenericParameters($parameterIndex)" +} + +public object PointingToDeclaration : DriTarget() + +public data class PointingToCallableParameters(val parameterIndex: Int) : DriTarget() { + override fun toString(): String = "PointingToCallableParameters($parameterIndex)" +} + +public fun DriTarget.nextTarget(): DriTarget = when (this) { + is PointingToGenericParameters -> PointingToGenericParameters(this.parameterIndex + 1) + is PointingToCallableParameters -> PointingToCallableParameters(this.parameterIndex + 1) + else -> this +} diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/CompositeSourceSetID.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/CompositeSourceSetID.kt new file mode 100644 index 00000000..af7d1a5d --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/CompositeSourceSetID.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaSourceSetID + +/** + * A unique composite key of multiple [DokkaSourceSetID] that identifies [DisplaySourceSet]. + * Consists of multiple (non-zero) [DokkaSourceSetID] that the corresponding [DisplaySourceSet] was built from. + * + * Should not be constructed or copied outside of [DisplaySourceSet] instantiation. + */ +public data class CompositeSourceSetID( + private val children: Set +) { + public constructor(sourceSetIDs: Iterable) : this(sourceSetIDs.toSet()) + public constructor(sourceSetId: DokkaSourceSetID) : this(setOf(sourceSetId)) + + init { + require(children.isNotEmpty()) { "Expected at least one source set id" } + } + + public val merged: DokkaSourceSetID = children.sortedBy { it.scopeId + it.sourceSetName }.let { sortedChildren -> + DokkaSourceSetID( + scopeId = sortedChildren.joinToString(separator = "+") { it.scopeId }, + sourceSetName = sortedChildren.joinToString(separator = "+") { it.sourceSetName } + ) + } + + public val all: Set = setOf(merged, *children.toTypedArray()) + + public operator fun contains(sourceSetId: DokkaSourceSetID): Boolean { + return sourceSetId in all + } + + public operator fun contains(sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean { + return sourceSet.sourceSetID in this + } + + public operator fun plus(other: DokkaSourceSetID): CompositeSourceSetID { + return copy(children = children + other) + } +} diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/DisplaySourceSet.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/DisplaySourceSet.kt new file mode 100644 index 00000000..9d637048 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/DisplaySourceSet.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.* +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet + +/** + * Represents a final user-visible source set in the documentable model that is + * used to specify under which source sets/targets current signatures are available, + * can be used to filter in and out all available signatures under the specified source set, + * and, depending on the format, are rendered as "platform" selectors. + * + * E.g. HTML format renders display source sets as "bubbles" that later are used for filtering + * and informational purposes. + * + * [DisplaySourceSet]s typically have a one-to-one correspondence to the build system source sets, + * are created by the base plugin from [DokkaSourceSet] and never tweaked manually. + * [DisplaySourceSet] is uniquely identified by the corresponding [CompositeSourceSetID]. + * + * @property sourceSetIDs unique stable id of the display source set. + * It is composite by definition, as it uniquely defines the source set and all nested source sets. + * Apart from names, it also contains a substitute to a full source set path in order to differentiate + * source sets with the same name in a stable manner. + * @property name corresponds to the name of the original [DokkaSourceSet] + * @property platform the platform of the source set. If the source set is a mix of multiple source sets + * that correspond to multiple KMP platforms, then it is [Platform.common] + */ +public data class DisplaySourceSet( + val sourceSetIDs: CompositeSourceSetID, + val name: String, + val platform: Platform +) { + public constructor(sourceSet: DokkaSourceSet) : this( + sourceSetIDs = CompositeSourceSetID(sourceSet.sourceSetID), + name = sourceSet.displayName, + platform = sourceSet.analysisPlatform + ) +} + +/** + * Transforms the current [DokkaSourceSet] into [DisplaySourceSet], + * matching the corresponding subset of its properties to [DisplaySourceSet] properties. + */ +public fun DokkaSourceSet.toDisplaySourceSet(): DisplaySourceSet = DisplaySourceSet(this) + +/** + * Transforms all the given [DokkaSourceSet]s into [DisplaySourceSet]s. + */ +public fun Iterable.toDisplaySourceSets(): Set = + map { it.toDisplaySourceSet() }.toSet() + +@InternalDokkaApi +@Deprecated("Use computeSourceSetIds() and cache its results instead", replaceWith = ReplaceWith("computeSourceSetIds()")) +public val Iterable.sourceSetIDs: List get() = this.flatMap { it.sourceSetIDs.all } + +@InternalDokkaApi +public fun Iterable.computeSourceSetIds(): Set = + fold(hashSetOf()) { acc, set -> acc.addAll(set.sourceSetIDs.all); acc } diff --git a/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/Documentable.kt b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/Documentable.kt new file mode 100644 index 00000000..c6109f47 --- /dev/null +++ b/dokka-subprojects/core/src/main/kotlin/org/jetbrains/dokka/model/Documentable.kt @@ -0,0 +1,540 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties + +public interface AnnotationTarget + +public abstract class Documentable : WithChildren, + AnnotationTarget { + public abstract val name: String? + public abstract val dri: DRI + public abstract val documentation: SourceSetDependent + public abstract val sourceSets: Set + public abstract val expectPresentInSet: DokkaSourceSet? + abstract override val children: List + + override fun toString(): String = + "${javaClass.simpleName}($dri)" + + override fun equals(other: Any?): Boolean = + other is Documentable && this.dri == other.dri // TODO: https://github.com/Kotlin/dokka/pull/667#discussion_r382555806 + + override fun hashCode(): Int = dri.hashCode() +} + +public typealias SourceSetDependent = Map + +public interface WithSources { + public val sources: SourceSetDependent +} + +public interface WithScope { + public val functions: List + public val properties: List + public val classlikes: List +} + +public interface WithVisibility { + public val visibility: SourceSetDependent +} + +public interface WithType { + public val type: Bound +} + +public interface WithAbstraction { + public val modifier: SourceSetDependent +} + +public sealed class Modifier( + public val name: String +) + +public sealed class KotlinModifier(name: String) : Modifier(name) { + public object Abstract : KotlinModifier("abstract") + public object Open : KotlinModifier("open") + public object Final : KotlinModifier("final") + public object Sealed : KotlinModifier("sealed") + public object Empty : KotlinModifier("") +} + +public sealed class JavaModifier(name: String) : Modifier(name) { + public object Abstract : JavaModifier("abstract") + public object Final : JavaModifier("final") + public object Empty : JavaModifier("") +} + +public interface WithCompanion { + public val companion: DObject? +} + +public interface WithConstructors { + public val constructors: List +} + +public interface WithGenerics { + public val generics: List +} + +public interface WithSupertypes { + public val supertypes: SourceSetDependent> +} + +public interface WithIsExpectActual { + public val isExpectActual: Boolean +} + +public interface Callable : WithVisibility, WithType, WithAbstraction, WithSources, WithIsExpectActual { + public val receiver: DParameter? +} + +public sealed class DClasslike : Documentable(), WithScope, WithVisibility, WithSources, WithIsExpectActual + +public data class DModule( + override val name: String, + val packages: List, + override val documentation: SourceSetDependent, + override val expectPresentInSet: DokkaSourceSet? = null, + override val sourceSets: Set, + override val extra: PropertyContainer = PropertyContainer.empty() +) : Documentable(), WithExtraProperties { + override val dri: DRI = DRI.topLevel + override val children: List + get() = packages + + override fun withNewExtras(newExtras: PropertyContainer): DModule = copy(extra = newExtras) +} + +public data class DPackage( + override val dri: DRI, + override val functions: List, + override val properties: List, + override val classlikes: List, + val typealiases: List, + override val documentation: SourceSetDependent, + override val expectPresentInSet: DokkaSourceSet? = null, + override val sourceSets: Set, + override val extra: PropertyContainer = PropertyContainer.empty() +) : Documentable(), WithScope, WithExtraProperties { + + val packageName: String = dri.packageName.orEmpty() + + /** + * !!! WARNING !!! + * This name is not guaranteed to be a be a canonical/real package name. + * e.g. this will return a human readable version for root packages. + * Use [packageName] or `dri.packageName` instead to obtain the real packageName + */ + override val name: String = packageName.ifBlank { "[root]" } + + override val children: List = properties + functions + classlikes + typealiases + + override fun withNewExtras(newExtras: PropertyContainer): DPackage = copy(extra = newExtras) +} + +public data class DClass( + override val dri: DRI, + override val name: String, + override val constructors: List, + override val functions: List, + override val properties: List, + override val classlikes: List, + override val sources: SourceSetDependent, + override val visibility: SourceSetDependent, + override val companion: DObject?, + override val generics: List, + override val supertypes: SourceSetDependent>, + override val documentation: SourceSetDependent, + override val expectPresentInSet: DokkaSourceSet?, + override val modifier: SourceSetDependent, + override val sourceSets: Set, + override val isExpectActual: Boolean, + override val extra: PropertyContainer = PropertyContainer.empty() +) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, + WithExtraProperties { + + override val children: List + get() = (functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer): DClass = copy(extra = newExtras) +} + +public data class DEnum( + override val dri: DRI, + override val name: String, + val entries: List, + override val documentation: SourceSetDependent, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent, + override val functions: List, + override val properties: List, + override val classlikes: List, + override val visibility: SourceSetDependent, + override val companion: DObject?, + override val constructors: List, + override val supertypes: SourceSetDependent>, + override val sourceSets: Set, + override val isExpectActual: Boolean, + override val extra: PropertyContainer = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithConstructors, WithSupertypes, WithExtraProperties { + override val children: List + get() = (entries + functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer): DEnum = copy(extra = newExtras) +} + +public data class DEnumEntry( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent, + override val expectPresentInSet: DokkaSourceSet?, + override val functions: List, + override val properties: List, + override val classlikes: List, + override val sourceSets: Set