diff options
Diffstat (limited to 'plugins/base/src/main/kotlin')
110 files changed, 0 insertions, 10312 deletions
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt deleted file mode 100644 index ca86d4d5..00000000 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base - -import org.jetbrains.dokka.CoreExtensions -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.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 -import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat -import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider -import org.jetbrains.dokka.base.signatures.SignatureProvider -import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer -import org.jetbrains.dokka.base.transformers.documentables.* -import org.jetbrains.dokka.base.transformers.pages.DefaultSamplesTransformer -import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer -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.sourcelinks.SourceLinksTransformer -import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider -import org.jetbrains.dokka.base.transformers.pages.tags.SinceKotlinTagContentProvider -import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToPageTranslator -import org.jetbrains.dokka.generation.Generation -import org.jetbrains.dokka.plugability.* -import org.jetbrains.dokka.renderers.Renderer -import org.jetbrains.dokka.transformers.documentation.* -import org.jetbrains.dokka.transformers.pages.PageTransformer - -@Suppress("unused") -public class DokkaBase : DokkaPlugin() { - - public val preMergeDocumentableTransformer: ExtensionPoint<PreMergeDocumentableTransformer> by extensionPoint() - public val pageMergerStrategy: ExtensionPoint<PageMergerStrategy> by extensionPoint() - public val commentsToContentConverter: ExtensionPoint<CommentsToContentConverter> by extensionPoint() - public val customTagContentProvider: ExtensionPoint<CustomTagContentProvider> by extensionPoint() - public val signatureProvider: ExtensionPoint<SignatureProvider> by extensionPoint() - public val locationProviderFactory: ExtensionPoint<LocationProviderFactory> by extensionPoint() - public val externalLocationProviderFactory: ExtensionPoint<ExternalLocationProviderFactory> by extensionPoint() - public val outputWriter: ExtensionPoint<OutputWriter> by extensionPoint() - public val htmlPreprocessors: ExtensionPoint<PageTransformer> by extensionPoint() - - @Deprecated("It is not used anymore") - public val tabSortingStrategy: ExtensionPoint<TabSortingStrategy> by extensionPoint() - public val immediateHtmlCommandConsumer: ExtensionPoint<ImmediateHtmlCommandConsumer> by extensionPoint() - - - public val singleGeneration: Extension<Generation, *, *> by extending { - CoreExtensions.generation providing ::SingleModuleGeneration - } - - public val documentableMerger: Extension<DocumentableMerger, *, *> by extending { - CoreExtensions.documentableMerger providing ::DefaultDocumentableMerger - } - - public val deprecatedDocumentableFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::DeprecatedDocumentableFilterTransformer - } - - public val suppressedDocumentableFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::SuppressedByConfigurationDocumentableFilterTransformer - } - - public val suppressedBySuppressTagDocumentableFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::SuppressTagDocumentableFilter - } - - public val documentableVisibilityFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::DocumentableVisibilityFilterTransformer - } - - public val obviousFunctionsVisbilityFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::ObviousFunctionsDocumentableFilterTransformer - } - - public val inheritedEntriesVisbilityFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::InheritedEntriesDocumentableFilterTransformer - } - - public val kotlinArrayDocumentableReplacer: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::KotlinArrayDocumentableReplacerTransformer - } - - public val emptyPackagesFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::EmptyPackagesFilterTransformer order { - after( - deprecatedDocumentableFilter, - suppressedDocumentableFilter, - documentableVisibilityFilter, - suppressedBySuppressTagDocumentableFilter, - obviousFunctionsVisbilityFilter, - inheritedEntriesVisbilityFilter, - ) - } - } - - public val emptyModulesFilter: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer with EmptyModulesFilterTransformer() order { - after(emptyPackagesFilter) - } - } - - public val modulesAndPackagesDocumentation: Extension<PreMergeDocumentableTransformer, *, *> by extending { - preMergeDocumentableTransformer providing ::ModuleAndPackageDocumentationTransformer - } - - public val actualTypealiasAdder: Extension<DocumentableTransformer, *, *> by extending { - CoreExtensions.documentableTransformer with ActualTypealiasAdder() - } - - public val kotlinSignatureProvider: Extension<SignatureProvider, *, *> by extending { - signatureProvider providing ::KotlinSignatureProvider - } - - public val sinceKotlinTransformer: Extension<DocumentableTransformer, *, *> by extending { - CoreExtensions.documentableTransformer providing ::SinceKotlinTransformer applyIf { - SinceKotlinTransformer.shouldDisplaySinceKotlin() - } order { - before(extensionsExtractor) - } - } - - public val inheritorsExtractor: Extension<DocumentableTransformer, *, *> by extending { - CoreExtensions.documentableTransformer with InheritorsExtractorTransformer() - } - - public val undocumentedCodeReporter: Extension<DocumentableTransformer, *, *> by extending { - CoreExtensions.documentableTransformer with ReportUndocumentedTransformer() - } - - public val extensionsExtractor: Extension<DocumentableTransformer, *, *> by extending { - CoreExtensions.documentableTransformer with ExtensionExtractorTransformer() - } - - public val documentableToPageTranslator: Extension<DocumentableToPageTranslator, *, *> by extending { - CoreExtensions.documentableToPageTranslator providing ::DefaultDocumentableToPageTranslator - } - - public val docTagToContentConverter: Extension<CommentsToContentConverter, *, *> by extending { - commentsToContentConverter with DocTagToContentConverter() - } - - public val sinceKotlinTagContentProvider: Extension<CustomTagContentProvider, *, *> by extending { - customTagContentProvider with SinceKotlinTagContentProvider applyIf { - SinceKotlinTransformer.shouldDisplaySinceKotlin() - } - } - - public val pageMerger: Extension<PageTransformer, *, *> by extending { - CoreExtensions.pageTransformer providing ::PageMerger - } - - public val sourceSetMerger: Extension<PageTransformer, *, *> by extending { - CoreExtensions.pageTransformer providing ::SourceSetMergingPageTransformer - } - - public val fallbackMerger: Extension<PageMergerStrategy, *, *> by extending { - pageMergerStrategy providing { ctx -> FallbackPageMergerStrategy(ctx.logger) } - } - - public val sameMethodNameMerger: Extension<PageMergerStrategy, *, *> by extending { - pageMergerStrategy providing { ctx -> SameMethodNamePageMergerStrategy(ctx.logger) } order { - before(fallbackMerger) - } - } - - public val htmlRenderer: Extension<Renderer, *, *> by extending { - CoreExtensions.renderer providing ::HtmlRenderer - } - - public val locationProvider: Extension<LocationProviderFactory, *, *> by extending { - locationProviderFactory providing ::DokkaLocationProviderFactory - } - - public val javadocLocationProvider: Extension<ExternalLocationProviderFactory, *, *> by extending { - externalLocationProviderFactory providing ::JavadocExternalLocationProviderFactory - } - - public val dokkaLocationProvider: Extension<ExternalLocationProviderFactory, *, *> by extending { - externalLocationProviderFactory providing ::DefaultExternalLocationProviderFactory - } - - public val fileWriter: Extension<OutputWriter, *, *> by extending { - outputWriter providing ::FileWriter - } - - public val rootCreator: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors with RootCreator applyIf { !delayTemplateSubstitution } - } - - public val defaultSamplesTransformer: Extension<PageTransformer, *, *> by extending { - CoreExtensions.pageTransformer providing ::DefaultSamplesTransformer order { - before(pageMerger) - } - } - - public val sourceLinksTransformer: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing ::SourceLinksTransformer order { after(rootCreator) } - } - - public val navigationPageInstaller: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing ::NavigationPageInstaller order { after(rootCreator) } - } - - public val scriptsInstaller: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing ::ScriptsInstaller order { after(rootCreator) } - } - - public val stylesInstaller: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing ::StylesInstaller order { after(rootCreator) } - } - - public val assetsInstaller: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors with AssetsInstaller order { after(rootCreator) } applyIf { !delayTemplateSubstitution } - } - - public val customResourceInstaller: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing { ctx -> CustomResourceInstaller(ctx) } order { - after(stylesInstaller) - after(scriptsInstaller) - after(assetsInstaller) - } - } - - public val packageListCreator: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing { - PackageListCreator(it, RecognizedLinkFormat.DokkaHtml) - } order { after(rootCreator) } - } - - public val sourcesetDependencyAppender: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing ::SourcesetDependencyAppender order { after(rootCreator) } - } - - public val resolveLinkConsumer: Extension<ImmediateHtmlCommandConsumer, *, *> by extending { - immediateHtmlCommandConsumer with ResolveLinkConsumer - } - public val replaceVersionConsumer: Extension<ImmediateHtmlCommandConsumer, *, *> by extending { - immediateHtmlCommandConsumer providing ::ReplaceVersionsConsumer - } - public val pathToRootConsumer: Extension<ImmediateHtmlCommandConsumer, *, *> by extending { - immediateHtmlCommandConsumer with PathToRootConsumer - } - public val baseSearchbarDataInstaller: Extension<PageTransformer, *, *> by extending { - htmlPreprocessors providing ::SearchbarDataInstaller order { after(sourceLinksTransformer) } - } - - //<editor-fold desc="Deprecated API left for compatibility"> - @Suppress("DEPRECATION_ERROR") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val kotlinAnalysis: ExtensionPoint<org.jetbrains.dokka.analysis.KotlinAnalysis> by extensionPoint() - - @Suppress("DEPRECATION_ERROR") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val externalDocumentablesProvider: ExtensionPoint<org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider> by extensionPoint() - - @Suppress("DEPRECATION_ERROR") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val externalClasslikesTranslator: ExtensionPoint<org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTranslator> by extensionPoint() - - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val descriptorToDocumentableTranslator: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator, *, *> - get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError() - - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val psiToDocumentableTranslator: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator, *, *> - get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError() - - @Suppress("DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val defaultKotlinAnalysis: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.analysis.KotlinAnalysis, *, *> - get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError() - - @Suppress("DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val defaultExternalDocumentablesProvider: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider, *, *> - get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError() - - @Suppress("DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith") - @Deprecated(message = org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val defaultExternalClasslikesTranslator: org.jetbrains.dokka.plugability.Extension<org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTranslator, *, *> - get() = throw org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError() - //</editor-fold> - - @OptIn(DokkaPluginApiPreview::class) - override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = - PluginApiPreviewAcknowledgement -} diff --git a/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt b/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt deleted file mode 100644 index 34195f65..00000000 --- a/plugins/base/src/main/kotlin/DokkaBaseConfiguration.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base - -import org.jetbrains.dokka.plugability.ConfigurableBlock -import java.io.File -import java.time.Year - -public data class DokkaBaseConfiguration( - var customStyleSheets: List<File> = defaultCustomStyleSheets, - var customAssets: List<File> = defaultCustomAssets, - var separateInheritedMembers: Boolean = separateInheritedMembersDefault, - var footerMessage: String = defaultFooterMessage, - var mergeImplicitExpectActualDeclarations: Boolean = mergeImplicitExpectActualDeclarationsDefault, - var templatesDir: File? = defaultTemplatesDir, - var homepageLink: String? = null, -) : ConfigurableBlock { - public companion object { - public val defaultFooterMessage: String = "© ${Year.now().value} Copyright" - public val defaultCustomStyleSheets: List<File> = emptyList() - public val defaultCustomAssets: List<File> = emptyList() - public const val separateInheritedMembersDefault: Boolean = false - public const val mergeImplicitExpectActualDeclarationsDefault: Boolean = false - public val defaultTemplatesDir: File? = null - } -} diff --git a/plugins/base/src/main/kotlin/deprecated/AnalysisApiDeprecatedError.kt b/plugins/base/src/main/kotlin/deprecated/AnalysisApiDeprecatedError.kt deleted file mode 100644 index 52280b3e..00000000 --- a/plugins/base/src/main/kotlin/deprecated/AnalysisApiDeprecatedError.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.deprecated - -import org.jetbrains.dokka.InternalDokkaApi - -// TODO all API that mentions this message or error can be removed in Dokka >= 2.1 - -internal const val ANALYSIS_API_DEPRECATION_MESSAGE = - "Dokka's Analysis API has been reworked. Please, see the following issue for details and migration options: " + - "https://github.com/Kotlin/dokka/issues/3099" - -@InternalDokkaApi -public class AnalysisApiDeprecatedError : Error(ANALYSIS_API_DEPRECATION_MESSAGE) diff --git a/plugins/base/src/main/kotlin/deprecated/KotlinAnalysisDeprecatedApi.kt b/plugins/base/src/main/kotlin/deprecated/KotlinAnalysisDeprecatedApi.kt deleted file mode 100644 index 1d9e7e9f..00000000 --- a/plugins/base/src/main/kotlin/deprecated/KotlinAnalysisDeprecatedApi.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch", "FunctionName", "UNUSED_PARAMETER", "unused", "DEPRECATION_ERROR", - "DeprecatedCallableAddReplaceWith", "unused" -) - -package org.jetbrains.dokka.analysis - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaSourceSetID -import org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE -import org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.utilities.DokkaLogger -import java.io.Closeable - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public abstract class KotlinAnalysis( - private val parent: KotlinAnalysis? = null -) : Closeable { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public operator fun get(key: DokkaConfiguration.DokkaSourceSet): AnalysisContext = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public operator fun get(key: DokkaSourceSetID): AnalysisContext = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - protected abstract fun find(sourceSetID: DokkaSourceSetID): AnalysisContext? -} - -public class AnalysisContext(environment: Any, facade: Any, private val analysisEnvironment: Any) : Closeable { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val environment: Any get() = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public val facade: Any get() = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public operator fun component1(): Any = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public operator fun component2(): Any = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - override fun close() { throw AnalysisApiDeprecatedError() } -} - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public class DokkaAnalysisConfiguration(public val ignoreCommonBuiltIns: Boolean = false) - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public fun KotlinAnalysis(context: DokkaContext): KotlinAnalysis = throw AnalysisApiDeprecatedError() - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public fun KotlinAnalysis( - sourceSets: List<DokkaConfiguration.DokkaSourceSet>, - logger: DokkaLogger, - analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() -): KotlinAnalysis = throw AnalysisApiDeprecatedError() - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public fun ProjectKotlinAnalysis( - sourceSets: List<DokkaConfiguration.DokkaSourceSet>, - logger: DokkaLogger, - analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() -): KotlinAnalysis = throw AnalysisApiDeprecatedError() - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public fun SamplesKotlinAnalysis( - sourceSets: List<DokkaConfiguration.DokkaSourceSet>, - logger: DokkaLogger, - projectKotlinAnalysis: KotlinAnalysis, - analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration() -): KotlinAnalysis = throw AnalysisApiDeprecatedError() - diff --git a/plugins/base/src/main/kotlin/deprecated/ParsersDeprecatedAPI.kt b/plugins/base/src/main/kotlin/deprecated/ParsersDeprecatedAPI.kt deleted file mode 100644 index 55b1daab..00000000 --- a/plugins/base/src/main/kotlin/deprecated/ParsersDeprecatedAPI.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch", "DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith", "unused") - -package org.jetbrains.dokka.base.parsers - -import org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE -import org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.DocTag -import org.jetbrains.dokka.model.doc.DocumentationNode -import org.jetbrains.dokka.model.doc.TagWrapper - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public abstract class Parser { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public open fun parseStringToDocNode(extractedString: String): DocTag = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public open fun preparse(text: String): String = throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public open fun parseTagWithBody(tagName: String, content: String): TagWrapper = throw AnalysisApiDeprecatedError() -} - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public open class MarkdownParser( - private val externalDri: (String) -> DRI?, - private val kdocLocation: String?, -) : Parser() { - public companion object { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public fun parseFromKDocTag( - @Suppress("UNUSED_PARAMETER") kDocTag: Any?, - @Suppress("UNUSED_PARAMETER") externalDri: (String) -> DRI?, - @Suppress("UNUSED_PARAMETER") kdocLocation: String?, - @Suppress("UNUSED_PARAMETER") parseWithChildren: Boolean = true - ): DocumentationNode = throw AnalysisApiDeprecatedError() - } -} diff --git a/plugins/base/src/main/kotlin/deprecated/ParsersFactoriesDeprecatedAPI.kt b/plugins/base/src/main/kotlin/deprecated/ParsersFactoriesDeprecatedAPI.kt deleted file mode 100644 index 7b84803c..00000000 --- a/plugins/base/src/main/kotlin/deprecated/ParsersFactoriesDeprecatedAPI.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("DeprecatedCallableAddReplaceWith", "PackageDirectoryMismatch", "unused") - -package org.jetbrains.dokka.base.parsers.factories - -import org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE -import org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.doc.DocTag - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public object DocTagsFromStringFactory { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public fun getInstance( - @Suppress("UNUSED_PARAMETER") name: String, - @Suppress("UNUSED_PARAMETER") children: List<DocTag> = emptyList(), - @Suppress("UNUSED_PARAMETER") params: Map<String, String> = emptyMap(), - @Suppress("UNUSED_PARAMETER") body: String? = null, - @Suppress("UNUSED_PARAMETER") dri: DRI? = null, - ): DocTag = throw AnalysisApiDeprecatedError() -} diff --git a/plugins/base/src/main/kotlin/deprecated/TranslatorDescriptorsDeprecatedAPI.kt b/plugins/base/src/main/kotlin/deprecated/TranslatorDescriptorsDeprecatedAPI.kt deleted file mode 100644 index 87d82ccf..00000000 --- a/plugins/base/src/main/kotlin/deprecated/TranslatorDescriptorsDeprecatedAPI.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch", "DEPRECATION_ERROR", "DeprecatedCallableAddReplaceWith", "unused") - -package org.jetbrains.dokka.base.translators.descriptors - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE -import org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DClasslike -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public fun interface ExternalDocumentablesProvider { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public fun findClasslike(dri: DRI, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike? -} - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public class DefaultExternalDocumentablesProvider( - @Suppress("UNUSED_PARAMETER") context: DokkaContext -) : ExternalDocumentablesProvider { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - override fun findClasslike(dri: DRI, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike = - throw AnalysisApiDeprecatedError() -} - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public fun interface ExternalClasslikesTranslator { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - public fun translateClassDescriptor(descriptor: Any, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike -} - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public class DefaultDescriptorToDocumentableTranslator( - private val context: DokkaContext -) : AsyncSourceToDocumentableTranslator, ExternalClasslikesTranslator { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - override suspend fun invokeSuspending(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext, ): DModule = - throw AnalysisApiDeprecatedError() - - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - override fun translateClassDescriptor(descriptor: Any, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike = - throw AnalysisApiDeprecatedError() -} diff --git a/plugins/base/src/main/kotlin/deprecated/TranslatorPsiDeprecatedAPI.kt b/plugins/base/src/main/kotlin/deprecated/TranslatorPsiDeprecatedAPI.kt deleted file mode 100644 index 1906a7b1..00000000 --- a/plugins/base/src/main/kotlin/deprecated/TranslatorPsiDeprecatedAPI.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -@file:Suppress("PackageDirectoryMismatch", "DeprecatedCallableAddReplaceWith", "unused") - -package org.jetbrains.dokka.base.translators.psi - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.deprecated.ANALYSIS_API_DEPRECATION_MESSAGE -import org.jetbrains.dokka.base.deprecated.AnalysisApiDeprecatedError -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator - -@Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) -public class DefaultPsiToDocumentableTranslator( - @Suppress("UNUSED_PARAMETER") context: DokkaContext, -) : AsyncSourceToDocumentableTranslator { - @Deprecated(message = ANALYSIS_API_DEPRECATION_MESSAGE, level = DeprecationLevel.ERROR) - override suspend fun invokeSuspending( - sourceSet: DokkaConfiguration.DokkaSourceSet, - context: DokkaContext, - ): DModule = throw AnalysisApiDeprecatedError() -} diff --git a/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt b/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt deleted file mode 100644 index 8ea109b9..00000000 --- a/plugins/base/src/main/kotlin/generation/SingleModuleGeneration.kt +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.generation - -import kotlinx.coroutines.* -import org.jetbrains.dokka.CoreExtensions -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaException -import org.jetbrains.dokka.Timer -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.generation.Generation -import org.jetbrains.dokka.generation.exitGenerationGracefully -import org.jetbrains.dokka.model.DModule -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.transformers.sources.AsyncSourceToDocumentableTranslator -import org.jetbrains.dokka.utilities.parallelMap -import org.jetbrains.dokka.utilities.report - -public class SingleModuleGeneration(private val context: DokkaContext) : Generation { - - override fun Timer.generate() { - report("Validity check") - validityCheck(context) - - // Step 1: translate sources into documentables & transform documentables (change internally) - report("Creating documentation models") - val modulesFromPlatforms = createDocumentationModels() - - report("Transforming documentation model before merging") - val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms) - - report("Merging documentation models") - val transformedDocumentationAfterMerge = mergeDocumentationModels(transformedDocumentationBeforeMerge) - ?: exitGenerationGracefully("Nothing to document") - - report("Transforming documentation model after merging") - val transformedDocumentation = transformDocumentationModelAfterMerge(transformedDocumentationAfterMerge) - - // Step 2: Generate pages & transform them (change internally) - report("Creating pages") - val pages = createPages(transformedDocumentation) - - report("Transforming pages") - val transformedPages = transformPages(pages) - - // Step 3: Rendering - report("Rendering") - render(transformedPages) - - report("Running post-actions") - runPostActions() - - reportAfterRendering() - } - - override val generationName: String = "documentation for ${context.configuration.moduleName}" - - /** - * Implementation note: it runs in a separated single thread due to existing support of coroutines (see #2936) - */ - @OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class) - public fun createDocumentationModels(): List<DModule> = newSingleThreadContext("Generating documentable model").use { coroutineContext -> // see https://github.com/Kotlin/dokka/issues/3151 - runBlocking(coroutineContext) { - context.configuration.sourceSets.parallelMap { sourceSet -> translateSources(sourceSet, context) }.flatten() - .also { modules -> if (modules.isEmpty()) exitGenerationGracefully("Nothing to document") } - } - } - - - public fun transformDocumentationModelBeforeMerge(modulesFromPlatforms: List<DModule>): List<DModule> { - return context.plugin<DokkaBase>() - .query { preMergeDocumentableTransformer } - .fold(modulesFromPlatforms) { acc, t -> t(acc) } - } - - public fun mergeDocumentationModels(modulesFromPlatforms: List<DModule>): DModule? = - context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms) - - public fun transformDocumentationModelAfterMerge(documentationModel: DModule): DModule = - context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } - - public fun createPages(transformedDocumentation: DModule): RootPageNode = - context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) - - public fun transformPages(pages: RootPageNode): RootPageNode = - context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } - - public fun render(transformedPages: RootPageNode) { - context.single(CoreExtensions.renderer).render(transformedPages) - } - - public fun runPostActions() { - context[CoreExtensions.postActions].forEach { it() } - } - - public fun validityCheck(context: DokkaContext) { - val (preGenerationCheckResult, checkMessages) = context[CoreExtensions.preGenerationCheck].fold( - Pair(true, emptyList<String>()) - ) { acc, checker -> checker() + acc } - if (!preGenerationCheckResult) throw DokkaException( - "Pre-generation validity check failed: ${checkMessages.joinToString(",")}" - ) - } - - public fun reportAfterRendering() { - context.unusedPoints.takeIf { it.isNotEmpty() }?.also { - context.logger.info("Unused extension points found: ${it.joinToString(", ")}") - } - - context.logger.report() - - if (context.configuration.failOnWarning && (context.logger.warningsCount > 0 || context.logger.errorsCount > 0)) { - throw DokkaException( - "Failed with warningCount=${context.logger.warningsCount} and errorCount=${context.logger.errorsCount}" - ) - } - } - - private suspend fun translateSources(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext) = - context[CoreExtensions.sourceToDocumentableTranslator].parallelMap { translator -> - when (translator) { - is AsyncSourceToDocumentableTranslator -> translator.invokeSuspending(sourceSet, context) - else -> translator.invoke(sourceSet, context) - } - } -} diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt deleted file mode 100644 index eed7794e..00000000 --- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.jetbrains.dokka.DokkaException -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.resolvers.local.LocationProvider -import org.jetbrains.dokka.model.DisplaySourceSet -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.renderers.Renderer -import org.jetbrains.dokka.transformers.pages.PageTransformer - -public abstract class DefaultRenderer<T>( - protected val context: DokkaContext -) : Renderer { - - protected val outputWriter: OutputWriter = context.plugin<DokkaBase>().querySingle { outputWriter } - - protected lateinit var locationProvider: LocationProvider - private set - - protected open val preprocessors: Iterable<PageTransformer> = emptyList() - - public abstract fun T.buildHeader(level: Int, node: ContentHeader, content: T.() -> Unit) - public abstract fun T.buildLink(address: String, content: T.() -> Unit) - public abstract fun T.buildList( - node: ContentList, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) - - public abstract fun T.buildLineBreak() - public open fun T.buildLineBreak(node: ContentBreakLine, pageContext: ContentPage) { - buildLineBreak() - } - - public abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage) - public abstract fun T.buildTable( - node: ContentTable, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) - - public abstract fun T.buildText(textNode: ContentText) - public abstract fun T.buildNavigation(page: PageNode) - - public abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String - public abstract fun buildError(node: ContentNode) - - public open fun T.buildPlatformDependent( - content: PlatformHintedContent, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - buildContentNode(content.inner, pageContext) - } - - public open fun T.buildGroup( - node: ContentGroup, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - wrapGroup(node, pageContext) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } } - } - - public open fun T.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { - node.children.forEach { it.build(this, pageContext) } - } - - public open fun T.wrapGroup(node: ContentGroup, pageContext: ContentPage, childrenCallback: T.() -> Unit) { - childrenCallback() - } - - public open fun T.buildText( - nodes: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - nodes.forEach { it.build(this, pageContext, sourceSetRestriction) } - } - - public open fun T.buildCodeBlock(code: ContentCodeBlock, pageContext: ContentPage) { - code.children.forEach { it.build(this, pageContext) } - } - - public open fun T.buildCodeInline(code: ContentCodeInline, pageContext: ContentPage) { - code.children.forEach { it.build(this, pageContext) } - } - - public open fun T.buildHeader( - node: ContentHeader, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - buildHeader(node.level, node) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } } - } - - public open fun ContentNode.build( - builder: T, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - builder.buildContentNode(this, pageContext, sourceSetRestriction) - } - - public fun T.buildContentNode( - node: ContentNode, - pageContext: ContentPage, - sourceSetRestriction: DisplaySourceSet - ) { - buildContentNode(node, pageContext, setOf(sourceSetRestriction)) - } - - public open fun T.buildContentNode( - node: ContentNode, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - if (sourceSetRestriction.isNullOrEmpty() || node.sourceSets.any { it in sourceSetRestriction }) { - when (node) { - is ContentText -> buildText(node) - is ContentHeader -> buildHeader(node, pageContext, sourceSetRestriction) - is ContentCodeBlock -> buildCodeBlock(node, pageContext) - is ContentCodeInline -> buildCodeInline(node, pageContext) - is ContentDRILink -> buildDRILink(node, pageContext, sourceSetRestriction) - is ContentResolvedLink -> buildResolvedLink(node, pageContext, sourceSetRestriction) - is ContentEmbeddedResource -> buildResource(node, pageContext) - is ContentList -> buildList(node, pageContext, sourceSetRestriction) - is ContentTable -> buildTable(node, pageContext, sourceSetRestriction) - is ContentGroup -> buildGroup(node, pageContext, sourceSetRestriction) - is ContentBreakLine -> buildLineBreak(node, pageContext) - is PlatformHintedContent -> buildPlatformDependent(node, pageContext, sourceSetRestriction) - is ContentDivergentGroup -> buildDivergent(node, pageContext) - is ContentDivergentInstance -> buildDivergentInstance(node, pageContext) - else -> buildError(node) - } - } - } - - public open fun T.buildDRILink( - node: ContentDRILink, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - locationProvider.resolve(node.address, node.sourceSets, pageContext)?.let { address -> - buildLink(address) { - buildText(node.children, pageContext, sourceSetRestriction) - } - } ?: buildText(node.children, pageContext, sourceSetRestriction) - } - - public open fun T.buildResolvedLink( - node: ContentResolvedLink, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - buildLink(node.address) { - buildText(node.children, pageContext, sourceSetRestriction) - } - } - - public open fun T.buildDivergentInstance(node: ContentDivergentInstance, pageContext: ContentPage) { - node.before?.build(this, pageContext) - node.divergent.build(this, pageContext) - node.after?.build(this, pageContext) - } - - public open fun buildPageContent(context: T, page: ContentPage) { - context.buildNavigation(page) - page.content.build(context, page) - } - - public open suspend fun renderPage(page: PageNode) { - val path by lazy { - locationProvider.resolve(page, skipExtension = true) - ?: throw DokkaException("Cannot resolve path for ${page.name}") - } - when (page) { - is ContentPage -> outputWriter.write(path, buildPage(page) { c, p -> buildPageContent(c, p) }, ".html") - is RendererSpecificPage -> when (val strategy = page.strategy) { - is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path) - is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "") - is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".html") - is RenderingStrategy.DriLocationResolvableWrite -> outputWriter.write( - path, - strategy.contentToResolve { dri, sourcesets -> - locationProvider.resolve(dri, sourcesets) - }, - "" - ) - is RenderingStrategy.PageLocationResolvableWrite -> outputWriter.write( - path, - strategy.contentToResolve { pageToLocate, context -> - locationProvider.resolve(pageToLocate, context) - }, - "" - ) - RenderingStrategy.DoNothing -> Unit - } - else -> throw AssertionError( - "Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content" - ) - } - } - - private suspend fun renderPages(root: PageNode) { - coroutineScope { - renderPage(root) - - root.children.forEach { - launch { renderPages(it) } - } - } - } - - override fun render(root: RootPageNode) { - val newRoot = preprocessors.fold(root) { acc, t -> t(acc) } - - locationProvider = - context.plugin<DokkaBase>().querySingle { locationProviderFactory }.getLocationProvider(newRoot) - - runBlocking(Dispatchers.Default) { - renderPages(newRoot) - } - } - - protected fun ContentDivergentGroup.groupDivergentInstances( - pageContext: ContentPage, - beforeTransformer: (ContentDivergentInstance, ContentPage, DisplaySourceSet) -> String, - afterTransformer: (ContentDivergentInstance, ContentPage, DisplaySourceSet) -> String - ): Map<SerializedBeforeAndAfter, List<InstanceWithSource>> = - children.flatMap { instance -> - instance.sourceSets.map { sourceSet -> - Pair(instance, sourceSet) to Pair( - beforeTransformer(instance, pageContext, sourceSet), - afterTransformer(instance, pageContext, sourceSet) - ) - } - }.groupBy( - Pair<InstanceWithSource, SerializedBeforeAndAfter>::second, - Pair<InstanceWithSource, SerializedBeforeAndAfter>::first - ) -} - -internal typealias SerializedBeforeAndAfter = Pair<String, String> -internal typealias InstanceWithSource = Pair<ContentDivergentInstance, DisplaySourceSet> - -public fun ContentPage.sourceSets(): Set<DisplaySourceSet> = this.content.sourceSets diff --git a/plugins/base/src/main/kotlin/renderers/FileWriter.kt b/plugins/base/src/main/kotlin/renderers/FileWriter.kt deleted file mode 100644 index 1a1c3b42..00000000 --- a/plugins/base/src/main/kotlin/renderers/FileWriter.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext -import org.jetbrains.dokka.plugability.DokkaContext -import java.io.File -import java.io.IOException -import java.net.URI -import java.nio.file.* - -public class FileWriter( - public val context: DokkaContext -): OutputWriter { - private val createdFiles: MutableSet<String> = mutableSetOf() - private val createdFilesMutex = Mutex() - private val jarUriPrefix = "jar:file:" - private val root = context.configuration.outputDir - - override suspend fun write(path: String, text: String, ext: String) { - if (checkFileCreated(path)) return - - try { - val dir = Paths.get(root.absolutePath, path.dropLastWhile { it != '/' }).toFile() - withContext(Dispatchers.IO) { - dir.mkdirsOrFail() - Files.write(Paths.get(root.absolutePath, "$path$ext"), text.lines()) - } - } catch (e: Throwable) { - context.logger.error("Failed to write $this. ${e.message}") - e.printStackTrace() - } - } - - private suspend fun checkFileCreated(path: String): Boolean = createdFilesMutex.withLock { - if (createdFiles.contains(path)) { - context.logger.error("An attempt to write ${root}/$path several times!") - return true - } - createdFiles.add(path) - return false - } - - override suspend fun writeResources(pathFrom: String, pathTo: String) { - if (javaClass.getResource(pathFrom)?.toURI()?.toString()?.startsWith(jarUriPrefix) == true) { - copyFromJar(pathFrom, pathTo) - } else { - copyFromDirectory(pathFrom, pathTo) - } - } - - - private suspend fun copyFromDirectory(pathFrom: String, pathTo: String) { - val dest = Paths.get(root.path, pathTo).toFile() - val uri = javaClass.getResource(pathFrom)?.toURI() - val file = uri?.let { File(it) } ?: File(pathFrom) - withContext(Dispatchers.IO) { - file.copyRecursively(dest, true) - } - } - - private suspend fun copyFromJar(pathFrom: String, pathTo: String) { - val rebase = fun(path: String) = - "$pathTo/${path.removePrefix(pathFrom)}" - val dest = Paths.get(root.path, pathTo).toFile() - if(dest.isDirectory){ - dest.mkdirsOrFail() - } else { - dest.parentFile.mkdirsOrFail() - } - val uri = javaClass.getResource(pathFrom).toURI() - val fs = getFileSystemForURI(uri) - val path = fs.getPath(pathFrom) - for (file in Files.walk(path).iterator()) { - if (Files.isDirectory(file)) { - val dirPath = file.toAbsolutePath().toString() - withContext(Dispatchers.IO) { - Paths.get(root.path, rebase(dirPath)).toFile().mkdirsOrFail() - } - } else { - val filePath = file.toAbsolutePath().toString() - withContext(Dispatchers.IO) { - Paths.get(root.path, rebase(filePath)).toFile().writeBytes( - this@FileWriter.javaClass.getResourceAsStream(filePath).use { it?.readBytes() } - ?: throw IllegalStateException("Can not get a resource from $filePath") - ) - } - } - } - } - - private fun File.mkdirsOrFail() { - if (!mkdirs() && !exists()) { - throw IOException("Failed to create directory $this") - } - } - - private fun getFileSystemForURI(uri: URI): FileSystem = - try { - FileSystems.newFileSystem(uri, emptyMap<String, Any>()) - } catch (e: FileSystemAlreadyExistsException) { - FileSystems.getFileSystem(uri) - } -} diff --git a/plugins/base/src/main/kotlin/renderers/OutputWriter.kt b/plugins/base/src/main/kotlin/renderers/OutputWriter.kt deleted file mode 100644 index 3fdd1802..00000000 --- a/plugins/base/src/main/kotlin/renderers/OutputWriter.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -public interface OutputWriter { - - public suspend fun write(path: String, text: String, ext: String) - public suspend fun writeResources(pathFrom: String, pathTo: String) -} diff --git a/plugins/base/src/main/kotlin/renderers/PackageListService.kt b/plugins/base/src/main/kotlin/renderers/PackageListService.kt deleted file mode 100644 index 3ed6cd21..00000000 --- a/plugins/base/src/main/kotlin/renderers/PackageListService.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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.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 - -public class PackageListService( - public val context: DokkaContext, - public val rootPage: RootPageNode -) { - - public fun createPackageList(module: ModulePage, format: LinkFormat): String { - - val packages = mutableSetOf<String>() - val nonStandardLocations = mutableMapOf<String, String>() - - val locationProvider = - context.plugin<DokkaBase>().querySingle { locationProviderFactory }.getLocationProvider(rootPage) - - fun visit(node: PageNode) { - if (node is PackagePage) { - node.name - .takeUnless { name -> name.startsWith("[") && name.endsWith("]") } // Do not include the package name for declarations without one - ?.let { packages.add(it) } - } - - 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 } - - if (dri != DRI.topLevel && locationProvider.expectedLocationForDri(dri) != nodeLocation) { - nonStandardLocations[dri.toString()] = "$nodeLocation.${format.linkExtension}" - } - } - - node.children.forEach { visit(it) } - } - - visit(module) - return renderPackageList( - nonStandardLocations = nonStandardLocations, - modules = mapOf(SINGLE_MODULE_NAME to packages), - format = format.formatName, - linkExtension = format.linkExtension - ) - } - - public companion object { - public fun renderPackageList( - nonStandardLocations: Map<String, String>, - modules: Map<String, Set<String>>, - format: String, - linkExtension: String - ): String = buildString { - appendLine("$DOKKA_PARAM_PREFIX.format:${format}") - appendLine("$DOKKA_PARAM_PREFIX.linkExtension:${linkExtension}") - nonStandardLocations.map { (signature, location) -> - "$DOKKA_PARAM_PREFIX.location:$signature\u001f$location" - }.sorted().joinTo(this, separator = "\n", postfix = "\n") - - modules.mapNotNull { (module, packages) -> - ("$MODULE_DELIMITER$module\n".takeIf { module != SINGLE_MODULE_NAME }.orEmpty() + - packages.filter(String::isNotBlank).sorted().joinToString(separator = "\n")) - .takeIf { packages.isNotEmpty() } - }.joinTo(this, separator = "\n", postfix = "\n") - } - } -} diff --git a/plugins/base/src/main/kotlin/renderers/TabSortingStrategy.kt b/plugins/base/src/main/kotlin/renderers/TabSortingStrategy.kt deleted file mode 100644 index 665b6717..00000000 --- a/plugins/base/src/main/kotlin/renderers/TabSortingStrategy.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -import org.jetbrains.dokka.pages.ContentNode - -public interface TabSortingStrategy { - public fun <T: ContentNode> sort(tabs: Collection<T>) : List<T> -} diff --git a/plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt b/plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt deleted file mode 100644 index 0fcb0efb..00000000 --- a/plugins/base/src/main/kotlin/renderers/contentTypeChecking.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -import org.jetbrains.dokka.base.renderers.HtmlFileExtensions.imageExtensions -import org.jetbrains.dokka.pages.ContentEmbeddedResource -import java.io.File - -public fun ContentEmbeddedResource.isImage(): Boolean { - return File(address).extension.toLowerCase() in imageExtensions -} - -public val String.URIExtension: String - get() = substringBefore('?').substringAfterLast('.') - -public fun String.isImage(): Boolean = - URIExtension in imageExtensions - -public object HtmlFileExtensions { - public val imageExtensions: Set<String> = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg") -} - diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlContent.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlContent.kt deleted file mode 100644 index 1ef6e04c..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlContent.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import org.jetbrains.dokka.pages.ContentBreakLine -import org.jetbrains.dokka.pages.Style - - -/** - * Html-specific style that represents <hr> tag if used in conjunction with [ContentBreakLine] - */ -internal object HorizontalBreakLineStyle : Style { - // this exists as a simple internal solution to avoid introducing unnecessary public API on content level. - // If you have the need to implement proper horizontal divider (i.e to support `---` markdown element), - // consider removing this and providing proper API for all formats and levels -} diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt deleted file mode 100644 index 083876d5..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import kotlinx.html.* -import kotlinx.html.stream.createHTML -import org.jetbrains.dokka.DokkaSourceSetID -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.renderers.* -import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer -import org.jetbrains.dokka.base.renderers.html.innerTemplating.DefaultTemplateModelFactory -import org.jetbrains.dokka.base.renderers.html.innerTemplating.DefaultTemplateModelMerger -import org.jetbrains.dokka.base.renderers.html.innerTemplating.DokkaTemplateTypes -import org.jetbrains.dokka.base.renderers.html.innerTemplating.HtmlTemplater -import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint -import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider -import org.jetbrains.dokka.base.templating.* -import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions -import org.jetbrains.dokka.base.translators.documentables.shouldDocumentConstructors -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.dokka.pages.HtmlContent -import org.jetbrains.dokka.plugability.* -import org.jetbrains.dokka.transformers.pages.PageTransformer -import org.jetbrains.dokka.utilities.htmlEscape - -internal const val TEMPLATE_REPLACEMENT: String = "###" -internal const val TOGGLEABLE_CONTENT_TYPE_ATTR = "data-togglable" - -public open class HtmlRenderer( - context: DokkaContext -) : DefaultRenderer<FlowContent>(context) { - private val sourceSetDependencyMap: Map<DokkaSourceSetID, List<DokkaSourceSetID>> = - context.configuration.sourceSets.associate { sourceSet -> - sourceSet.sourceSetID to context.configuration.sourceSets - .map { it.sourceSetID } - .filter { it in sourceSet.dependentSourceSets } - } - - private val templateModelFactories = listOf(DefaultTemplateModelFactory(context)) // TODO: Make extension point - private val templateModelMerger = DefaultTemplateModelMerger() - private val templater = HtmlTemplater(context).apply { - setupSharedModel(templateModelMerger.invoke(templateModelFactories) { buildSharedModel() }) - } - - private var shouldRenderSourceSetTabs: Boolean = false - - override val preprocessors: List<PageTransformer> = context.plugin<DokkaBase>().query { htmlPreprocessors } - - /** - * Tabs themselves are created in HTML plugin since, currently, only HTML format supports them. - * [TabbedContentType] is used to mark content that should be inside tab content. - * A tab can display multiple [TabbedContentType]. - * The content style [ContentStyle.TabbedContent] is used to determine where tabs will be generated. - * - * @see TabbedContentType - * @see ContentStyle.TabbedContent - */ - private fun createTabs(pageContext: ContentPage): List<ContentTab> { - return when(pageContext) { - is ClasslikePage -> createTabsForClasslikes(pageContext) - is PackagePage -> createTabsForPackage(pageContext) - else -> throw IllegalArgumentException("Page ${pageContext.name} cannot have tabs") - } - } - - private fun createTabsForClasslikes(page: ClasslikePage): List<ContentTab> { - val documentables = page.documentables - val csEnum = documentables.filterIsInstance<DEnum>() - val csWithConstructor = documentables.filterIsInstance<WithConstructors>() - val scopes = documentables.filterIsInstance<WithScope>() - val constructorsToDocumented = csWithConstructor.flatMap { it.constructors } - - val containsRenderableConstructors = constructorsToDocumented.isNotEmpty() && documentables.shouldDocumentConstructors() - val containsRenderableMembers = - containsRenderableConstructors || scopes.any { it.classlikes.isNotEmpty() || it.functions.isNotEmpty() || it.properties.isNotEmpty() } - - @Suppress("UNCHECKED_CAST") - val extensions = (documentables as List<WithExtraProperties<DClasslike>>).flatMap { - it.extra[CallableExtensions]?.extensions - ?.filterIsInstance<Documentable>().orEmpty() - } - .distinctBy { it.sourceSets to it.dri } // [Documentable] has expensive equals/hashCode at the moment, see #2620 - return listOfNotNull( - if(!containsRenderableMembers) null else - ContentTab( - "Members", - listOf( - BasicTabbedContentType.CONSTRUCTOR, - BasicTabbedContentType.TYPE, - BasicTabbedContentType.PROPERTY, - BasicTabbedContentType.FUNCTION - ) - ), - if (extensions.isEmpty()) null else ContentTab( - "Members & Extensions", - listOf( - BasicTabbedContentType.CONSTRUCTOR, - BasicTabbedContentType.TYPE, - BasicTabbedContentType.PROPERTY, - BasicTabbedContentType.FUNCTION, - BasicTabbedContentType.EXTENSION_PROPERTY, - BasicTabbedContentType.EXTENSION_FUNCTION - ) - ), - if(csEnum.isEmpty()) null else ContentTab( - "Entries", - listOf( - BasicTabbedContentType.ENTRY - ) - ) - ) - } - - private fun createTabsForPackage(page: PackagePage): List<ContentTab> { - val p = page.documentables.single() as DPackage - return listOfNotNull( - if (p.typealiases.isEmpty() && p.classlikes.isEmpty()) null else ContentTab( - "Types", - listOf( - BasicTabbedContentType.TYPE, - ) - ), - if (p.functions.isEmpty()) null else ContentTab( - "Functions", - listOf( - BasicTabbedContentType.FUNCTION, - BasicTabbedContentType.EXTENSION_FUNCTION, - ) - ), - if (p.properties.isEmpty()) null else ContentTab( - "Properties", - listOf( - BasicTabbedContentType.PROPERTY, - BasicTabbedContentType.EXTENSION_PROPERTY, - ) - ) - ) - } - - private fun <R> TagConsumer<R>.prepareForTemplates() = - if (context.configuration.delayTemplateSubstitution || this is ImmediateResolutionTagConsumer) this - else ImmediateResolutionTagConsumer(this, context) - - override fun FlowContent.wrapGroup( - node: ContentGroup, - pageContext: ContentPage, - childrenCallback: FlowContent.() -> Unit - ) { - val additionalClasses = node.style.joinToString(" ") { it.toString().toLowerCase() } - return when { - node.hasStyle(ContentStyle.TabbedContent) -> div(additionalClasses) { - val contentTabs = createTabs(pageContext) - - div(classes = "tabs-section") { - attributes["tabs-section"] = "tabs-section" - contentTabs.forEachIndexed { index, contentTab -> - button(classes = "section-tab") { - if (index == 0) attributes["data-active"] = "" - attributes[TOGGLEABLE_CONTENT_TYPE_ATTR] = - contentTab.tabbedContentTypes.joinToString(",") { it.toHtmlAttribute() } - text(contentTab.text) - } - } - } - div(classes = "tabs-section-body") { - childrenCallback() - } - } - node.hasStyle(ContentStyle.WithExtraAttributes) -> div { - node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } - childrenCallback() - } - node.dci.kind in setOf(ContentKind.Symbol) -> div("symbol $additionalClasses") { - childrenCallback() - } - node.hasStyle(ContentStyle.KDocTag) -> span("kdoc-tag") { childrenCallback() } - node.hasStyle(ContentStyle.Footnote) -> div("footnote") { childrenCallback() } - node.hasStyle(TextStyle.BreakableAfter) -> { - span { childrenCallback() } - wbr { } - } - node.hasStyle(TextStyle.Breakable) -> { - span("breakable-word") { childrenCallback() } - } - node.hasStyle(TextStyle.Span) -> span { childrenCallback() } - node.dci.kind == ContentKind.Symbol -> div("symbol $additionalClasses") { - childrenCallback() - } - node.dci.kind == SymbolContentKind.Parameters -> { - span("parameters $additionalClasses") { - childrenCallback() - } - } - node.dci.kind == SymbolContentKind.Parameter -> { - span("parameter $additionalClasses") { - childrenCallback() - } - } - node.hasStyle(TextStyle.InlineComment) -> div("inline-comment") { childrenCallback() } - node.dci.kind == ContentKind.BriefComment -> div("brief $additionalClasses") { childrenCallback() } - node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") { //TODO this can be removed - childrenCallback() - } - node.dci.kind == ContentKind.Deprecation -> div("deprecation-content") { childrenCallback() } - node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() } - node.hasStyle(TextStyle.Block) -> div(additionalClasses) { - childrenCallback() - } - node.hasStyle(TextStyle.Quotation) -> blockQuote(additionalClasses) { childrenCallback() } - node.hasStyle(TextStyle.FloatingRight) -> span("clearfix") { span("floating-right") { childrenCallback() } } - node.hasStyle(TextStyle.Strikethrough) -> strike { childrenCallback() } - node.isAnchorable -> buildAnchor( - node.anchor!!, - node.anchorLabel!!, - node.buildSourceSetFilterValues() - ) { childrenCallback() } - node.extra[InsertTemplateExtra] != null -> node.extra[InsertTemplateExtra]?.let { templateCommand(it.command) } - ?: Unit - node.hasStyle(ListStyle.DescriptionTerm) -> DT(emptyMap(), consumer).visit { - this@wrapGroup.childrenCallback() - } - node.hasStyle(ListStyle.DescriptionDetails) -> DD(emptyMap(), consumer).visit { - this@wrapGroup.childrenCallback() - } - node.extra.extraTabbedContentType() != null -> div() { - node.extra.extraTabbedContentType()?.let { attributes[TOGGLEABLE_CONTENT_TYPE_ATTR] = it.value.toHtmlAttribute() } - this@wrapGroup.childrenCallback() - } - else -> childrenCallback() - } - } - - private fun FlowContent.copyButton() = span(classes = "top-right-position") { - span("copy-icon") - copiedPopup("Content copied to clipboard", "popup-to-left") - } - - private fun FlowContent.copiedPopup(notificationContent: String, additionalClasses: String = "") = - div("copy-popup-wrapper $additionalClasses") { - span("copy-popup-icon") - span { - text(notificationContent) - } - } - - override fun FlowContent.buildPlatformDependent( - content: PlatformHintedContent, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - buildPlatformDependent( - content.sourceSets.filter { - sourceSetRestriction == null || it in sourceSetRestriction - }.associateWith { setOf(content.inner) }, - pageContext, - content.extra, - content.style - ) - } - - private fun FlowContent.buildPlatformDependent( - nodes: Map<DisplaySourceSet, Collection<ContentNode>>, - pageContext: ContentPage, - extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), - styles: Set<Style> = emptySet(), - shouldHaveTabs: Boolean = shouldRenderSourceSetTabs - ) { - val contents = contentsForSourceSetDependent(nodes, pageContext) - val isOnlyCommonContent = contents.singleOrNull()?.let { (sourceSet, _) -> - sourceSet.platform == Platform.common - && sourceSet.name.equals("common", ignoreCase = true) - && sourceSet.sourceSetIDs.all.all { sourceSetDependencyMap[it]?.isEmpty() == true } - } ?: false - - // little point in rendering a single "common" tab - it can be - // assumed that code without any tabs is common by default - val renderTabs = shouldHaveTabs && !isOnlyCommonContent - - val divStyles = "platform-hinted ${styles.joinToString()}" + if (renderTabs) " with-platform-tabs" else "" - div(divStyles) { - attributes["data-platform-hinted"] = "data-platform-hinted" - extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } - if (renderTabs) { - div("platform-bookmarks-row") { - attributes["data-toggle-list"] = "data-toggle-list" - contents.forEachIndexed { index, pair -> - button(classes = "platform-bookmark") { - attributes["data-filterable-current"] = pair.first.sourceSetIDs.merged.toString() - attributes["data-filterable-set"] = pair.first.sourceSetIDs.merged.toString() - if (index == 0) attributes["data-active"] = "" - attributes["data-toggle"] = pair.first.sourceSetIDs.merged.toString() - text(pair.first.name) - } - } - } - } - contents.forEach { - consumer.onTagContentUnsafe { +it.second } - } - } - } - - private fun contentsForSourceSetDependent( - nodes: Map<DisplaySourceSet, Collection<ContentNode>>, - pageContext: ContentPage, - ): List<Pair<DisplaySourceSet, String>> { - var counter = 0 - return nodes.toList().map { (sourceSet, elements) -> - val htmlContent = createHTML(prettyPrint = false).prepareForTemplates().div { - elements.forEach { - buildContentNode(it, pageContext, sourceSet) - } - }.stripDiv() - sourceSet to createHTML(prettyPrint = false).prepareForTemplates() - .div(classes = "content sourceset-dependent-content") { - if (counter++ == 0) attributes["data-active"] = "" - attributes["data-togglable"] = sourceSet.sourceSetIDs.merged.toString() - unsafe { - +htmlContent - } - } - }.sortedBy { it.first.comparableKey } - } - - override fun FlowContent.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { - if (node.implicitlySourceSetHinted) { - val groupedInstancesBySourceSet = node.children.flatMap { instance -> - instance.sourceSets.map { sourceSet -> instance to sourceSet } - }.groupBy( - Pair<ContentDivergentInstance, DisplaySourceSet>::second, - Pair<ContentDivergentInstance, DisplaySourceSet>::first - ) - - val nodes = groupedInstancesBySourceSet.mapValues { - val distinct = - groupDivergentInstancesWithSourceSet(it.value, it.key, pageContext, - beforeTransformer = { instance, _, sourceSet -> - createHTML(prettyPrint = false).prepareForTemplates().div { - instance.before?.let { before -> - buildContentNode(before, pageContext, sourceSet) - } - }.stripDiv() - }, - afterTransformer = { instance, _, sourceSet -> - createHTML(prettyPrint = false).prepareForTemplates().div { - instance.after?.let { after -> - buildContentNode(after, pageContext, sourceSet) - } - }.stripDiv() - }) - - val isPageWithOverloadedMembers = pageContext is MemberPage && pageContext.documentables().size > 1 - - val contentOfSourceSet = mutableListOf<ContentNode>() - distinct.onEachIndexed{ index, (_, distinctInstances) -> - distinctInstances.firstOrNull()?.before?.let { contentOfSourceSet.add(it) } - contentOfSourceSet.addAll(distinctInstances.map { it.divergent }) - (distinctInstances.firstOrNull()?.after ?: if (index != distinct.size - 1) ContentBreakLine(setOf(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) { - if (isPageWithOverloadedMembers) { - // add some spacing and distinction between function/property overloads. - // not ideal, but there's no other place to modify overloads page atm - contentOfSourceSet.add(ContentBreakLine(setOf(it.key), style = setOf(HorizontalBreakLineStyle))) - } else { - contentOfSourceSet.add(ContentBreakLine(setOf(it.key))) - } - } - } - contentOfSourceSet - } - buildPlatformDependent(nodes, pageContext) - } else { - node.children.forEach { - buildContentNode(it.divergent, pageContext, it.sourceSets) - } - } - } - - private fun groupDivergentInstancesWithSourceSet( - instances: List<ContentDivergentInstance>, - sourceSet: DisplaySourceSet, - pageContext: ContentPage, - beforeTransformer: (ContentDivergentInstance, ContentPage, DisplaySourceSet) -> String, - afterTransformer: (ContentDivergentInstance, ContentPage, DisplaySourceSet) -> String - ): Map<SerializedBeforeAndAfter, List<ContentDivergentInstance>> = - instances.map { instance -> - instance to Pair( - beforeTransformer(instance, pageContext, sourceSet), - afterTransformer(instance, pageContext, sourceSet) - ) - }.groupBy( - Pair<ContentDivergentInstance, SerializedBeforeAndAfter>::second, - Pair<ContentDivergentInstance, SerializedBeforeAndAfter>::first - ) - - private fun ContentPage.documentables(): List<Documentable> { - return (this as? WithDocumentables)?.documentables ?: emptyList() - } - - override fun FlowContent.buildList( - node: ContentList, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - return when { - node.ordered -> { - ol { buildListItems(node.children, pageContext, sourceSetRestriction) } - } - node.hasStyle(ListStyle.DescriptionList) -> { - dl { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } } - } - else -> { - ul { buildListItems(node.children, pageContext, sourceSetRestriction) } - } - } - } - - public open fun OL.buildListItems( - items: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - items.forEach { - if (it is ContentList) - buildList(it, pageContext) - else - li { it.build(this, pageContext, sourceSetRestriction) } - } - } - - public open fun UL.buildListItems( - items: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? = null - ) { - items.forEach { - if (it is ContentList) - buildList(it, pageContext) - else - li { it.build(this, pageContext) } - } - } - - override fun FlowContent.buildResource( - node: ContentEmbeddedResource, - pageContext: ContentPage - ) { // TODO: extension point there - if (node.isImage()) { - img(src = node.address, alt = node.altText) - } else { - println("Unrecognized resource type: $node") - } - } - - private fun FlowContent.buildRow( - node: ContentGroup, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - node.children - .filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } - .takeIf { it.isNotEmpty() } - ?.let { - when (pageContext) { - is MultimoduleRootPage -> buildRowForMultiModule(node, it, pageContext, sourceSetRestriction) - is ModulePage -> buildRowForModule(node, it, pageContext, sourceSetRestriction) - else -> buildRowForContent(node, it, pageContext, sourceSetRestriction) - } - } - } - - private fun FlowContent.buildRowForMultiModule( - contextNode: ContentGroup, - toRender: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - buildAnchor(contextNode) - div(classes = "table-row") { - div("main-subrow " + contextNode.style.joinToString(separator = " ")) { - buildRowHeaderLink(toRender, pageContext, sourceSetRestriction, contextNode.anchor, "w-100") - div { - buildRowBriefSectionForDocs(toRender, pageContext, sourceSetRestriction) - } - } - } - } - - private fun FlowContent.buildRowForModule( - contextNode: ContentGroup, - toRender: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - buildAnchor(contextNode) - div(classes = "table-row") { - addSourceSetFilteringAttributes(contextNode) - div { - div("main-subrow " + contextNode.style.joinToString(separator = " ")) { - buildRowHeaderLink(toRender, pageContext, sourceSetRestriction, contextNode.anchor) - div("pull-right") { - if (ContentKind.shouldBePlatformTagged(contextNode.dci.kind)) { - createPlatformTags(contextNode, cssClasses = "no-gutters") - } - } - } - div { - buildRowBriefSectionForDocs(toRender, pageContext, sourceSetRestriction) - } - } - } - } - - private fun FlowContent.buildRowForContent( - contextNode: ContentGroup, - toRender: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - buildAnchor(contextNode) - div(classes = "table-row") { - contextNode.extra.extraTabbedContentType()?.let { attributes[TOGGLEABLE_CONTENT_TYPE_ATTR] = it.value.toHtmlAttribute() } - addSourceSetFilteringAttributes(contextNode) - div("main-subrow keyValue " + contextNode.style.joinToString(separator = " ")) { - buildRowHeaderLink(toRender, pageContext, sourceSetRestriction, contextNode.anchor) - div { - toRender.filter { it !is ContentLink && !it.hasStyle(ContentStyle.RowTitle) } - .takeIf { it.isNotEmpty() }?.let { - div("title") { - it.forEach { - it.build(this, pageContext, sourceSetRestriction) - } - } - } - } - } - } - } - - private fun FlowContent.buildRowHeaderLink( - toRender: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>?, - anchorDestination: String?, - classes: String = "" - ) { - toRender.filter { it is ContentLink || it.hasStyle(ContentStyle.RowTitle) }.takeIf { it.isNotEmpty() }?.let { - div(classes) { - it.filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } - .forEach { - span("inline-flex") { - div { - it.build(this, pageContext, sourceSetRestriction) - } - if (it is ContentLink && !anchorDestination.isNullOrBlank()) { - buildAnchorCopyButton(anchorDestination) - } - } - } - } - } - } - - private fun FlowContent.addSourceSetFilteringAttributes( - contextNode: ContentGroup, - ) { - attributes["data-filterable-current"] = contextNode.buildSourceSetFilterValues() - attributes["data-filterable-set"] = contextNode.buildSourceSetFilterValues() - } - - private fun ContentNode.buildSourceSetFilterValues(): String { - // This value is used in HTML and JS for filtering out source set declarations, - // it is expected that the separator is the same here and there. - // See https://github.com/Kotlin/dokka/issues/3011#issuecomment-1568620493 - return this.sourceSets.joinToString(",") { - it.sourceSetIDs.merged.toString() - } - } - - private fun FlowContent.buildRowBriefSectionForDocs( - toRender: List<ContentNode>, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>?, - ) { - toRender.filter { it !is ContentLink }.takeIf { it.isNotEmpty() }?.let { - it.forEach { - span(classes = if (it.dci.kind == ContentKind.Comment) "brief-comment" else "") { - it.build(this, pageContext, sourceSetRestriction) - } - } - } - } - - private fun FlowContent.createPlatformTagBubbles(sourceSets: List<DisplaySourceSet>, cssClasses: String = "") { - div("platform-tags $cssClasses") { - sourceSets.sortedBy { it.name }.forEach { - div("platform-tag") { - when (it.platform.key) { - "common" -> classes = classes + "common-like" - "native" -> classes = classes + "native-like" - "jvm" -> classes = classes + "jvm-like" - "js" -> classes = classes + "js-like" - "wasm" -> classes = classes + "wasm-like" - } - text(it.name) - } - } - } - } - - private fun FlowContent.createPlatformTags( - node: ContentNode, - sourceSetRestriction: Set<DisplaySourceSet>? = null, - cssClasses: String = "" - ) { - node.takeIf { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } }?.let { - createPlatformTagBubbles(node.sourceSets.filter { - sourceSetRestriction == null || it in sourceSetRestriction - }.sortedBy { it.name }, cssClasses) - } - } - - override fun FlowContent.buildTable( - node: ContentTable, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - when { - node.style.contains(CommentTable) -> buildDefaultTable(node, pageContext, sourceSetRestriction) - else -> div(classes = "table") { - node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } - node.children.forEach { - buildRow(it, pageContext, sourceSetRestriction) - } - } - } - - } - - public fun FlowContent.buildDefaultTable( - node: ContentTable, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - table { - thead { - node.header.forEach { - tr { - it.children.forEach { - th { - it.build(this@table, pageContext, sourceSetRestriction) - } - } - } - } - } - tbody { - node.children.forEach { - tr { - it.children.forEach { - td { - it.build(this, pageContext, sourceSetRestriction) - } - } - } - } - } - } - } - - - override fun FlowContent.buildHeader(level: Int, node: ContentHeader, content: FlowContent.() -> Unit) { - val classes = node.style.joinToString { it.toString() }.toLowerCase() - when (level) { - 1 -> h1(classes = classes, content) - 2 -> h2(classes = classes, content) - 3 -> h3(classes = classes, content) - 4 -> h4(classes = classes, content) - 5 -> h5(classes = classes, content) - else -> h6(classes = classes, content) - } - } - - private fun FlowContent.buildAnchor( - anchor: String, - anchorLabel: String, - sourceSets: String, - content: FlowContent.() -> Unit - ) { - a { - attributes["data-name"] = anchor - attributes["anchor-label"] = anchorLabel - attributes["id"] = anchor - attributes["data-filterable-set"] = sourceSets - } - content() - } - - private fun FlowContent.buildAnchor(anchor: String, anchorLabel: String, sourceSets: String) = - buildAnchor(anchor, anchorLabel, sourceSets) {} - - private fun FlowContent.buildAnchor(node: ContentNode) { - node.anchorLabel?.let { label -> buildAnchor(node.anchor!!, label, node.buildSourceSetFilterValues()) } - } - - - override fun FlowContent.buildNavigation(page: PageNode) { - div(classes = "breadcrumbs") { - val path = locationProvider.ancestors(page).filterNot { it is RendererSpecificPage }.asReversed() - if (path.size > 1) { - buildNavigationElement(path.first(), page) - path.drop(1).forEach { node -> - span(classes = "delimiter") { - text("/") - } - buildNavigationElement(node, page) - } - } - } - } - - private fun FlowContent.buildNavigationElement(node: PageNode, page: PageNode) = - if (node.isNavigable) { - val isCurrentPage = (node == page) - if (isCurrentPage) { - span(classes = "current") { - text(node.name) - } - } else { - buildLink(node, page) - } - } else { - text(node.name) - } - - private fun FlowContent.buildLink(to: PageNode, from: PageNode) = - locationProvider.resolve(to, from)?.let { path -> - buildLink(path) { - text(to.name) - } - } ?: span { - attributes["data-unresolved-link"] = to.name.htmlEscape() - text(to.name) - } - - public fun FlowContent.buildAnchorCopyButton(pointingTo: String) { - span(classes = "anchor-wrapper") { - span(classes = "anchor-icon") { - attributes["pointing-to"] = pointingTo - } - copiedPopup("Link copied to clipboard") - } - } - - public fun FlowContent.buildLink( - to: DRI, - platforms: List<DisplaySourceSet>, - from: PageNode? = null, - block: FlowContent.() -> Unit - ) { - locationProvider.resolve(to, platforms.toSet(), from)?.let { buildLink(it, block) } - ?: run { context.logger.error("Cannot resolve path for `$to` from `$from`"); block() } - } - - override fun buildError(node: ContentNode) { - context.logger.error("Unknown ContentNode type: $node") - } - - override fun FlowContent.buildLineBreak() { - br() - } - override fun FlowContent.buildLineBreak(node: ContentBreakLine, pageContext: ContentPage) { - if (node.style.contains(HorizontalBreakLineStyle)) { - hr() - } else { - buildLineBreak() - } - } - - override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) { - a(href = address, block = content) - } - - override fun FlowContent.buildDRILink( - node: ContentDRILink, - pageContext: ContentPage, - sourceSetRestriction: Set<DisplaySourceSet>? - ) { - locationProvider.resolve(node.address, node.sourceSets, pageContext)?.let { address -> - buildLink(address) { - buildText(node.children, pageContext, sourceSetRestriction) - } - } ?: if (isPartial) { - templateCommand(ResolveLinkCommand(node.address)) { - buildText(node.children, pageContext, sourceSetRestriction) - } - } else { - span { - attributes["data-unresolved-link"] = node.address.toString().htmlEscape() - buildText(node.children, pageContext, sourceSetRestriction) - } - } - } - - override fun FlowContent.buildCodeBlock( - code: ContentCodeBlock, - pageContext: ContentPage - ) { - div("sample-container") { - val codeLang = "lang-" + code.language.ifEmpty { "kotlin" } - val stylesWithBlock = code.style + TextStyle.Block + codeLang - pre { - code(stylesWithBlock.joinToString(" ") { it.toString().toLowerCase() }) { - attributes["theme"] = "idea" - code.children.forEach { buildContentNode(it, pageContext) } - } - } - /* - Disable copy button on samples as: - - it is useless - - it overflows with playground's run button - */ - if (!code.style.contains(ContentStyle.RunnableSample)) copyButton() - } - } - - override fun FlowContent.buildCodeInline( - code: ContentCodeInline, - pageContext: ContentPage - ) { - val codeLang = "lang-" + code.language.ifEmpty { "kotlin" } - val stylesWithBlock = code.style + codeLang - code(stylesWithBlock.joinToString(" ") { it.toString().toLowerCase() }) { - code.children.forEach { buildContentNode(it, pageContext) } - } - } - - override fun FlowContent.buildText(textNode: ContentText) { - buildText(textNode, textNode.style) - } - - private fun FlowContent.buildText(textNode: ContentText, unappliedStyles: Set<Style>) { - when { - textNode.extra[HtmlContent] != null -> { - consumer.onTagContentUnsafe { raw(textNode.text) } - } - unappliedStyles.contains(TextStyle.Indented) -> { - consumer.onTagContentEntity(Entities.nbsp) - buildText(textNode, unappliedStyles - TextStyle.Indented) - } - unappliedStyles.isNotEmpty() -> { - val styleToApply = unappliedStyles.first() - applyStyle(styleToApply) { - buildText(textNode, unappliedStyles - styleToApply) - } - } - textNode.hasStyle(ContentStyle.RowTitle) || textNode.hasStyle(TextStyle.Cover) -> - buildBreakableText(textNode.text) - else -> text(textNode.text) - } - } - - private inline fun FlowContent.applyStyle(styleToApply: Style, crossinline body: FlowContent.() -> Unit) { - when (styleToApply) { - TextStyle.Bold -> b { body() } - TextStyle.Italic -> i { body() } - TextStyle.Strikethrough -> strike { body() } - TextStyle.Strong -> strong { body() } - TextStyle.Var -> htmlVar { body() } - TextStyle.Underlined -> underline { body() } - is TokenStyle -> span("token ${styleToApply.prismJsClass()}") { body() } - else -> body() - } - } - - private fun TokenStyle.prismJsClass(): String = when(this) { - // Prism.js parser adds Builtin token instead of Annotation - // for some reason, so we also add it for consistency and correct coloring - TokenStyle.Annotation -> "annotation builtin" - else -> this.toString().toLowerCase() - } - - override fun render(root: RootPageNode) { - shouldRenderSourceSetTabs = shouldRenderSourceSetTabs(root) - super.render(root) - } - - override fun buildPage(page: ContentPage, content: (FlowContent, ContentPage) -> Unit): String = - buildHtml(page, page.embeddedResources) { - content(this, page) - } - - private fun PageNode.getDocumentableType(): String? = - when(this) { - is PackagePage -> "package" - is ClasslikePage -> "classlike" - is MemberPage -> "member" - else -> null - } - - public open fun buildHtml( - page: PageNode, - resources: List<String>, content: FlowContent.() -> Unit - ): String { - return templater.renderFromTemplate(DokkaTemplateTypes.BASE) { - val generatedContent = - createHTML().div("main-content") { - page.getDocumentableType()?.let { attributes["data-page-type"] = it } - id = "content" - (page as? ContentPage)?.let { - attributes["pageIds"] = "${context.configuration.moduleName}::${page.pageId}" - } - content() - } - - templateModelMerger.invoke(templateModelFactories) { - buildModel( - page, - resources, - locationProvider, - generatedContent - ) - } - } - } - - /** - * This is deliberately left open for plugins that have some other pages above ours and would like to link to them - * instead of ours when clicking the logo - */ - public open fun FlowContent.clickableLogo(page: PageNode, pathToRoot: String) { - if (context.configuration.delayTemplateSubstitution && page is ContentPage) { - templateCommand(PathToRootSubstitutionCommand(pattern = "###", default = pathToRoot)) { - a { - href = "###index.html" - templateCommand( - ProjectNameSubstitutionCommand( - pattern = "@@@", - default = context.configuration.moduleName - ) - ) { - span { - text("@@@") - } - } - } - } - } else { - a { - href = pathToRoot + "index.html" - text(context.configuration.moduleName) - } - } - } - - private val ContentNode.isAnchorable: Boolean - get() = anchorLabel != null - - private val ContentNode.anchorLabel: String? - get() = extra[SymbolAnchorHint]?.anchorName - - private val ContentNode.anchor: String? - get() = extra[SymbolAnchorHint]?.contentKind?.let { contentKind -> - (locationProvider as DokkaBaseLocationProvider).anchorForDCI(DCI(dci.dri, contentKind), sourceSets) - } - - private val isPartial = context.configuration.delayTemplateSubstitution -} - -private fun TabbedContentType.toHtmlAttribute(): String = - when(this) { - is BasicTabbedContentType -> - when(this) { - BasicTabbedContentType.ENTRY -> "ENTRY" - BasicTabbedContentType.TYPE -> "TYPE" - BasicTabbedContentType.CONSTRUCTOR -> "CONSTRUCTOR" - BasicTabbedContentType.FUNCTION -> "FUNCTION" - BasicTabbedContentType.PROPERTY -> "PROPERTY" - BasicTabbedContentType.EXTENSION_PROPERTY -> "EXTENSION_PROPERTY" - BasicTabbedContentType.EXTENSION_FUNCTION -> "EXTENSION_FUNCTION" - } - else -> throw IllegalStateException("Unknown TabbedContentType $this") - } - -/** - * Tabs for a content with [ContentStyle.TabbedContent]. - * - * @see ContentStyle.TabbedContent] - */ -private data class ContentTab(val text: String, val tabbedContentTypes: List<TabbedContentType>) - -public fun List<SimpleAttr>.joinAttr(): String = joinToString(" ") { it.extraKey + "=" + it.extraValue } - -private fun String.stripDiv() = drop(5).dropLast(6) // TODO: Find a way to do it without arbitrary trims - -private val PageNode.isNavigable: Boolean - get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing - -private fun PropertyContainer<ContentNode>.extraHtmlAttributes() = allOfType<SimpleAttr>() -private fun PropertyContainer<ContentNode>.extraTabbedContentType() = this[TabbedContentTypeExtra] - -private val DisplaySourceSet.comparableKey - get() = sourceSetIDs.merged.let { it.scopeId + it.sourceSetName } diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt deleted file mode 100644 index fccfd145..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/NavigationDataProvider.kt +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -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.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.dokka.analysis.kotlin.internal.DocumentableLanguage -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -public abstract class NavigationDataProvider( - dokkaContext: DokkaContext -) { - private val documentableSourceLanguageParser = dokkaContext.plugin<InternalKotlinAnalysisPlugin>().querySingle { documentableSourceLanguageParser } - - public open fun navigableChildren(input: RootPageNode): NavigationNode = input.withDescendants() - .first { it is ModulePage || it is MultimoduleRootPage }.let { visit(it as ContentPage) } - - public open fun visit(page: ContentPage): NavigationNode = - NavigationNode( - name = page.displayableName(), - dri = page.dri.first(), - sourceSets = page.sourceSets(), - icon = chooseNavigationIcon(page), - styles = chooseStyles(page), - children = page.navigableChildren() - ) - - /** - * Parenthesis is applied in 1 case: - * - page only contains functions (therefore documentable from this page is [DFunction]) - */ - private fun ContentPage.displayableName(): String = - if (this is WithDocumentables && documentables.all { it is DFunction }) { - "$name()" - } else { - name - } - - private fun chooseNavigationIcon(contentPage: ContentPage): NavigationNodeIcon? = - if (contentPage is WithDocumentables) { - val documentable = contentPage.documentables.firstOrNull() - val isJava = documentable?.hasAnyJavaSources() ?: false - - when (documentable) { - is DTypeAlias -> NavigationNodeIcon.TYPEALIAS_KT - is DClass -> when { - documentable.isException -> NavigationNodeIcon.EXCEPTION - documentable.isAbstract() -> { - if (isJava) NavigationNodeIcon.ABSTRACT_CLASS else NavigationNodeIcon.ABSTRACT_CLASS_KT - } - else -> if (isJava) NavigationNodeIcon.CLASS else NavigationNodeIcon.CLASS_KT - } - is DFunction -> NavigationNodeIcon.FUNCTION - is DProperty -> { - val isVar = documentable.extra[IsVar] != null - if (isVar) NavigationNodeIcon.VAR else NavigationNodeIcon.VAL - } - is DInterface -> if (isJava) NavigationNodeIcon.INTERFACE else NavigationNodeIcon.INTERFACE_KT - is DEnum, - is DEnumEntry -> if (isJava) NavigationNodeIcon.ENUM_CLASS else NavigationNodeIcon.ENUM_CLASS_KT - is DAnnotation -> { - if (isJava) NavigationNodeIcon.ANNOTATION_CLASS else NavigationNodeIcon.ANNOTATION_CLASS_KT - } - is DObject -> NavigationNodeIcon.OBJECT - else -> null - } - } else { - null - } - - private fun Documentable.hasAnyJavaSources(): Boolean { - return this.sourceSets.any { sourceSet -> - documentableSourceLanguageParser.getLanguage(this, sourceSet) == DocumentableLanguage.JAVA - } - } - - private fun DClass.isAbstract() = - modifier.values.all { it is KotlinModifier.Abstract || it is JavaModifier.Abstract } - - private fun chooseStyles(page: ContentPage): Set<Style> = - if (page.containsOnlyDeprecatedDocumentables()) setOf(TextStyle.Strikethrough) else emptySet() - - private fun ContentPage.containsOnlyDeprecatedDocumentables(): Boolean { - if (this !is WithDocumentables) { - return false - } - return this.documentables.isNotEmpty() && this.documentables.all { it.isDeprecatedForAllSourceSets() } - } - - private fun Documentable.isDeprecatedForAllSourceSets(): Boolean { - val sourceSetAnnotations = this.annotations() - return sourceSetAnnotations.isNotEmpty() && sourceSetAnnotations.all { (_, annotations) -> - annotations.any { it.isDeprecated() } - } - } - - private val navigationNodeOrder: Comparator<NavigationNode> = - compareBy(canonicalAlphabeticalOrder) { it.name } - - private fun ContentPage.navigableChildren() = - if (this is ClasslikePage) { - this.navigableChildren() - } else { - children - .filterIsInstance<ContentPage>() - .map { visit(it) } - .sortedWith(navigationNodeOrder) - } - - private fun ClasslikePage.navigableChildren(): List<NavigationNode> { - // Classlikes should only have other classlikes as navigable children - val navigableChildren = children - .filterIsInstance<ClasslikePage>() - .map { visit(it) } - - val isEnumPage = documentables.any { it is DEnum } - return if (isEnumPage) { - // no sorting for enum entries, should be the same order as in source code - navigableChildren - } else { - navigableChildren.sortedWith(navigationNodeOrder) - } - } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt deleted file mode 100644 index eae43daf..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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 -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.WithChildren -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext - -public class NavigationPage( - public val root: NavigationNode, - public val moduleName: String, - public val context: DokkaContext -) : RendererSpecificPage { - - override val name: String = "navigation" - - override val children: List<PageNode> = emptyList() - - override fun modified(name: String, children: List<PageNode>): NavigationPage = this - - override val strategy: RenderingStrategy = RenderingStrategy<HtmlRenderer> { - createHTML().visit(root, this) - } - - private fun <R> TagConsumer<R>.visit(node: NavigationNode, renderer: HtmlRenderer): R = with(renderer) { - if (context.configuration.delayTemplateSubstitution) { - templateCommand(AddToNavigationCommand(moduleName)) { - visit(node, "${moduleName}-nav-submenu", renderer) - } - } else { - visit(node, "${moduleName}-nav-submenu", renderer) - } - } - - private fun <R> TagConsumer<R>.visit(node: NavigationNode, navId: String, renderer: HtmlRenderer): R = - with(renderer) { - div("sideMenuPart") { - id = navId - attributes["pageId"] = "${moduleName}::${node.pageId}" - div("overview") { - if (node.children.isNotEmpty()) { - span("navButton") { - onClick = """document.getElementById("$navId").classList.toggle("hidden");""" - span("navButtonContent") - } - } - buildLink(node.dri, node.sourceSets.toList()) { - val withIcon = node.icon != null - if (withIcon) { - // in case link text is so long that it needs to have word breaks, - // and it stretches to two or more lines, make sure the icon - // is always on the left in the grid and is not wrapped with text - span("nav-link-grid") { - span("nav-link-child ${node.icon?.style()}") - span("nav-link-child") { - nodeText(node) - } - } - } else { - nodeText(node) - } - } - } - node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) } - } - } - - private fun FlowContent.nodeText(node: NavigationNode) { - if (node.styles.contains(TextStyle.Strikethrough)) { - strike { - buildBreakableText(node.name) - } - } else { - buildBreakableText(node.name) - } - } -} - -public data class NavigationNode( - val name: String, - val dri: DRI, - val sourceSets: Set<DisplaySourceSet>, - val icon: NavigationNodeIcon?, - val styles: Set<Style> = emptySet(), - override val children: List<NavigationNode> -) : WithChildren<NavigationNode> - -/** - * [CLASS] represents a neutral (a.k.a Java-style) icon, - * whereas [CLASS_KT] should be Kotlin-styled - */ -public enum class NavigationNodeIcon( - private val cssClass: String -) { - CLASS("class"), - CLASS_KT("class-kt"), - ABSTRACT_CLASS("abstract-class"), - ABSTRACT_CLASS_KT("abstract-class-kt"), - ENUM_CLASS("enum-class"), - ENUM_CLASS_KT("enum-class-kt"), - ANNOTATION_CLASS("annotation-class"), - ANNOTATION_CLASS_KT("annotation-class-kt"), - INTERFACE("interface"), - INTERFACE_KT("interface-kt"), - FUNCTION("function"), - EXCEPTION("exception-class"), - OBJECT("object"), - TYPEALIAS_KT("typealias-kt"), - VAL("val"), - VAR("var"); - - internal fun style(): String = "nav-icon $cssClass" -} - -public fun NavigationPage.transform(block: (NavigationNode) -> NavigationNode): NavigationPage = - NavigationPage(root.transform(block), moduleName, context) - -public fun NavigationNode.transform(block: (NavigationNode) -> NavigationNode): NavigationNode = - run(block).let { NavigationNode(it.name, it.dri, it.sourceSets, it.icon, it.styles, it.children.map(block)) } diff --git a/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt b/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt deleted file mode 100644 index 83d4b24f..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/SearchbarDataInstaller.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.renderers.sourceSets -import org.jetbrains.dokka.base.templating.AddToSearch -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.dfs -import org.jetbrains.dokka.model.withDescendants -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.pages.PageTransformer - -public data class SearchRecord( - val name: String, - val description: String? = null, - val location: String, - val searchKeys: List<String> = listOf(name) -) { - public companion object -} - -public open class SearchbarDataInstaller( - public val context: DokkaContext -) : PageTransformer { - - public data class DRIWithSourceSets(val dri: DRI, val sourceSet: Set<DisplaySourceSet>) - - public data class SignatureWithId(val driWithSourceSets: DRIWithSourceSets, val displayableSignature: String) { - public constructor(dri: DRI, page: ContentPage) : this( DRIWithSourceSets(dri, page.sourceSets()), - getSymbolSignature(page, dri)?.let { flattenToText(it) } ?: page.name) - - val id: String - get() = with(driWithSourceSets.dri) { - listOfNotNull( - packageName?.takeIf { it.isNotBlank() }, - classNames, - callable?.name - ).joinToString(".") - } - } - - private val mapper = jacksonObjectMapper() - - public open fun generatePagesList( - pages: List<SignatureWithId>, - locationResolver: DriResolver - ): List<SearchRecord> = - pages.map { pageWithId -> - createSearchRecord( - name = pageWithId.displayableSignature, - description = pageWithId.id, - location = resolveLocation(locationResolver, pageWithId.driWithSourceSets).orEmpty(), - searchKeys = listOf( - pageWithId.id.substringAfterLast("."), - pageWithId.displayableSignature, - pageWithId.id, - ) - ) - }.sortedWith(compareBy({ it.name }, { it.description })) - - public open fun createSearchRecord( - name: String, - description: String?, - location: String, - searchKeys: List<String> - ): SearchRecord = - SearchRecord(name, description, location, searchKeys) - - public open fun processPage(page: PageNode): List<SignatureWithId> = - when (page) { - is ContentPage -> page.takeIf { page !is ModulePageNode && page !is PackagePageNode }?.dri - ?.map { dri -> SignatureWithId(dri, page) }.orEmpty() - else -> emptyList() - } - - private fun resolveLocation(locationResolver: DriResolver, driWithSourceSets: DRIWithSourceSets): String? = - locationResolver(driWithSourceSets.dri, driWithSourceSets.sourceSet).also { location -> - if (location.isNullOrBlank()) context.logger.warn("Cannot resolve path for ${driWithSourceSets.dri}") - } - - override fun invoke(input: RootPageNode): RootPageNode { - val signatureWithIds = input.withDescendants().fold(emptyList<SignatureWithId>()) { pageList, page -> - pageList + processPage(page) - } - val page = RendererSpecificResourcePage( - name = "scripts/pages.json", - children = emptyList(), - strategy = RenderingStrategy.DriLocationResolvableWrite { resolver -> - val content = signatureWithIds.run { - generatePagesList(this, resolver) - } - - if (context.configuration.delayTemplateSubstitution) { - mapper.writeValueAsString(AddToSearch(context.configuration.moduleName, content)) - } else { - mapper.writeValueAsString(content) - } - }) - - return input.modified(children = input.children + page) - } -} - -private fun getSymbolSignature(page: ContentPage, dri: DRI) = - page.content.dfs { it.dci.kind == ContentKind.Symbol && it.dci.dri.contains(dri) } - -private fun flattenToText(node: ContentNode): String { - fun getContentTextNodes(node: ContentNode, sourceSetRestriction: DisplaySourceSet): List<ContentText> = - when (node) { - is ContentText -> listOf(node) - is ContentComposite -> node.children - .filter { sourceSetRestriction in it.sourceSets } - .flatMap { getContentTextNodes(it, sourceSetRestriction) } - .takeIf { node.dci.kind != ContentKind.Annotations && node.dci.kind != ContentKind.Source } - .orEmpty() - else -> emptyList() - } - - val sourceSetRestriction = - node.sourceSets.find { it.platform == Platform.common } ?: node.sourceSets.first() - return getContentTextNodes(node, sourceSetRestriction).joinToString("") { it.text } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/Tags.kt b/plugins/base/src/main/kotlin/renderers/html/Tags.kt deleted file mode 100644 index 7d6fc390..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/Tags.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import kotlinx.html.* -import kotlinx.html.stream.createHTML -import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer -import org.jetbrains.dokka.base.templating.Command -import org.jetbrains.dokka.base.templating.toJsonString - -public typealias TemplateBlock = TemplateCommand.() -> Unit - -@HtmlTagMarker -public fun FlowOrPhrasingContent.wbr(classes: String? = null, block: WBR.() -> Unit = {}): Unit = - WBR(attributesMapOf("class", classes), consumer).visit(block) - -@Suppress("unused") -public open class WBR(initialAttributes: Map<String, String>, consumer: TagConsumer<*>) : - HTMLTag("wbr", consumer, initialAttributes, namespace = null, inlineTag = true, emptyTag = false), - HtmlBlockInlineTag - -/** - * Work-around until next version of kotlinx.html doesn't come out - */ -@HtmlTagMarker -public inline fun FlowOrPhrasingContent.strike(classes : String? = null, crossinline block : STRIKE.() -> Unit = {}) : Unit = STRIKE(attributesMapOf("class", classes), consumer).visit(block) - -public open class STRIKE(initialAttributes: Map<String, String>, override val consumer: TagConsumer<*>) : - HTMLTag("strike", consumer, initialAttributes, null, false, false), HtmlBlockInlineTag - -@HtmlTagMarker -public inline fun FlowOrPhrasingContent.underline(classes : String? = null, crossinline block : UNDERLINE.() -> Unit = {}) : Unit = UNDERLINE(attributesMapOf("class", classes), consumer).visit(block) - -public open class UNDERLINE(initialAttributes: Map<String, String>, override val consumer: TagConsumer<*>) : - HTMLTag("u", consumer, initialAttributes, null, false, false), HtmlBlockInlineTag - -public const val TEMPLATE_COMMAND_SEPARATOR: String = ":" -public const val TEMPLATE_COMMAND_BEGIN_BORDER: String = "[+]cmd" -public const val TEMPLATE_COMMAND_END_BORDER: String = "[-]cmd" - -public fun FlowOrMetaDataContent.templateCommandAsHtmlComment(data: Command, block: FlowOrMetaDataContent.() -> Unit = {}): Unit = - (consumer as? ImmediateResolutionTagConsumer)?.processCommand(data, block) - ?: let{ - comment( "$TEMPLATE_COMMAND_BEGIN_BORDER$TEMPLATE_COMMAND_SEPARATOR${toJsonString(data)}") - block() - comment(TEMPLATE_COMMAND_END_BORDER) - } - -public fun <T: Appendable> T.templateCommandAsHtmlComment(command: Command, action: T.() -> Unit ) { - append("<!--$TEMPLATE_COMMAND_BEGIN_BORDER$TEMPLATE_COMMAND_SEPARATOR${toJsonString(command)}-->") - action() - append("<!--$TEMPLATE_COMMAND_END_BORDER-->") -} - -public fun FlowOrMetaDataContent.templateCommand(data: Command, block: TemplateBlock = {}): Unit = - (consumer as? ImmediateResolutionTagConsumer)?.processCommand(data, block) - ?: TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer).visit(block) - -public fun <T> TagConsumer<T>.templateCommand(data: Command, block: TemplateBlock = {}): T = - (this as? ImmediateResolutionTagConsumer)?.processCommandAndFinalize(data, block) - ?: TemplateCommand(attributesMapOf("data", toJsonString(data)), this).visitAndFinalize(this, block) - -public fun templateCommandFor(data: Command, consumer: TagConsumer<*>): TemplateCommand = - TemplateCommand(attributesMapOf("data", toJsonString(data)), consumer) - -public class TemplateCommand(initialAttributes: Map<String, String>, consumer: TagConsumer<*>) : - HTMLTag( - "dokka-template-command", - consumer, - initialAttributes, - namespace = null, - inlineTag = true, - emptyTag = false - ), - CommonAttributeGroupFacadeFlowInteractivePhrasingContent - -// This hack is outrageous. I hate it but I cannot find any other way around `kotlinx.html` type system. -public fun TemplateBlock.buildAsInnerHtml(): String = createHTML(prettyPrint = false).run { - TemplateCommand(emptyMap, this).visitAndFinalize(this, this@buildAsInnerHtml).substringAfter(">").substringBeforeLast("<") -} diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt deleted file mode 100644 index 9cde1fca..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ImmediateResolutionTagConsumer.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.command.consumers - -import kotlinx.html.TagConsumer -import kotlinx.html.visit -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.renderers.html.TemplateBlock -import org.jetbrains.dokka.base.renderers.html.templateCommand -import org.jetbrains.dokka.base.renderers.html.templateCommandFor -import org.jetbrains.dokka.base.templating.Command -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.query - -public class ImmediateResolutionTagConsumer<out R>( - private val downstream: TagConsumer<R>, - private val context: DokkaContext -): TagConsumer<R> by downstream { - - public fun processCommand(command: Command, block: TemplateBlock) { - context.plugin<DokkaBase>().query { immediateHtmlCommandConsumer } - .find { it.canProcess(command) } - ?.processCommand(command, block, this) - ?: run { templateCommandFor(command, downstream).visit(block) } - } - - public fun processCommandAndFinalize(command: Command, block: TemplateBlock): R { - return context.plugin<DokkaBase>().query { immediateHtmlCommandConsumer } - .find { it.canProcess(command) } - ?.processCommandAndFinalize(command, block, this) - ?: downstream.templateCommand(command, block) - } -} - diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt deleted file mode 100644 index 9ac6eb91..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/command/consumers/PathToRootConsumer.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.command.consumers - -import org.jetbrains.dokka.base.renderers.html.TemplateBlock -import org.jetbrains.dokka.base.renderers.html.buildAsInnerHtml -import org.jetbrains.dokka.base.templating.Command -import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer -import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand - -public object PathToRootConsumer: ImmediateHtmlCommandConsumer { - override fun canProcess(command: Command): Boolean = command is PathToRootSubstitutionCommand - - override fun <R> processCommand(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>) { - command as PathToRootSubstitutionCommand - tagConsumer.onTagContentUnsafe { +block.buildAsInnerHtml().replace(command.pattern, command.default) } - } - - override fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R { - processCommand(command, block, tagConsumer) - return tagConsumer.finalize() - } - -} diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ReplaceVersionsConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ReplaceVersionsConsumer.kt deleted file mode 100644 index dd95c202..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ReplaceVersionsConsumer.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.command.consumers - -import org.jetbrains.dokka.base.renderers.html.TemplateBlock -import org.jetbrains.dokka.base.templating.Command -import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer -import org.jetbrains.dokka.base.templating.ReplaceVersionsCommand -import org.jetbrains.dokka.plugability.DokkaContext - -public class ReplaceVersionsConsumer(private val context: DokkaContext) : ImmediateHtmlCommandConsumer { - override fun canProcess(command: Command): Boolean = command is ReplaceVersionsCommand - - override fun <R> processCommand( - command: Command, - block: TemplateBlock, - tagConsumer: ImmediateResolutionTagConsumer<R> - ) { - command as ReplaceVersionsCommand - tagConsumer.onTagContentUnsafe { +context.configuration.moduleVersion.orEmpty() } - } - - override fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R { - processCommand(command, block, tagConsumer) - return tagConsumer.finalize() - } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt b/plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt deleted file mode 100644 index 292e88b0..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/command/consumers/ResolveLinkConsumer.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.command.consumers - -import kotlinx.html.SPAN -import kotlinx.html.span -import kotlinx.html.unsafe -import kotlinx.html.visit -import org.jetbrains.dokka.base.renderers.html.TemplateBlock -import org.jetbrains.dokka.base.renderers.html.buildAsInnerHtml -import org.jetbrains.dokka.base.templating.Command -import org.jetbrains.dokka.base.templating.ImmediateHtmlCommandConsumer -import org.jetbrains.dokka.base.templating.ResolveLinkCommand -import org.jetbrains.dokka.utilities.htmlEscape - -public object ResolveLinkConsumer: ImmediateHtmlCommandConsumer { - override fun canProcess(command: Command): Boolean = command is ResolveLinkCommand - - override fun <R> processCommand(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>) { - command as ResolveLinkCommand - SPAN(mapOf("data-unresolved-link" to command.dri.toString().htmlEscape()), tagConsumer).visit { - unsafe { block.buildAsInnerHtml() } - } - } - - override fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R { - command as ResolveLinkCommand - return tagConsumer.span { - attributes["data-unresolved-link"] = command.dri.toString().htmlEscape() - } - } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlFormatingUtils.kt b/plugins/base/src/main/kotlin/renderers/html/htmlFormatingUtils.kt deleted file mode 100644 index b6ce4147..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/htmlFormatingUtils.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import kotlinx.html.FlowContent -import kotlinx.html.span - -public fun FlowContent.buildTextBreakableAfterCapitalLetters(name: String, hasLastElement: Boolean = false) { - if (name.contains(" ")) { - val withOutSpaces = name.split(" ") - withOutSpaces.dropLast(1).forEach { - buildBreakableText(it) - +" " - } - buildBreakableText(withOutSpaces.last()) - } else { - val content = name.replace(Regex("(?<=[a-z])([A-Z])"), " $1").split(" ") - joinToHtml(content, hasLastElement) { - it - } - } -} - -public fun FlowContent.buildBreakableDotSeparatedHtml(name: String) { - val phrases = name.split(".") - phrases.forEachIndexed { i, e -> - val elementWithOptionalDot = e.takeIf { i == phrases.lastIndex } ?: "$e." - if (e.length > 10) { - buildTextBreakableAfterCapitalLetters(elementWithOptionalDot, hasLastElement = i == phrases.lastIndex) - } else { - buildBreakableHtmlElement(elementWithOptionalDot, i == phrases.lastIndex) - } - } -} - -private fun FlowContent.joinToHtml(elements: List<String>, hasLastElement: Boolean = true, onEach: (String) -> String) { - elements.dropLast(1).forEach { - buildBreakableHtmlElement(onEach(it)) - } - elements.takeIf { it.isNotEmpty() && it.last().isNotEmpty() }?.let { - if (hasLastElement) { - span { - buildBreakableHtmlElement(it.last(), last = true) - } - } else { - buildBreakableHtmlElement(it.last(), last = false) - } - } -} - -private fun FlowContent.buildBreakableHtmlElement(element: String, last: Boolean = false) { - element.takeIf { it.isNotBlank() }?.let { - span { - +it - } - } - if (!last) { - wbr { } - } -} - -public fun FlowContent.buildBreakableText(name: String) { - if (name.contains(".")) buildBreakableDotSeparatedHtml(name) - else buildTextBreakableAfterCapitalLetters(name, hasLastElement = true) -} diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt deleted file mode 100644 index dad013e2..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.jetbrains.dokka.base.templating.AddToSourcesetDependencies -import org.jetbrains.dokka.base.templating.toJsonString -import org.jetbrains.dokka.pages.RendererSpecificResourcePage -import org.jetbrains.dokka.pages.RenderingStrategy -import org.jetbrains.dokka.pages.RootPageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.configuration -import org.jetbrains.dokka.transformers.pages.PageTransformer - -public open class NavigationPageInstaller( - public val context: DokkaContext -) : NavigationDataProvider(context), PageTransformer { - override fun invoke(input: RootPageNode): RootPageNode = - input.modified( - children = input.children + NavigationPage( - root = navigableChildren(input), - moduleName = context.configuration.moduleName, - context = context - ) - ) -} - -public class CustomResourceInstaller( - public val dokkaContext: DokkaContext -) : PageTransformer { - private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(dokkaContext) - - private val customAssets = configuration?.customAssets?.map { - RendererSpecificResourcePage("images/${it.name}", emptyList(), RenderingStrategy.Copy(it.absolutePath)) - }.orEmpty() - - private val customStylesheets = configuration?.customStyleSheets?.map { - RendererSpecificResourcePage("styles/${it.name}", emptyList(), RenderingStrategy.Copy(it.absolutePath)) - }.orEmpty() - - override fun invoke(input: RootPageNode): RootPageNode { - val customResourcesPaths = (customAssets + customStylesheets).map { it.name }.toSet() - val withEmbeddedResources = - input.transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + customResourcesPaths) } - if(dokkaContext.configuration.delayTemplateSubstitution) - return withEmbeddedResources - val (currentResources, otherPages) = withEmbeddedResources.children.partition { it is RendererSpecificResourcePage } - return input.modified(children = otherPages + currentResources.filterNot { it.name in customResourcesPaths } + customAssets + customStylesheets) - } -} - -public class ScriptsInstaller(private val dokkaContext: DokkaContext) : PageTransformer { - - // scripts ending with `_deferred.js` are loaded with `defer`, otherwise `async` - private val scriptsPages = listOf( - "scripts/clipboard.js", - "scripts/navigation-loader.js", - "scripts/platform-content-handler.js", - "scripts/main.js", - "scripts/prism.js", - - // It's important for this script to be deferred because it has logic that makes decisions based on - // rendered elements (for instance taking their clientWidth), and if not all styles are loaded/applied - // at the time of inspecting them, it will give incorrect results and might lead to visual bugs. - // should be easy to test if you open any page in incognito or by reloading it (Ctrl+Shift+R) - "scripts/symbol-parameters-wrapper_deferred.js", - ) - - override fun invoke(input: RootPageNode): RootPageNode = - input.let { root -> - if (dokkaContext.configuration.delayTemplateSubstitution) root - else root.modified(children = input.children + scriptsPages.toRenderSpecificResourcePage()) - }.transformContentPagesTree { - it.modified( - embeddedResources = it.embeddedResources + scriptsPages - ) - } -} - -public class StylesInstaller(private val dokkaContext: DokkaContext) : PageTransformer { - private val stylesPages = listOf( - "styles/style.css", - "styles/main.css", - "styles/prism.css", - "styles/logo-styles.css", - "styles/font-jb-sans-auto.css" - ) - - override fun invoke(input: RootPageNode): RootPageNode = - input.let { root -> - if (dokkaContext.configuration.delayTemplateSubstitution) root - else root.modified(children = input.children + stylesPages.toRenderSpecificResourcePage()) - }.transformContentPagesTree { - it.modified( - embeddedResources = it.embeddedResources + stylesPages - ) - } -} - -public object AssetsInstaller : PageTransformer { - private val imagesPages = listOf( - "images/arrow_down.svg", - "images/logo-icon.svg", - "images/go-to-top-icon.svg", - "images/footer-go-to-link.svg", - "images/anchor-copy-button.svg", - "images/copy-icon.svg", - "images/copy-successful-icon.svg", - "images/theme-toggle.svg", - "images/burger.svg", - "images/homepage.svg", - - // navigation icons - "images/nav-icons/abstract-class.svg", - "images/nav-icons/abstract-class-kotlin.svg", - "images/nav-icons/annotation.svg", - "images/nav-icons/annotation-kotlin.svg", - "images/nav-icons/class.svg", - "images/nav-icons/class-kotlin.svg", - "images/nav-icons/enum.svg", - "images/nav-icons/enum-kotlin.svg", - "images/nav-icons/exception-class.svg", - "images/nav-icons/field-value.svg", - "images/nav-icons/field-variable.svg", - "images/nav-icons/function.svg", - "images/nav-icons/interface.svg", - "images/nav-icons/interface-kotlin.svg", - "images/nav-icons/object.svg", - "images/nav-icons/typealias-kotlin.svg", - ) - - override fun invoke(input: RootPageNode): RootPageNode = input.modified( - children = input.children + imagesPages.toRenderSpecificResourcePage() - ) -} - -private fun List<String>.toRenderSpecificResourcePage(): List<RendererSpecificResourcePage> = - map { RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it")) } - -public class SourcesetDependencyAppender( - public val context: DokkaContext -) : PageTransformer { - private val name = "scripts/sourceset_dependencies.js" - override fun invoke(input: RootPageNode): RootPageNode { - val dependenciesMap = context.configuration.sourceSets.associate { - it.sourceSetID to it.dependentSourceSets - } - - fun createDependenciesJson(): String = - dependenciesMap.map { (key, values) -> key.toString() to values.map { it.toString() } }.toMap() - .let { content -> - if (context.configuration.delayTemplateSubstitution) { - toJsonString(AddToSourcesetDependencies(context.configuration.moduleName, content)) - } else { - "sourceset_dependencies='${toJsonString(content)}'" - } - } - - val deps = RendererSpecificResourcePage( - name = name, - children = emptyList(), - strategy = RenderingStrategy.Write(createDependenciesJson()) - ) - - return input.modified( - children = input.children + deps - ).transformContentPagesTree { it.modified(embeddedResources = it.embeddedResources + name) } - } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt deleted file mode 100644 index fe6f0089..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelFactory.kt +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.innerTemplating - -import freemarker.core.Environment -import freemarker.template.* -import kotlinx.html.* -import kotlinx.html.stream.createHTML -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.jetbrains.dokka.base.renderers.URIExtension -import org.jetbrains.dokka.base.renderers.html.TEMPLATE_REPLACEMENT -import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer -import org.jetbrains.dokka.base.renderers.html.templateCommand -import org.jetbrains.dokka.base.renderers.html.templateCommandAsHtmlComment -import org.jetbrains.dokka.base.renderers.isImage -import org.jetbrains.dokka.base.resolvers.local.LocationProvider -import org.jetbrains.dokka.base.templating.PathToRootSubstitutionCommand -import org.jetbrains.dokka.base.templating.ProjectNameSubstitutionCommand -import org.jetbrains.dokka.base.templating.ReplaceVersionsCommand -import org.jetbrains.dokka.base.templating.SubstitutionCommand -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.withDescendants -import org.jetbrains.dokka.pages.ContentPage -import org.jetbrains.dokka.pages.PageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.configuration -import java.net.URI - -public class DefaultTemplateModelFactory( - public val context: DokkaContext -) : TemplateModelFactory { - private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(context) - private val isPartial = context.configuration.delayTemplateSubstitution - - private fun <R> TagConsumer<R>.prepareForTemplates() = - if (context.configuration.delayTemplateSubstitution || this is ImmediateResolutionTagConsumer) this - else ImmediateResolutionTagConsumer(this, context) - - public data class SourceSetModel(val name: String, val platform: String, val filter: String) - - override fun buildModel( - page: PageNode, - resources: List<String>, - locationProvider: LocationProvider, - content: String - ): TemplateMap { - val path = locationProvider.resolve(page) - val pathToRoot = locationProvider.pathToRoot(page) - val mapper = mutableMapOf<String, Any>() - mapper["pageName"] = page.name - mapper["resources"] = PrintDirective { - val sb = StringBuilder() - if (isPartial) - sb.templateCommandAsHtmlComment( - PathToRootSubstitutionCommand( - TEMPLATE_REPLACEMENT, - default = pathToRoot - ) - ) { resourcesForPage(TEMPLATE_REPLACEMENT, resources) } - else - sb.resourcesForPage(pathToRoot, resources) - sb.toString() - } - mapper["content"] = PrintDirective { content } - mapper["version"] = PrintDirective { - createHTML().prepareForTemplates().templateCommand(ReplaceVersionsCommand(path.orEmpty())) - } - mapper["template_cmd"] = TemplateDirective(context.configuration, pathToRoot) - - if (page is ContentPage) { - val sourceSets = page.content.withDescendants() - .flatMap { it.sourceSets } - .distinct() - .sortedBy { it.comparableKey } - .map { SourceSetModel(it.name, it.platform.key, it.sourceSetIDs.merged.toString()) } - .toList() - - if (sourceSets.isNotEmpty()) { - mapper["sourceSets"] = sourceSets - } - } - return mapper - } - - override fun buildSharedModel(): TemplateMap { - val mapper = mutableMapOf<String, Any>() - - mapper["footerMessage"] = - (configuration?.footerMessage?.takeIf(String::isNotBlank) ?: DokkaBaseConfiguration.defaultFooterMessage) - - configuration?.homepageLink?.takeIf(String::isNotBlank)?.let { mapper["homepageLink"] = it } - - return mapper - } - - private val DisplaySourceSet.comparableKey - get() = sourceSetIDs.merged.let { it.scopeId + it.sourceSetName } - private val String.isAbsolute: Boolean - get() = URI(this).isAbsolute - - private fun Appendable.resourcesForPage(pathToRoot: String, resources: List<String>): Unit = - resources.forEach { resource -> - - val resourceHtml = with(createHTML()) { - when { - - resource.URIExtension == "css" -> - link( - rel = LinkRel.stylesheet, - href = if (resource.isAbsolute) resource else "$pathToRoot$resource" - ) - - resource.URIExtension == "js" -> - script( - type = ScriptType.textJavaScript, - src = if (resource.isAbsolute) resource else "$pathToRoot$resource" - ) { - if (resource == "scripts/main.js" || resource.endsWith("_deferred.js")) - defer = true - else - async = true - } - - resource.isImage() -> link(href = if (resource.isAbsolute) resource else "$pathToRoot$resource") - else -> null - } - } - if (resourceHtml != null) { - append(resourceHtml) - } - } - -} - -private class PrintDirective(val generateData: () -> String) : TemplateDirectiveModel { - override fun execute( - env: Environment, - params: MutableMap<Any?, Any?>?, - loopVars: Array<TemplateModel>?, - body: TemplateDirectiveBody? - ) { - if (params?.isNotEmpty() == true) throw TemplateModelException( - "Parameters are not allowed" - ) - if (loopVars?.isNotEmpty() == true) throw TemplateModelException( - "Loop variables are not allowed" - ) - env.out.write(generateData()) - } -} - -private class TemplateDirective( - val configuration: DokkaConfiguration, - val pathToRoot: String -) : TemplateDirectiveModel { - override fun execute( - env: Environment, - params: MutableMap<Any?, Any?>?, - loopVars: Array<TemplateModel>?, - body: TemplateDirectiveBody? - ) { - val commandName = params?.get(PARAM_NAME) ?: throw TemplateModelException( - "The required $PARAM_NAME parameter is missing." - ) - val replacement = (params[PARAM_REPLACEMENT] as? SimpleScalar)?.asString ?: TEMPLATE_REPLACEMENT - - when ((commandName as? SimpleScalar)?.asString) { - "pathToRoot" -> { - body ?: throw TemplateModelException( - "No directive body for $commandName command." - ) - executeSubstituteCommand( - PathToRootSubstitutionCommand( - replacement, pathToRoot - ), - "pathToRoot", - pathToRoot, - Context(env, body) - ) - } - - "projectName" -> { - body ?: throw TemplateModelException( - "No directive body $commandName command." - ) - executeSubstituteCommand( - ProjectNameSubstitutionCommand( - replacement, configuration.moduleName - ), - "projectName", - configuration.moduleName, - Context(env, body) - ) - } - - else -> throw TemplateModelException( - "The parameter $PARAM_NAME $commandName is unknown" - ) - } - } - - private data class Context(val env: Environment, val body: TemplateDirectiveBody) - - private fun executeSubstituteCommand( - command: SubstitutionCommand, - name: String, - value: String, - ctx: Context - ) { - if (configuration.delayTemplateSubstitution) - ctx.env.out.templateCommandAsHtmlComment(command) { - renderWithLocalVar(name, command.pattern, ctx) - } - else { - renderWithLocalVar(name, value, ctx) - } - } - - private fun renderWithLocalVar(name: String, value: String, ctx: Context) = - with(ctx) { - env.setVariable(name, SimpleScalar(value)) - body.render(env.out) - env.setVariable(name, null) - } - - companion object { - const val PARAM_NAME = "name" - const val PARAM_REPLACEMENT = "replacement" - } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelMerger.kt b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelMerger.kt deleted file mode 100644 index 2f17183d..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/DefaultTemplateModelMerger.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.innerTemplating - -public class DefaultTemplateModelMerger : TemplateModelMerger { - override fun invoke( - factories: List<TemplateModelFactory>, - buildModel: TemplateModelFactory.() -> TemplateMap - ): TemplateMap { - val mapper = mutableMapOf<String, Any?>() - factories.map(buildModel).forEach { partialModel -> - partialModel.forEach { (k, v) -> - mapper[k] = v - } - } - return mapper - } -} diff --git a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/HtmlTemplater.kt b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/HtmlTemplater.kt deleted file mode 100644 index 1638c9c0..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/HtmlTemplater.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.innerTemplating - -import freemarker.cache.ClassTemplateLoader -import freemarker.cache.FileTemplateLoader -import freemarker.cache.MultiTemplateLoader -import freemarker.log.Logger -import freemarker.template.Configuration -import freemarker.template.TemplateExceptionHandler -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.configuration -import java.io.StringWriter - - -public enum class DokkaTemplateTypes( - public val path: String -) { - BASE("base.ftl") -} - -public typealias TemplateMap = Map<String, Any?> - -public class HtmlTemplater( - context: DokkaContext -) { - - init { - // to disable logging, but it isn't reliable see [Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY] - // (use SLF4j further) - System.setProperty( - Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY, - System.getProperty(Logger.SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY) ?: Logger.LIBRARY_NAME_NONE - ) - } - - private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(context) - private val templaterConfiguration = - Configuration(Configuration.VERSION_2_3_31).apply { configureTemplateEngine() } - - private fun Configuration.configureTemplateEngine() { - val loaderFromResources = ClassTemplateLoader(javaClass, "/dokka/templates") - templateLoader = configuration?.templatesDir?.let { - MultiTemplateLoader( - arrayOf( - FileTemplateLoader(it), - loaderFromResources - ) - ) - } ?: loaderFromResources - - unsetLocale() - defaultEncoding = "UTF-8" - templateExceptionHandler = TemplateExceptionHandler.RETHROW_HANDLER - logTemplateExceptions = false - wrapUncheckedExceptions = true - fallbackOnNullLoopVariable = false - templateUpdateDelayMilliseconds = Long.MAX_VALUE - } - - public fun setupSharedModel(model: TemplateMap) { - templaterConfiguration.setSharedVariables(model) - } - - public fun renderFromTemplate( - templateType: DokkaTemplateTypes, - generateModel: () -> TemplateMap - ): String { - val out = StringWriter() - // Freemarker has own thread-safe cache to keep templates - val template = templaterConfiguration.getTemplate(templateType.path) - val model = generateModel() - template.process(model, out) - - return out.toString() - } -} - diff --git a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/TemplateModelFactory.kt b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/TemplateModelFactory.kt deleted file mode 100644 index 3af11bf9..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/TemplateModelFactory.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.innerTemplating - -import org.jetbrains.dokka.base.resolvers.local.LocationProvider -import org.jetbrains.dokka.pages.PageNode - -public interface TemplateModelFactory { - public fun buildModel( - page: PageNode, - resources: List<String>, - locationProvider: LocationProvider, - content: String - ): TemplateMap - - public fun buildSharedModel(): TemplateMap -} diff --git a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/TemplateModelMerger.kt b/plugins/base/src/main/kotlin/renderers/html/innerTemplating/TemplateModelMerger.kt deleted file mode 100644 index ada0c6cd..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/innerTemplating/TemplateModelMerger.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html.innerTemplating - -public fun interface TemplateModelMerger { - public fun invoke(factories: List<TemplateModelFactory>, buildModel: TemplateModelFactory.() -> TemplateMap): TemplateMap -} diff --git a/plugins/base/src/main/kotlin/renderers/html/shouldRenderSourceSetBubbles.kt b/plugins/base/src/main/kotlin/renderers/html/shouldRenderSourceSetBubbles.kt deleted file mode 100644 index a7bafadb..00000000 --- a/plugins/base/src/main/kotlin/renderers/html/shouldRenderSourceSetBubbles.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers.html - -import org.jetbrains.dokka.model.withDescendants -import org.jetbrains.dokka.pages.ContentPage -import org.jetbrains.dokka.pages.RootPageNode - -internal fun shouldRenderSourceSetTabs(page: RootPageNode): Boolean { - return page.withDescendants() - .flatMap { pageNode -> - if (pageNode is ContentPage) pageNode.content.withDescendants() - else emptySequence() - } - .flatMap { contentNode -> contentNode.sourceSets } - .distinct() - .count() > 1 -} diff --git a/plugins/base/src/main/kotlin/renderers/pageId.kt b/plugins/base/src/main/kotlin/renderers/pageId.kt deleted file mode 100644 index f5d75cfc..00000000 --- a/plugins/base/src/main/kotlin/renderers/pageId.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -import org.jetbrains.dokka.base.renderers.html.NavigationNode -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.pages.ContentPage - -internal val ContentPage.pageId: String - get() = pageId(dri.first(), sourceSets()) - -internal val NavigationNode.pageId: String - get() = pageId(dri, sourceSets) - -@JvmName("shortenSourceSetsToUrl") -internal fun Set<DisplaySourceSet>.shortenToUrl() = - sortedBy { it.sourceSetIDs.merged.let { it.scopeId + it.sourceSetName } }.joinToString().hashCode() - -internal fun DRI.shortenToUrl() = toString() - -@JvmName("shortenDrisToUrl") -internal fun Set<DRI>.shortenToUrl() = sortedBy { it.toString() }.joinToString().hashCode() - -/** - * Page Id is required to have a sourceSet in order to distinguish between different pages that has same DRI but different sourceSet - * like main functions that are not expect/actual - */ -private fun pageId(dri: DRI, sourceSets: Set<DisplaySourceSet>): String = "${dri.shortenToUrl()}/${sourceSets.shortenToUrl()}" diff --git a/plugins/base/src/main/kotlin/renderers/preprocessors.kt b/plugins/base/src/main/kotlin/renderers/preprocessors.kt deleted file mode 100644 index a3a32651..00000000 --- a/plugins/base/src/main/kotlin/renderers/preprocessors.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.renderers - -import org.jetbrains.dokka.base.resolvers.shared.LinkFormat -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.pages.PageTransformer - -public object RootCreator : PageTransformer { - override fun invoke(input: RootPageNode): RootPageNode = - RendererSpecificRootPage("", listOf(input), RenderingStrategy.DoNothing) -} - -public class PackageListCreator( - public val context: DokkaContext, - public val format: LinkFormat, - public val outputFilesNames: List<String> = listOf("package-list") -) : PageTransformer { - override fun invoke(input: RootPageNode): RootPageNode { - return input.transformPageNodeTree { pageNode -> - pageNode.takeIf { it is ModulePage }?.let { it.modified(children = it.children + packageList(input, it as ModulePage)) } ?: pageNode - } - } - - private fun packageList(rootPageNode: RootPageNode, module: ModulePage): List<RendererSpecificPage> { - val content = PackageListService(context, rootPageNode).createPackageList( - module, - format - ) - return outputFilesNames.map { fileName -> - RendererSpecificResourcePage( - fileName, - emptyList(), - RenderingStrategy.Write(content) - ) - } - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/anchors/AnchorsHint.kt b/plugins/base/src/main/kotlin/resolvers/anchors/AnchorsHint.kt deleted file mode 100644 index c9218947..00000000 --- a/plugins/base/src/main/kotlin/resolvers/anchors/AnchorsHint.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.anchors - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.properties.ExtraProperty -import org.jetbrains.dokka.pages.ContentNode -import org.jetbrains.dokka.pages.Kind - -public data class SymbolAnchorHint(val anchorName: String, val contentKind: Kind) : ExtraProperty<ContentNode> { - override val key: ExtraProperty.Key<ContentNode, SymbolAnchorHint> = SymbolAnchorHint - - public companion object : ExtraProperty.Key<ContentNode, SymbolAnchorHint> { - public fun from(d: Documentable, contentKind: Kind): SymbolAnchorHint? = - d.name?.let { SymbolAnchorHint(it, contentKind) } - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt deleted file mode 100644 index 32825303..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProvider.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external - -import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider.Companion.identifierToFilename -import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.plugability.DokkaContext - -public open class DefaultExternalLocationProvider( - public val externalDocumentation: ExternalDocumentation, - public val extension: String, - public val dokkaContext: DokkaContext -) : ExternalLocationProvider { - public val docURL: String = externalDocumentation.documentationURL.toString().removeSuffix("/") + "/" - - override fun resolve(dri: DRI): String? { - externalDocumentation.packageList.locations[dri.toString()]?.let { path -> return "$docURL$path" } - - if (dri.packageName !in externalDocumentation.packageList.packages) - return null - - return dri.constructPath() - } - - protected open fun DRI.constructPath(): String { - val modulePart = packageName?.let { packageName -> - externalDocumentation.packageList.moduleFor(packageName)?.let { - if (it.isNotBlank()) - "$it/" - else - "" - } - }.orEmpty() - - val docWithModule = docURL + modulePart - val classNamesChecked = classNames ?: return "$docWithModule${packageName ?: ""}/index$extension" - val classLink = (listOfNotNull(packageName) + classNamesChecked.split('.')) - .joinToString("/", transform = ::identifierToFilename) - - val fileName = callable?.let { identifierToFilename(it.name) } ?: "index" - return "$docWithModule$classLink/$fileName$extension" - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProviderFactory.kt deleted file mode 100644 index 09ddca01..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/DefaultExternalLocationProviderFactory.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external - -import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat -import org.jetbrains.dokka.plugability.DokkaContext - -public class DefaultExternalLocationProviderFactory( - public val context: DokkaContext, -) : ExternalLocationProviderFactory by ExternalLocationProviderFactoryWithCache( - { doc -> - when (doc.packageList.linkFormat) { - RecognizedLinkFormat.KotlinWebsite, - RecognizedLinkFormat.KotlinWebsiteHtml, - RecognizedLinkFormat.DokkaOldHtml, - -> Dokka010ExternalLocationProvider(doc, ".html", context) - - RecognizedLinkFormat.DokkaHtml -> DefaultExternalLocationProvider(doc, ".html", context) - RecognizedLinkFormat.DokkaGFM, - RecognizedLinkFormat.DokkaJekyll, - -> DefaultExternalLocationProvider(doc, ".md", context) - - else -> null - } - } -) diff --git a/plugins/base/src/main/kotlin/resolvers/external/Dokka010ExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/Dokka010ExternalLocationProvider.kt deleted file mode 100644 index f887c9bc..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/Dokka010ExternalLocationProvider.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external - -import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider.Companion.identifierToFilename -import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.plugability.DokkaContext - -public open class Dokka010ExternalLocationProvider( - public val externalDocumentation: ExternalDocumentation, - public val extension: String, - public val dokkaContext: DokkaContext -) : ExternalLocationProvider { - public val docURL: String = externalDocumentation.documentationURL.toString().removeSuffix("/") + "/" - - override fun resolve(dri: DRI): String? { - - val fqName = listOfNotNull( - dri.packageName.takeIf { it?.isNotBlank() == true }, - dri.classNames.takeIf { it?.isNotBlank() == true }?.removeCompanion() - ).joinToString(".") - val relocationId = - fqName.let { if (dri.callable != null) it + "$" + dri.callable!!.toOldString() else it } - externalDocumentation.packageList.locations[relocationId]?.let { path -> return "$docURL$path" } - - if (dri.packageName !in externalDocumentation.packageList.packages) - return null - - val classNamesChecked = dri.classNames?.removeCompanion() - ?: return "$docURL${dri.packageName ?: ""}/index$extension" - - val classLink = (listOfNotNull(dri.packageName) + classNamesChecked.split('.')) - .joinToString("/", transform = ::identifierToFilename) - - val callableChecked = dri.callable ?: return "$docURL$classLink/index$extension" - return "$docURL$classLink/" + identifierToFilename(callableChecked.name) + extension - } - - private fun String.removeCompanion() = removeSuffix(".Companion") - - private fun Callable.toOldString() = name + params.joinToString(", ", "(", ")") + (receiver?.let { "#$it" } ?: "") -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProvider.kt deleted file mode 100644 index 238b6342..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProvider.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external - -import org.jetbrains.dokka.links.DRI - -/** - * Provides the path to the page documenting a [DRI] in an external documentation source - */ -public fun interface ExternalLocationProvider { - /** - * @return Path to the page containing the [dri] or null if the path cannot be created - * (eg. when the package-list does not contain [dri]'s package) - */ - public fun resolve(dri: DRI): String? -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt deleted file mode 100644 index 952f4d51..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external - -import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation - -public fun interface ExternalLocationProviderFactory { - public fun getExternalLocationProvider(doc: ExternalDocumentation): ExternalLocationProvider? -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactoryWithCache.kt b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactoryWithCache.kt deleted file mode 100644 index 0b56e174..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactoryWithCache.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external - -import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation -import java.util.concurrent.ConcurrentHashMap - -public class ExternalLocationProviderFactoryWithCache( - public val ext: ExternalLocationProviderFactory -) : ExternalLocationProviderFactory { - - private val locationProviders = ConcurrentHashMap<ExternalDocumentation, CacheWrapper>() - - override fun getExternalLocationProvider(doc: ExternalDocumentation): ExternalLocationProvider? = - locationProviders.getOrPut(doc) { CacheWrapper(ext.getExternalLocationProvider(doc)) }.provider - - private class CacheWrapper(val provider: ExternalLocationProvider?) -} - diff --git a/plugins/base/src/main/kotlin/resolvers/external/javadoc/AndroidExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/javadoc/AndroidExternalLocationProvider.kt deleted file mode 100644 index 8c18be0c..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/AndroidExternalLocationProvider.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external.javadoc - -import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.plugability.DokkaContext - -public open class AndroidExternalLocationProvider( - externalDocumentation: ExternalDocumentation, - dokkaContext: DokkaContext -) : JavadocExternalLocationProvider(externalDocumentation, "", "", dokkaContext) { - - override fun anchorPart(callable: Callable): String = callable.name.toLowerCase() - -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt deleted file mode 100644 index 65ee0e02..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProvider.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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.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 - -public open class JavadocExternalLocationProvider( - externalDocumentation: ExternalDocumentation, - public val brackets: String, - public val separator: String, - dokkaContext: DokkaContext -) : DefaultExternalLocationProvider(externalDocumentation, ".html", dokkaContext) { - - override fun DRI.constructPath(): String { - val packageLink = packageName?.replace(".", "/") - val modulePart = packageName?.let { packageName -> - externalDocumentation.packageList.moduleFor(packageName)?.let { - if (it.isNotBlank()) - "$it/" - else - "" - } - }.orEmpty() - - val docWithModule = docURL + modulePart - - if (classNames == null) { - return "$docWithModule$packageLink/package-summary$extension".htmlEscape() - } - - if (DRIExtraContainer(extra)[EnumEntryDRIExtra] != null) { - val lastIndex = classNames?.lastIndexOf(".") ?: 0 - val (classSplit, enumEntityAnchor) = - classNames?.substring(0, lastIndex) to classNames?.substring(lastIndex + 1) - - val classLink = - if (packageLink == null) "${classSplit}$extension" else "$packageLink/${classSplit}$extension" - return "$docWithModule$classLink#$enumEntityAnchor".htmlEscape() - } - - val classLink = if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension" - val callableChecked = callable ?: return "$docWithModule$classLink".htmlEscape() - - return ("$docWithModule$classLink#" + anchorPart(callableChecked)).htmlEscape() - } - - protected open fun anchorPart(callable: Callable): String { - return callable.name + - "${brackets.first()}" + - callable.params.joinToString(separator) + - "${brackets.last()}" - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProviderFactory.kt deleted file mode 100644 index dc184e49..00000000 --- a/plugins/base/src/main/kotlin/resolvers/external/javadoc/JavadocExternalLocationProviderFactory.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.external.javadoc - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.androidSdk -import org.jetbrains.dokka.androidX -import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProviderFactory -import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProviderFactoryWithCache -import org.jetbrains.dokka.base.resolvers.shared.RecognizedLinkFormat -import org.jetbrains.dokka.plugability.DokkaContext - -public class JavadocExternalLocationProviderFactory( - public val context: DokkaContext, -) : ExternalLocationProviderFactory by ExternalLocationProviderFactoryWithCache( - { doc -> - when (doc.packageList.url) { - DokkaConfiguration.ExternalDocumentationLink.androidX().packageListUrl, - DokkaConfiguration.ExternalDocumentationLink.androidSdk().packageListUrl, - -> - AndroidExternalLocationProvider(doc, context) - - else -> - when (doc.packageList.linkFormat) { - RecognizedLinkFormat.Javadoc1 -> - JavadocExternalLocationProvider(doc, "()", ", ", context) // Covers JDK 1 - 7 - RecognizedLinkFormat.Javadoc8 -> - JavadocExternalLocationProvider(doc, "--", "-", context) // Covers JDK 8 - 9 - RecognizedLinkFormat.Javadoc10, - RecognizedLinkFormat.DokkaJavadoc, - -> - JavadocExternalLocationProvider(doc, "()", ",", context) // Covers JDK 10 - else -> null - } - } - } -) diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt deleted file mode 100644 index 24d0f13e..00000000 --- a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.local - -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider -import org.jetbrains.dokka.base.resolvers.external.Dokka010ExternalLocationProvider -import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProvider -import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProviderFactory -import org.jetbrains.dokka.base.resolvers.external.javadoc.AndroidExternalLocationProvider -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.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.pages.RootPageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.query - -public abstract class DefaultLocationProvider( - protected val pageGraphRoot: RootPageNode, - protected val dokkaContext: DokkaContext -) : LocationProvider { - protected val externalLocationProviderFactories: List<ExternalLocationProviderFactory> = - dokkaContext.plugin<DokkaBase>().query { externalLocationProviderFactory } - - protected val externalLocationProviders: Map<ExternalDocumentation, ExternalLocationProvider?> = dokkaContext - .configuration - .sourceSets - .flatMap { sourceSet -> - sourceSet.externalDocumentationLinks.map { - PackageList.load(it.packageListUrl, sourceSet.jdkVersion, dokkaContext.configuration.offlineMode) - ?.let { packageList -> ExternalDocumentation(it.url, packageList) } - } - } - .filterNotNull().associateWith { extDocInfo -> - externalLocationProviderFactories - .mapNotNull { it.getExternalLocationProvider(extDocInfo) } - .firstOrNull() - ?: run { dokkaContext.logger.error("No ExternalLocationProvider for '${extDocInfo.packageList.url}' found"); null } - } - - protected val packagesIndex: Map<String, ExternalLocationProvider?> = - externalLocationProviders - .flatMap { (extDocInfo, externalLocationProvider) -> - extDocInfo.packageList.packages.map { packageName -> packageName to externalLocationProvider } - }.groupBy { it.first }.mapValues { (_, lst) -> - lst.map { it.second } - .sortedWith(compareBy(nullsLast(ExternalLocationProviderOrdering)) { it }) - .firstOrNull() - } - .filterKeys(String::isNotBlank) - - - protected val locationsIndex: Map<String, ExternalLocationProvider?> = externalLocationProviders - .flatMap { (extDocInfo, externalLocationProvider) -> - extDocInfo.packageList.locations.keys.map { relocatedDri -> relocatedDri to externalLocationProvider } - } - .toMap() - .filterKeys(String::isNotBlank) - - protected open fun getExternalLocation(dri: DRI, sourceSets: Set<DisplaySourceSet>): String? = - packagesIndex[dri.packageName]?.resolve(dri) - ?: locationsIndex[dri.toString()]?.resolve(dri) - ?: externalLocationProviders.values.mapNotNull { it?.resolve(dri) }.firstOrNull() - - private object ExternalLocationProviderOrdering : Comparator<ExternalLocationProvider> { - private val desiredOrdering = listOf( - DefaultExternalLocationProvider::class, - Dokka010ExternalLocationProvider::class, - AndroidExternalLocationProvider::class, - JavadocExternalLocationProvider::class - ) - - override fun compare(o1: ExternalLocationProvider, o2: ExternalLocationProvider): Int = - desiredOrdering.indexOf(o1::class).compareTo(desiredOrdering.indexOf(o2::class)) - } - -} diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaBaseLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaBaseLocationProvider.kt deleted file mode 100644 index ca3786ad..00000000 --- a/plugins/base/src/main/kotlin/resolvers/local/DokkaBaseLocationProvider.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.local - -import org.jetbrains.dokka.base.renderers.shortenToUrl -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.pages.DCI -import org.jetbrains.dokka.pages.RootPageNode -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.utilities.urlEncoded - -public abstract class DokkaBaseLocationProvider( - pageGraphRoot: RootPageNode, - dokkaContext: DokkaContext -) : DefaultLocationProvider(pageGraphRoot, dokkaContext) { - - /** - * Anchors should be unique and should contain sourcesets, dri and contentKind. - * The idea is to make them as short as possible and just use a hashCode from sourcesets in order to match the - * 2040 characters limit - */ - public open fun anchorForDCI(dci: DCI, sourceSets: Set<DisplaySourceSet>): String = - (dci.dri.shortenToUrl().toString() + "/" + dci.kind + "/" + sourceSets.shortenToUrl()).urlEncoded() - -} diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt deleted file mode 100644 index aedbfb88..00000000 --- a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProvider.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.local - -import org.jetbrains.dokka.base.renderers.sourceSets -import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.PointingToDeclaration -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.plugability.DokkaContext -import java.util.* - -public open class DokkaLocationProvider( - pageGraphRoot: RootPageNode, - dokkaContext: DokkaContext, - public val extension: String = ".html" -) : DokkaBaseLocationProvider(pageGraphRoot, dokkaContext) { - protected open val PAGE_WITH_CHILDREN_SUFFIX: String = "index" - - protected open val pathsIndex: Map<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>().apply { - fun registerPath(page: PageNode, prefix: List<String>) { - if (page is RootPageNode && page.forceTopLevelName) { - put(page, prefix + PAGE_WITH_CHILDREN_SUFFIX) - page.children.forEach { registerPath(it, prefix) } - } else { - val newPrefix = prefix + page.pathName - put(page, if (page is ModulePageNode) prefix else newPrefix) - page.children.forEach { registerPath(it, newPrefix) } - } - - } - put(pageGraphRoot, emptyList()) - pageGraphRoot.children.forEach { registerPath(it, emptyList()) } - } - - protected val pagesIndex: Map<DRIWithSourceSets, ContentPage> = - pageGraphRoot.withDescendants().filterIsInstance<ContentPage>() - .flatMap { page -> - page.dri.flatMap { dri -> - page.sourceSets().ifEmpty { setOf(null) } - .map { sourceSet -> DRIWithSourceSets(dri, setOfNotNull(sourceSet)) to page } - .let { - if (it.size > 1) { - it + (DRIWithSourceSets(dri, page.sourceSets()) to page) - } else { - it - } - } - } - } - .groupingBy { it.first } - .aggregate { key, _, (_, page), first -> - if (first) page else throw AssertionError("Multiple pages associated with key: ${key.dri}/${key.sourceSet}") - } - - protected val anchorsIndex: Map<DRIWithSourceSets, PageWithKind> = - pageGraphRoot.withDescendants().filterIsInstance<ContentPage>() - .flatMap { page -> - page.content.withDescendants() - .filter { it.extra[SymbolAnchorHint] != null && it.dci.dri.any() } - .flatMap { content -> - content.dci.dri.map { dri -> - (dri to content.sourceSets) to content.extra[SymbolAnchorHint]?.contentKind!! - } - } - .distinct() - .flatMap { (pair, kind) -> - val (dri, sourceSets) = pair - sourceSets.ifEmpty { setOf(null) }.map { sourceSet -> - DRIWithSourceSets(dri, setOfNotNull(sourceSet)) to PageWithKind(page, kind) - } - } - }.toMap() - - override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = - pathTo(node, context) + if (!skipExtension) extension else "" - - override fun resolve(dri: DRI, sourceSets: Set<DisplaySourceSet>, context: PageNode?): String? = - sourceSets.ifEmpty { setOf(null) }.mapNotNull { sourceSet -> - val driWithSourceSets = DRIWithSourceSets(dri, setOfNotNull(sourceSet)) - getLocalLocation(driWithSourceSets, context) - ?: getLocalLocation(driWithSourceSets.copy(dri = dri.copy(target = PointingToDeclaration)), context) - // Not found in PageGraph, that means it's an external link - ?: getExternalLocation(dri, sourceSets) - ?: getExternalLocation(dri.copy(target = PointingToDeclaration), sourceSets) - }.distinct().singleOrNull() - - private fun getLocalLocation(driWithSourceSets: DRIWithSourceSets, context: PageNode?): String? { - val (dri, originalSourceSet) = driWithSourceSets - val allSourceSets: List<Set<DisplaySourceSet>> = - listOf(originalSourceSet) + originalSourceSet.let { oss -> - val ossIds = oss.computeSourceSetIds() - dokkaContext.configuration.sourceSets.filter { it.sourceSetID in ossIds } - .flatMap { it.dependentSourceSets } - .mapNotNull { ssid -> - dokkaContext.configuration.sourceSets.find { it.sourceSetID == ssid }?.toDisplaySourceSet() - }.map { - setOf(it) - } - } - - return getLocalPageLink(dri, allSourceSets, context) - ?: getLocalAnchor(dri, allSourceSets, context) - } - - private fun getLocalPageLink(dri: DRI, allSourceSets: Iterable<Set<DisplaySourceSet>>, context: PageNode?) = - allSourceSets.mapNotNull { displaySourceSet -> - pagesIndex[DRIWithSourceSets(dri, displaySourceSet)] - }.firstOrNull()?.let { page -> resolve(page, context) } - - private fun getLocalAnchor(dri: DRI, allSourceSets: Iterable<Set<DisplaySourceSet>>, context: PageNode?) = - allSourceSets.mapNotNull { displaySourceSet -> - anchorsIndex[DRIWithSourceSets(dri, displaySourceSet)]?.let { (page, kind) -> - val dci = DCI(setOf(dri), kind) - resolve(page, context) + "#" + anchorForDCI(dci, displaySourceSet) - } - }.firstOrNull() - - override fun pathToRoot(from: PageNode): String = - pathTo(pageGraphRoot, from).removeSuffix(PAGE_WITH_CHILDREN_SUFFIX) - - override fun ancestors(node: PageNode): List<PageNode> = - generateSequence(node) { it.parent() }.toList() - - protected open fun pathTo(node: PageNode, context: PageNode?): String { - fun pathFor(page: PageNode) = pathsIndex[page] ?: throw AssertionError( - "${page::class.simpleName}(${page.name}) does not belong to the current page graph so it is impossible to compute its path" - ) - - val nodePath = pathFor(node) - val contextPath = context?.let { pathFor(it) }.orEmpty() - val endedContextPath = if (context?.isIndexPage() == false) - contextPath.toMutableList().also { it.removeLastOrNull() } - else contextPath - - val commonPathElements = nodePath.asSequence().zip(endedContextPath.asSequence()) - .takeWhile { (a, b) -> a == b }.count() - - return (List(endedContextPath.size - commonPathElements) { ".." } + nodePath.drop(commonPathElements) + - if (node.isIndexPage()) - listOf(PAGE_WITH_CHILDREN_SUFFIX) - else - emptyList() - ).joinToString("/") - } - - private fun PageNode.isIndexPage() = this is ClasslikePageNode || children.isNotEmpty() - - private fun PageNode.parent() = pageGraphRoot.parentMap[this] - - private val PageNode.pathName: String - get() = if (this is PackagePageNode || this is RendererSpecificResourcePage) name else identifierToFilename(name) - - protected data class DRIWithSourceSets(val dri: DRI, val sourceSet: Set<DisplaySourceSet>) - - protected data class PageWithKind(val page: ContentPage, val kind: Kind) - - public companion object { - public val reservedFilenames: Set<String> = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out") - - //Taken from: https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names - internal val reservedCharacters = setOf('|', '>', '<', '*', ':', '"', '?', '%') - - public fun identifierToFilename(name: String): String { - if (name.isEmpty()) return "--root--" - return sanitizeFileName(name, reservedFilenames, reservedCharacters) - } - } -} - -internal fun sanitizeFileName(name: String, reservedFileNames: Set<String>, reservedCharacters: Set<Char>): String { - val lowercase = name.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } - val withoutReservedFileNames = if (lowercase in reservedFileNames) "--$lowercase--" else lowercase - return reservedCharacters.fold(withoutReservedFileNames) { acc, character -> - if (character in acc) acc.replace(character.toString(), "[${character.toInt()}]") - else acc - } -} - diff --git a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProviderFactory.kt deleted file mode 100644 index bd9fa1bb..00000000 --- a/plugins/base/src/main/kotlin/resolvers/local/DokkaLocationProviderFactory.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.local - -import org.jetbrains.dokka.pages.RootPageNode -import org.jetbrains.dokka.plugability.DokkaContext -import java.util.concurrent.ConcurrentHashMap - -public class DokkaLocationProviderFactory( - private val context: DokkaContext -) : LocationProviderFactory { - private val cache = ConcurrentHashMap<CacheWrapper, LocationProvider>() - - override fun getLocationProvider(pageNode: RootPageNode): LocationProvider { - return cache.computeIfAbsent(CacheWrapper(pageNode)) { - DokkaLocationProvider(pageNode, context) - } - } - - private class CacheWrapper(val pageNode: RootPageNode) { - override fun equals(other: Any?) = other is CacheWrapper && other.pageNode == this.pageNode - override fun hashCode() = System.identityHashCode(pageNode) - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt deleted file mode 100644 index dbcd5c76..00000000 --- a/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.local - -import org.jetbrains.dokka.DokkaException -import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProvider.Companion.identifierToFilename -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.pages.PageNode - -public interface LocationProvider { - public fun resolve(dri: DRI, sourceSets: Set<DisplaySourceSet>, context: PageNode? = null): String? - public fun resolve(node: PageNode, context: PageNode? = null, skipExtension: Boolean = false): String? - public fun pathToRoot(from: PageNode): String - public fun ancestors(node: PageNode): List<PageNode> - - /** - * This method should return guessed filesystem location for a given [DRI] - * It is used to decide if a [DRI] should be present in the relocation list of the - * generated package-list so it is ok if the path differs from the one returned by [resolve] - * @return Path to a giver [DRI] or null if path should not be considered for relocations - */ - public fun expectedLocationForDri(dri: DRI): String = - (listOf(dri.packageName) + - dri.classNames?.split(".")?.map { identifierToFilename(it) }.orEmpty() + - listOf(dri.callable?.let { identifierToFilename(it.name) } ?: "index") - ).filterNotNull().joinToString("/") -} - -public fun LocationProvider.resolveOrThrow( - dri: DRI, sourceSets: Set<DisplaySourceSet>, - context: PageNode? = null -): String { - return resolve(dri = dri, sourceSets = sourceSets, context = context) - ?: throw DokkaException("Cannot resolve path for $dri") -} - -public fun LocationProvider.resolveOrThrow( - node: PageNode, - context: PageNode? = null, - skipExtension: Boolean = false -): String { - return resolve(node = node, context = context, skipExtension = skipExtension) - ?: throw DokkaException("Cannot resolve path for ${node.name}") -} diff --git a/plugins/base/src/main/kotlin/resolvers/local/LocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/local/LocationProviderFactory.kt deleted file mode 100644 index 31cac868..00000000 --- a/plugins/base/src/main/kotlin/resolvers/local/LocationProviderFactory.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.local - -import org.jetbrains.dokka.pages.RootPageNode - -public fun interface LocationProviderFactory { - public fun getLocationProvider(pageNode: RootPageNode): LocationProvider -} diff --git a/plugins/base/src/main/kotlin/resolvers/shared/ExternalDocumentation.kt b/plugins/base/src/main/kotlin/resolvers/shared/ExternalDocumentation.kt deleted file mode 100644 index db0c5492..00000000 --- a/plugins/base/src/main/kotlin/resolvers/shared/ExternalDocumentation.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.shared - -import java.net.URL - -public data class ExternalDocumentation(val documentationURL: URL, val packageList: PackageList) diff --git a/plugins/base/src/main/kotlin/resolvers/shared/LinkFormat.kt b/plugins/base/src/main/kotlin/resolvers/shared/LinkFormat.kt deleted file mode 100644 index 4f0d4932..00000000 --- a/plugins/base/src/main/kotlin/resolvers/shared/LinkFormat.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.shared - -public interface LinkFormat { - public val formatName: String - public val linkExtension: String -} diff --git a/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt b/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt deleted file mode 100644 index 8297f875..00000000 --- a/plugins/base/src/main/kotlin/resolvers/shared/PackageList.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.shared - -import java.net.URL - -public typealias Module = String - -public data class PackageList( - val linkFormat: RecognizedLinkFormat, - val modules: Map<Module, Set<String>>, - val locations: Map<String, String>, - val url: URL -) { - val packages: Set<String> - get() = modules.values.flatten().toSet() - - public fun moduleFor(packageName: String): Module? { - return modules.asSequence() - .filter { it.value.contains(packageName) } - .firstOrNull()?.key - } - - public companion object { - public const val PACKAGE_LIST_NAME: String = "package-list" - public const val MODULE_DELIMITER: String = "module:" - public const val DOKKA_PARAM_PREFIX: String = "\$dokka" - public const val SINGLE_MODULE_NAME: String = "" - - public fun load(url: URL, jdkVersion: Int, offlineMode: Boolean = false): PackageList? { - if (offlineMode && url.protocol.toLowerCase() != "file") - return null - - val packageListStream = runCatching { url.readContent() }.onFailure { - println("Failed to download package-list from $url, this might suggest that remote resource is not available," + - " module is empty or dokka output got corrupted") - return null - }.getOrThrow() - - val (params, packages) = packageListStream - .bufferedReader() - .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } } - - val paramsMap = splitParams(params) - val format = linkFormat(paramsMap["format"]?.singleOrNull(), jdkVersion) - val locations = splitLocations(paramsMap["location"].orEmpty()).filterKeys(String::isNotEmpty) - - val modulesMap = splitPackages(packages) - return PackageList(format, modulesMap, locations, url) - } - - private fun splitParams(params: List<String>) = params.asSequence() - .map { it.removePrefix("$DOKKA_PARAM_PREFIX.").split(":", limit = 2) } - .groupBy({ (key, _) -> key }, { (_, value) -> value }) - - private fun splitLocations(locations: List<String>) = locations.map { it.split("\u001f", limit = 2) } - .associate { (key, value) -> key to value } - - private fun splitPackages(packages: List<String>): Map<Module, Set<String>> = - packages.fold(("" to mutableMapOf<Module, Set<String>>())) { (lastModule, acc), el -> - val currentModule : String - when { - el.startsWith(MODULE_DELIMITER) -> currentModule = el.substringAfter(MODULE_DELIMITER) - el.isNotBlank() -> { - currentModule = lastModule - acc[currentModule] = acc.getOrDefault(lastModule, emptySet()) + el - } - else -> currentModule = lastModule - } - currentModule to acc - }.second - - private fun linkFormat(formatName: String?, jdkVersion: Int) = - formatName?.let { RecognizedLinkFormat.fromString(it) } - ?: when { - jdkVersion < 8 -> RecognizedLinkFormat.Javadoc1 // Covers JDK 1 - 7 - jdkVersion < 10 -> RecognizedLinkFormat.Javadoc8 // Covers JDK 8 - 9 - else -> RecognizedLinkFormat.Javadoc10 // Covers JDK 10+ - } - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/shared/RecognizedLinkFormat.kt b/plugins/base/src/main/kotlin/resolvers/shared/RecognizedLinkFormat.kt deleted file mode 100644 index 4810c9e5..00000000 --- a/plugins/base/src/main/kotlin/resolvers/shared/RecognizedLinkFormat.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.shared - -public enum class RecognizedLinkFormat( - override val formatName: String, - override val linkExtension: String -) : LinkFormat { - DokkaHtml("html-v1", "html"), - DokkaJavadoc("javadoc-v1", "html"), - DokkaGFM("gfm-v1", "md"), - DokkaJekyll("jekyll-v1", "html"), - Javadoc1("javadoc1", "html"), - Javadoc8("javadoc8", "html"), - Javadoc10("javadoc10", "html"), - DokkaOldHtml("html", "html"), - KotlinWebsite("kotlin-website", "html"), - KotlinWebsiteHtml("kotlin-website-html", "html"); - - public companion object { - private val values = values() - - public fun fromString(formatName: String): RecognizedLinkFormat? { - return values.firstOrNull { it.formatName == formatName } - } - } -} diff --git a/plugins/base/src/main/kotlin/resolvers/shared/utils.kt b/plugins/base/src/main/kotlin/resolvers/shared/utils.kt deleted file mode 100644 index a6d9afc6..00000000 --- a/plugins/base/src/main/kotlin/resolvers/shared/utils.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.resolvers.shared - -import java.io.InputStream -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLConnection - -internal fun URL.readContent(timeout: Int = 10000, redirectsAllowed: Int = 16): InputStream { - fun URL.doOpenConnection(timeout: Int, redirectsAllowed: Int): URLConnection { - val connection = this.openConnection().apply { - connectTimeout = timeout - readTimeout = timeout - } - - when (connection) { - is HttpURLConnection -> return when (connection.responseCode) { - in 200..299 -> connection - - HttpURLConnection.HTTP_MOVED_PERM, - HttpURLConnection.HTTP_MOVED_TEMP, - HttpURLConnection.HTTP_SEE_OTHER -> { - if (redirectsAllowed > 0) { - val newUrl = connection.getHeaderField("Location") - URL(newUrl).doOpenConnection(timeout, redirectsAllowed - 1) - } else { - throw RuntimeException("Too many redirects") - } - } - - else -> throw RuntimeException("Unhandled HTTP code: ${connection.responseCode}") - } - - else -> return connection - } - } - return doOpenConnection(timeout, redirectsAllowed).getInputStream() -} diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt deleted file mode 100644 index e5f85803..00000000 --- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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.* - -public interface JvmSignatureUtils { - - public fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: AnnotationTarget) - - public fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: AnnotationTarget) - - public fun <T : Documentable> WithExtraProperties<T>.modifiers(): SourceSetDependent<Set<ExtraModifiers>> - - public fun Collection<ExtraModifiers>.toSignatureString(): String = - joinToString("") { it.name.toLowerCase() + " " } - - @Suppress("UNCHECKED_CAST") - public fun Documentable.annotations(): Map<DokkaSourceSet, List<Annotations.Annotation>> { - return (this as? WithExtraProperties<Documentable>)?.annotations() ?: emptyMap() - } - - public fun <T : AnnotationTarget> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> = - extra[Annotations]?.directAnnotations ?: emptyMap() - - @Suppress("UNCHECKED_CAST") - public operator fun <T : Iterable<*>> SourceSetDependent<T>.plus(other: SourceSetDependent<T>): SourceSetDependent<T> { - return LinkedHashMap(this).apply { - for ((k, v) in other) { - put(k, get(k).let { if (it != null) (it + v) as T else v }) - } - } - } - - public fun DProperty.annotations(): SourceSetDependent<List<Annotations.Annotation>> { - return (extra[Annotations]?.directAnnotations ?: emptyMap()) + - (getter?.annotations() ?: emptyMap()).mapValues { it.value.map { it.copy( scope = Annotations.AnnotationScope.GETTER) } } + - (setter?.annotations() ?: emptyMap()).mapValues { it.value.map { it.copy( scope = Annotations.AnnotationScope.SETTER) } } - } - - private fun PageContentBuilder.DocumentableContentBuilder.annotations( - d: AnnotationTarget, - ignored: Set<Annotations.Annotation>, - styles: Set<Style>, - operation: PageContentBuilder.DocumentableContentBuilder.(Annotations.Annotation) -> Unit - ): Unit = when (d) { - is DFunction -> d.annotations() - is DProperty -> d.annotations() - is DClass -> d.annotations() - is DInterface -> d.annotations() - is DObject -> d.annotations() - is DEnum -> d.annotations() - is DAnnotation -> d.annotations() - is DTypeParameter -> d.annotations() - is DEnumEntry -> d.annotations() - is DTypeAlias -> d.annotations() - is DParameter -> d.annotations() - is TypeParameter -> d.annotations() - is GenericTypeConstructor -> d.annotations() - is FunctionalTypeConstructor -> d.annotations() - is JavaObject -> d.annotations() - else -> null - }?.let { - it.entries.forEach { - it.value.filter { it !in ignored && it.mustBeDocumented }.takeIf { it.isNotEmpty() }?.let { annotations -> - group(sourceSets = setOf(it.key), styles = styles, kind = ContentKind.Annotations) { - annotations.forEach { - operation(it) - } - } - } - } - } ?: Unit - - public fun PageContentBuilder.DocumentableContentBuilder.toSignatureString( - a: Annotations.Annotation, - renderAtStrategy: AtStrategy, - listBrackets: Pair<Char, Char>, - classExtension: String - ) { - - when (renderAtStrategy) { - is All, is OnlyOnce -> { - when(a.scope) { - Annotations.AnnotationScope.GETTER -> text("@get:", styles = mainStyles + TokenStyle.Annotation) - Annotations.AnnotationScope.SETTER -> text("@set:", styles = mainStyles + TokenStyle.Annotation) - else -> text("@", styles = mainStyles + TokenStyle.Annotation) - } - link(a.dri.classNames!!, a.dri, styles = mainStyles + TokenStyle.Annotation) - } - is Never -> link(a.dri.classNames!!, a.dri) - } - val isNoWrappedBrackets = a.params.entries.isEmpty() && renderAtStrategy is OnlyOnce - listParams( - a.params.entries, - if (isNoWrappedBrackets) null else Pair('(', ')') - ) { - text(it.key) - text(" = ", styles = mainStyles + TokenStyle.Operator) - when (renderAtStrategy) { - is All -> All - is Never, is OnlyOnce -> Never - }.let { strategy -> - valueToSignature(it.value, strategy, listBrackets, classExtension) - } - } - } - - private fun PageContentBuilder.DocumentableContentBuilder.valueToSignature( - a: AnnotationParameterValue, - renderAtStrategy: AtStrategy, - listBrackets: Pair<Char, Char>, - classExtension: String - ): Unit = when (a) { - is AnnotationValue -> toSignatureString(a.annotation, renderAtStrategy, listBrackets, classExtension) - is ArrayValue -> { - listParams(a.value, listBrackets) { valueToSignature(it, renderAtStrategy, listBrackets, classExtension) } - } - is EnumValue -> link(a.enumName, a.enumDri) - is ClassValue -> link(a.className + classExtension, a.classDRI) - is StringValue -> group(styles = setOf(TextStyle.Breakable)) { stringLiteral( "\"${a.text()}\"") } - is BooleanValue -> group(styles = setOf(TextStyle.Breakable)) { booleanLiteral(a.value) } - is LiteralValue -> group(styles = setOf(TextStyle.Breakable)) { constant(a.text()) } - } - - private fun<T> PageContentBuilder.DocumentableContentBuilder.listParams( - params: Collection<T>, - listBrackets: Pair<Char, Char>?, - outFn: PageContentBuilder.DocumentableContentBuilder.(T) -> Unit - ) { - listBrackets?.let{ punctuation(it.first.toString()) } - params.forEachIndexed { i, it -> - group(styles = setOf(TextStyle.BreakableAfter)) { - this.outFn(it) - if (i != params.size - 1) punctuation(", ") - } - } - listBrackets?.let{ punctuation(it.second.toString()) } - } - - public fun PageContentBuilder.DocumentableContentBuilder.annotationsBlockWithIgnored( - d: AnnotationTarget, - ignored: Set<Annotations.Annotation>, - renderAtStrategy: AtStrategy, - listBrackets: Pair<Char, Char>, - classExtension: String - ) { - annotations(d, ignored, setOf(TextStyle.Block)) { - group { - toSignatureString(it, renderAtStrategy, listBrackets, classExtension) - } - } - } - - public fun PageContentBuilder.DocumentableContentBuilder.annotationsInlineWithIgnored( - d: AnnotationTarget, - ignored: Set<Annotations.Annotation>, - renderAtStrategy: AtStrategy, - listBrackets: Pair<Char, Char>, - classExtension: String - ) { - annotations(d, ignored, setOf(TextStyle.Span)) { - toSignatureString(it, renderAtStrategy, listBrackets, classExtension) - text(Typography.nbsp.toString()) - } - } - - public fun <T : Documentable> WithExtraProperties<T>.stylesIfDeprecated(sourceSetData: DokkaSourceSet): Set<TextStyle> { - val directAnnotations = extra[Annotations]?.directAnnotations?.get(sourceSetData) ?: emptyList() - val hasAnyDeprecatedAnnotation = - directAnnotations.any { it.dri == DRI("kotlin", "Deprecated") || it.dri == DRI("java.lang", "Deprecated") } - - return if (hasAnyDeprecatedAnnotation) setOf(TextStyle.Strikethrough) else emptySet() - } - - public infix fun DFunction.uses(typeParameter: DTypeParameter): Boolean { - val parameterDris = parameters.flatMap { listOf(it.dri) + it.type.drisOfAllNestedBounds } - val receiverDris = - listOfNotNull( - receiver?.dri, - *receiver?.type?.drisOfAllNestedBounds?.toTypedArray() ?: emptyArray() - ) - val allDris = parameterDris + receiverDris - return typeParameter.dri in allDris - } - - /** - * Builds a distinguishable [function] parameters block, so that it - * can be processed or custom rendered down the road. - * - * Resulting structure: - * ``` - * SymbolContentKind.Parameters(style = wrapped) { - * SymbolContentKind.Parameter(style = indented) { param, } - * SymbolContentKind.Parameter(style = indented) { param, } - * SymbolContentKind.Parameter(style = indented) { param } - * } - * ``` - * Wrapping and indentation of parameters is applied conditionally, see [shouldWrapParams] - */ - public fun PageContentBuilder.DocumentableContentBuilder.parametersBlock( - function: DFunction, - paramBuilder: PageContentBuilder.DocumentableContentBuilder.(DParameter) -> Unit - ) { - group(kind = SymbolContentKind.Parameters, styles = emptySet()) { - function.parameters.dropLast(1).forEach { - group(kind = SymbolContentKind.Parameter) { - paramBuilder(it) - punctuation(", ") - } - } - group(kind = SymbolContentKind.Parameter) { - paramBuilder(function.parameters.last()) - } - } - } -} - -public sealed class AtStrategy -public object All : AtStrategy() -public object OnlyOnce : AtStrategy() -public object Never : AtStrategy() diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt deleted file mode 100644 index 2180e776..00000000 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.signatures - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.dri -import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.driOrNull -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder -import org.jetbrains.dokka.links.* -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.Nullable -import org.jetbrains.dokka.model.TypeConstructor -import org.jetbrains.dokka.model.properties.WithExtraProperties -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.utilities.DokkaLogger -import kotlin.text.Typography.nbsp - -public class KotlinSignatureProvider( - ctcc: CommentsToContentConverter, - logger: DokkaLogger -) : SignatureProvider, JvmSignatureUtils by KotlinSignatureUtils { - - public constructor(context: DokkaContext) : this( - context.plugin<DokkaBase>().querySingle { commentsToContentConverter }, - context.logger, - ) - - private val contentBuilder = PageContentBuilder(ctcc, this, logger) - - private val ignoredVisibilities = setOf(JavaVisibility.Public, KotlinVisibility.Public) - private val ignoredModifiers = setOf(JavaModifier.Final, KotlinModifier.Final) - private val ignoredExtraModifiers = setOf( - ExtraModifiers.KotlinOnlyModifiers.TailRec, - ExtraModifiers.KotlinOnlyModifiers.External - ) - private val platformSpecificModifiers: Map<ExtraModifiers, Set<Platform>> = mapOf( - ExtraModifiers.KotlinOnlyModifiers.External to setOf(Platform.js, Platform.wasm) - ) - - override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { - is DFunction -> functionSignature(documentable) - is DProperty -> propertySignature(documentable) - is DClasslike -> classlikeSignature(documentable) - is DTypeParameter -> signature(documentable) - is DEnumEntry -> signature(documentable) - is DTypeAlias -> signature(documentable) - else -> throw NotImplementedError( - "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" - ) - } - - private fun <T> PageContentBuilder.DocumentableContentBuilder.processExtraModifiers(t: T) - where T : Documentable, T : WithExtraProperties<T> { - sourceSetDependentText( - t.modifiers() - .mapValues { entry -> - entry.value.filter { - it !in ignoredExtraModifiers || entry.key.analysisPlatform in (platformSpecificModifiers[it] - ?: emptySet()) - } - }, styles = mainStyles + TokenStyle.Keyword - ) { - it.toSignatureString() - } - } - - private fun signature(e: DEnumEntry): List<ContentNode> = - e.sourceSets.map { - contentBuilder.contentFor( - e, - ContentKind.Symbol, - setOf(TextStyle.Monospace), - sourceSets = setOf(it) - ) { - group(styles = setOf(TextStyle.Block)) { - annotationsBlock(e) - link(e.name, e.dri, styles = mainStyles + e.stylesIfDeprecated(it)) - } - } - } - - private fun classlikeSignature(c: DClasslike): List<ContentNode> { - @Suppress("UNCHECKED_CAST") - val typeAlias = (c as? WithExtraProperties<DClasslike>) - ?.extra - ?.get(ActualTypealias) - ?.typeAlias - - return c.sourceSets.map { sourceSetData -> - if (typeAlias != null && sourceSetData in typeAlias.sourceSets) { - regularSignature(typeAlias, sourceSetData) - } else { - regularSignature(c, sourceSetData) - } - } - } - - private fun <T : Documentable> PageContentBuilder.DocumentableContentBuilder.defaultValueAssign( - d: WithExtraProperties<T>, - sourceSet: DokkaSourceSet - ) { - // a default value of parameter can be got from expect source set - // but expect properties cannot have a default value - d.extra[DefaultValue]?.expression?.let { - it[sourceSet] ?: if (d is DParameter) it[d.expectPresentInSet] else null - }?.let { expr -> - operator(" = ") - highlightValue(expr) - } - } - - private fun regularSignature(c: DClasslike, sourceSet: DokkaSourceSet): ContentGroup { - @Suppress("UNCHECKED_CAST") - val deprecationStyles = (c as? WithExtraProperties<out Documentable>) - ?.stylesIfDeprecated(sourceSet) - ?: emptySet() - - return contentBuilder.contentFor( - c, - ContentKind.Symbol, - setOf(TextStyle.Monospace), - sourceSets = setOf(sourceSet) - ) { - annotationsBlock(c) - c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } - if (c.isExpectActual) keyword(if (sourceSet == c.expectPresentInSet) "expect " else "actual ") - if (c is DClass) { - val modifier = - if (c.modifier[sourceSet] !in ignoredModifiers) { - when { - c.extra[AdditionalModifiers]?.content?.get(sourceSet)?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> "" - c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} " - else -> c.modifier[sourceSet]?.name?.let { "$it " } - } - } else { - null - } - modifier?.takeIf { it.isNotEmpty() }?.let { keyword(it) } - } - when (c) { - is DClass -> { - processExtraModifiers(c) - keyword("class ") - } - is DInterface -> { - processExtraModifiers(c) - keyword("interface ") - } - is DEnum -> { - processExtraModifiers(c) - keyword("enum ") - } - is DObject -> { - processExtraModifiers(c) - keyword("object ") - } - is DAnnotation -> { - processExtraModifiers(c) - keyword("annotation class ") - } - } - link(c.name!!, c.dri, styles = mainStyles + deprecationStyles) - if (c is WithGenerics) { - list(c.generics, prefix = "<", suffix = ">", - separatorStyles = mainStyles + TokenStyle.Punctuation, - surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { - annotationsInline(it) - +buildSignature(it) - } - } - if (c is WithConstructors) { - val pConstructor = c.constructors.singleOrNull { it.extra[PrimaryConstructorExtra] != null } - if (pConstructor?.sourceSets?.contains(sourceSet) == true) { - if (pConstructor.annotations().values.any { it.isNotEmpty() }) { - text(nbsp.toString()) - annotationsInline(pConstructor) - keyword("constructor") - } - - // for primary constructor, opening and closing parentheses - // should be present only if it has parameters. If there are - // no parameters, it should result in `class Example` - if (pConstructor.parameters.isNotEmpty()) { - val parameterPropertiesByName = c.properties - .filter { it.isAlsoParameter(sourceSet) } - .associateBy { it.name } - - punctuation("(") - parametersBlock(pConstructor) { param -> - annotationsInline(param) - parameterPropertiesByName[param.name]?.let { property -> - property.setter?.let { keyword("var ") } ?: keyword("val ") - } - text(param.name.orEmpty()) - operator(": ") - signatureForProjection(param.type) - defaultValueAssign(param, sourceSet) - } - punctuation(")") - } - } - } - if (c is WithSupertypes) { - c.supertypes.filter { it.key == sourceSet }.map { (s, typeConstructors) -> - list(typeConstructors, prefix = " : ", sourceSets = setOf(s)) { - link(it.typeConstructor.dri.sureClassNames, it.typeConstructor.dri, sourceSets = setOf(s)) - list(it.typeConstructor.projections, prefix = "<", suffix = "> ", - separatorStyles = mainStyles + TokenStyle.Punctuation, - surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { - signatureForProjection(it) - } - } - } - } - } - } - - /** - * 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): Boolean { - return this.extra[IsAlsoParameter] - ?.inSourceSets - ?.any { it.sourceSetID == sourceSet.sourceSetID } - ?: false - } - - private fun propertySignature(p: DProperty) = - p.sourceSets.map { sourceSet -> - contentBuilder.contentFor( - p, - ContentKind.Symbol, - setOf(TextStyle.Monospace), - sourceSets = setOf(sourceSet) - ) { - annotationsBlock(p) - p.visibility[sourceSet].takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } - if (p.isExpectActual) keyword(if (sourceSet == p.expectPresentInSet) "expect " else "actual ") - p.modifier[sourceSet].takeIf { it !in ignoredModifiers }?.let { - if (it is JavaModifier.Empty) KotlinModifier.Open else it - }?.name?.let { keyword("$it ") } - p.modifiers()[sourceSet]?.toSignatureString()?.let { keyword(it) } - if (p.isMutable()) keyword("var ") else keyword("val ") - list(p.generics, prefix = "<", suffix = "> ", - separatorStyles = mainStyles + TokenStyle.Punctuation, - surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { - annotationsInline(it) - +buildSignature(it) - } - p.receiver?.also { - signatureForProjection(it.type) - punctuation(".") - } - link(p.name, p.dri, styles = mainStyles + p.stylesIfDeprecated(sourceSet)) - operator(": ") - signatureForProjection(p.type) - - if (p.isNotMutable()) { - defaultValueAssign(p, sourceSet) - } - } - } - - private fun DProperty.isNotMutable(): Boolean = !isMutable() - - private fun DProperty.isMutable(): Boolean { - return this.extra[IsVar] != null || this.setter != null - } - - private fun PageContentBuilder.DocumentableContentBuilder.highlightValue(expr: Expression) = when (expr) { - is IntegerConstant -> constant(expr.value.toString()) - is FloatConstant -> constant(expr.value.toString() + "f") - is DoubleConstant -> constant(expr.value.toString()) - is BooleanConstant -> booleanLiteral(expr.value) - is StringConstant -> stringLiteral("\"${expr.value}\"") - is ComplexExpression -> text(expr.value) - else -> Unit - } - - private fun functionSignature(f: DFunction) = - f.sourceSets.map { sourceSet -> - contentBuilder.contentFor( - f, - ContentKind.Symbol, - setOf(TextStyle.Monospace), - sourceSets = setOf(sourceSet) - ) { - annotationsBlock(f) - f.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } - if (f.isExpectActual) keyword(if (sourceSet == f.expectPresentInSet) "expect " else "actual ") - if (f.isConstructor) { - keyword("constructor") - } else { - f.modifier[sourceSet]?.takeIf { it !in ignoredModifiers }?.let { - if (it is JavaModifier.Empty) KotlinModifier.Open else it - }?.name?.let { keyword("$it ") } - f.modifiers()[sourceSet]?.toSignatureString()?.let { keyword(it) } - keyword("fun ") - list( - f.generics, prefix = "<", suffix = "> ", - separatorStyles = mainStyles + TokenStyle.Punctuation, - surroundingCharactersStyle = mainStyles + TokenStyle.Operator - ) { - annotationsInline(it) - +buildSignature(it) - } - f.receiver?.also { - signatureForProjection(it.type) - punctuation(".") - } - link(f.name, f.dri, styles = mainStyles + TokenStyle.Function + f.stylesIfDeprecated(sourceSet)) - } - // for a function, opening and closing parentheses must be present - // anyway, even if it has no parameters, resulting in `fun test(): R` - punctuation("(") - if (f.parameters.isNotEmpty()) { - parametersBlock(f) { param -> - annotationsInline(param) - processExtraModifiers(param) - text(param.name!!) - operator(": ") - signatureForProjection(param.type) - defaultValueAssign(param, sourceSet) - } - } - punctuation(")") - if (f.documentReturnType()) { - operator(": ") - signatureForProjection(f.type) - } - } - } - - private fun DFunction.documentReturnType() = when { - this.isConstructor -> false - this.type is TypeConstructor && (this.type as TypeConstructor).dri == DriOfUnit -> false - this.type is Void -> false - else -> true - } - - private fun signature(t: DTypeAlias) = - t.sourceSets.map { - regularSignature(t, it) - } - - private fun regularSignature( - t: DTypeAlias, - sourceSet: DokkaSourceSet - ) = contentBuilder.contentFor(t, sourceSets = setOf(sourceSet)) { - t.underlyingType.entries.groupBy({ it.value }, { it.key }).map { (type, platforms) -> - +contentBuilder.contentFor( - t, - ContentKind.Symbol, - setOf(TextStyle.Monospace), - sourceSets = platforms.toSet() - ) { - annotationsBlock(t) - t.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } - if (t.expectPresentInSet != null) keyword("actual ") - processExtraModifiers(t) - keyword("typealias ") - group(styles = mainStyles + t.stylesIfDeprecated(sourceSet)) { - signatureForProjection(t.type) - } - operator(" = ") - signatureForTypealiasTarget(t, type) - } - } - } - - private fun signature(t: DTypeParameter) = - t.sourceSets.map { - contentBuilder.contentFor(t, sourceSets = setOf(it)) { - group(styles = mainStyles + t.stylesIfDeprecated(it)) { - signatureForProjection(t.variantTypeParameter.withDri(t.dri.withTargetToDeclaration())) - } - list( - elements = t.nontrivialBounds, - prefix = " : ", - surroundingCharactersStyle = mainStyles + TokenStyle.Operator - ) { bound -> - signatureForProjection(bound) - } - } - } - - private fun PageContentBuilder.DocumentableContentBuilder.signatureForTypealiasTarget( - typeAlias: DTypeAlias, bound: Bound - ) { - signatureForProjection( - p = bound, - showFullyQualifiedName = bound.driOrNull?.classNames == typeAlias.dri.classNames - ) - } - - private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection( - p: Projection, showFullyQualifiedName: Boolean = false - ) { - return when (p) { - is TypeParameter -> { - if (p.presentableName != null) { - text(p.presentableName!!) - operator(": ") - } - annotationsInline(p) - link(p.name, p.dri) - } - is FunctionalTypeConstructor -> +funType(mainDRI.single(), mainSourcesetData, p) - is GenericTypeConstructor -> - group(styles = emptySet()) { - val linkText = if (showFullyQualifiedName && p.dri.packageName != null) { - "${p.dri.packageName}.${p.dri.classNames.orEmpty()}" - } else p.dri.classNames.orEmpty() - if (p.presentableName != null) { - text(p.presentableName!!) - operator(": ") - } - annotationsInline(p) - link(linkText, p.dri) - list(p.projections, prefix = "<", suffix = ">", - separatorStyles = mainStyles + TokenStyle.Punctuation, - surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { - signatureForProjection(it, showFullyQualifiedName) - } - } - - is Variance<*> -> group(styles = emptySet()) { - keyword("$p ".takeIf { it.isNotBlank() } ?: "") - signatureForProjection(p.inner, showFullyQualifiedName) - } - - is Star -> operator("*") - - is Nullable -> group(styles = emptySet()) { - signatureForProjection(p.inner, showFullyQualifiedName) - operator("?") - } - is DefinitelyNonNullable -> group(styles = emptySet()) { - signatureForProjection(p.inner, showFullyQualifiedName) - operator(" & ") - link("Any", DriOfAny) - } - - is TypeAliased -> signatureForProjection(p.typeAlias) - is JavaObject -> { - annotationsInline(p) - link("Any", DriOfAny) - } - is Void -> link("Unit", DriOfUnit) - is PrimitiveJavaType -> signatureForProjection(p.translateToKotlin(), showFullyQualifiedName) - is Dynamic -> text("dynamic") - is UnresolvedBound -> text(p.name) - } - } - - private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: FunctionalTypeConstructor) = - contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) { - - if (type.presentableName != null) { - text(type.presentableName!!) - operator(": ") - } - annotationsInline(type) - if (type.isSuspendable) keyword("suspend ") - - if (type.isExtensionFunction) { - signatureForProjection(type.projections.first()) - punctuation(".") - } - - val args = if (type.isExtensionFunction) - type.projections.drop(1) - else - type.projections - - punctuation("(") - args.subList(0, args.size - 1).forEachIndexed { i, arg -> - signatureForProjection(arg) - if (i < args.size - 2) punctuation(", ") - } - punctuation(")") - operator(" -> ") - signatureForProjection(args.last()) - } -} - -private fun PrimitiveJavaType.translateToKotlin() = GenericTypeConstructor( - dri = dri, - projections = emptyList(), - presentableName = null -) - -private val DTypeParameter.nontrivialBounds: List<Bound> - get() = bounds.filterNot { it is Nullable && it.inner.driOrNull == DriOfAny } diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt deleted file mode 100644 index f16fbeb0..00000000 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.signatures - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer -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 - -public object KotlinSignatureUtils : JvmSignatureUtils { - - private const val classExtension = "::class" - private val strategy = OnlyOnce - private val listBrackets = Pair('[', ']') - private val ignoredAnnotations = setOf( - /** - * Rendered separately, see [SinceKotlinTransformer] - */ - Annotations.Annotation(DRI("kotlin", "SinceKotlin"), emptyMap()), - - /** - * Rendered separately as its own block, see usage of [ContentKind.Deprecation] - */ - Annotations.Annotation(DRI("kotlin", "Deprecated"), emptyMap()), - Annotations.Annotation(DRI("kotlin", "DeprecatedSinceKotlin"), emptyMap()), - Annotations.Annotation(DRI("java.lang", "Deprecated"), emptyMap()), // could be used as well for interop - ) - - - override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: AnnotationTarget) { - annotationsBlockWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension) - } - - override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: AnnotationTarget) { - annotationsInlineWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension) - } - - override fun <T : Documentable> WithExtraProperties<T>.modifiers(): SourceSetDependent<Set<ExtraModifiers>> { - return extra[AdditionalModifiers]?.content?.entries?.associate { - it.key to it.value.filterIsInstance<ExtraModifiers.KotlinOnlyModifiers>().toSet() - } ?: emptyMap() - } - - - public val PrimitiveJavaType.dri: DRI get() = DRI("kotlin", name.capitalize()) - - public val Bound.driOrNull: DRI? - get() { - return when (this) { - is TypeParameter -> dri - is TypeConstructor -> dri - is Nullable -> inner.driOrNull - is DefinitelyNonNullable -> inner.driOrNull - is PrimitiveJavaType -> dri - is Void -> DriOfUnit - is JavaObject -> DriOfAny - is Dynamic -> null - is UnresolvedBound -> null - is TypeAliased -> typeAlias.driOrNull - } - } - - public val Projection.drisOfAllNestedBounds: List<DRI> get() = when (this) { - is TypeParameter -> listOf(dri) - is TypeConstructor -> listOf(dri) + projections.flatMap { it.drisOfAllNestedBounds } - is Nullable -> inner.drisOfAllNestedBounds - is DefinitelyNonNullable -> inner.drisOfAllNestedBounds - is PrimitiveJavaType -> listOf(dri) - is Void -> listOf(DriOfUnit) - is JavaObject -> listOf(DriOfAny) - is Dynamic -> emptyList() - is UnresolvedBound -> emptyList() - is Variance<*> -> inner.drisOfAllNestedBounds - is Star -> emptyList() - is TypeAliased -> listOfNotNull(typeAlias.driOrNull, inner.driOrNull) - } - -} diff --git a/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt deleted file mode 100644 index 76245a40..00000000 --- a/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.signatures - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.pages.ContentNode - -public fun interface SignatureProvider { - public fun signature(documentable: Documentable): List<ContentNode> -} diff --git a/plugins/base/src/main/kotlin/templating/AddToNavigationCommand.kt b/plugins/base/src/main/kotlin/templating/AddToNavigationCommand.kt deleted file mode 100644 index 03bf8e6a..00000000 --- a/plugins/base/src/main/kotlin/templating/AddToNavigationCommand.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -public class AddToNavigationCommand( - public val moduleName: String -) : Command diff --git a/plugins/base/src/main/kotlin/templating/AddToSearch.kt b/plugins/base/src/main/kotlin/templating/AddToSearch.kt deleted file mode 100644 index 8c2ccc79..00000000 --- a/plugins/base/src/main/kotlin/templating/AddToSearch.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -import org.jetbrains.dokka.base.renderers.html.SearchRecord - -public data class AddToSearch( - val moduleName: String, - val elements: List<SearchRecord> -): Command diff --git a/plugins/base/src/main/kotlin/templating/AddToSourcesetDependencies.kt b/plugins/base/src/main/kotlin/templating/AddToSourcesetDependencies.kt deleted file mode 100644 index c9774e30..00000000 --- a/plugins/base/src/main/kotlin/templating/AddToSourcesetDependencies.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -public data class AddToSourcesetDependencies( - val moduleName: String, - val content: Map<String, List<String>> -) : Command diff --git a/plugins/base/src/main/kotlin/templating/Command.kt b/plugins/base/src/main/kotlin/templating/Command.kt deleted file mode 100644 index 94ed00d4..00000000 --- a/plugins/base/src/main/kotlin/templating/Command.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS - -@JsonTypeInfo(use = CLASS) -public interface Command - -public abstract class SubstitutionCommand : Command { - public abstract val pattern: String -} diff --git a/plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt b/plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt deleted file mode 100644 index f1735490..00000000 --- a/plugins/base/src/main/kotlin/templating/ImmediateHtmlCommandConsumer.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -import org.jetbrains.dokka.base.renderers.html.TemplateBlock -import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer - -public interface ImmediateHtmlCommandConsumer { - public fun canProcess(command: Command): Boolean - - public fun <R> processCommand(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>) - - public fun <R> processCommandAndFinalize(command: Command, block: TemplateBlock, tagConsumer: ImmediateResolutionTagConsumer<R>): R -} - diff --git a/plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt b/plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt deleted file mode 100644 index b4316e0f..00000000 --- a/plugins/base/src/main/kotlin/templating/InsertTemplateExtra.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -import org.jetbrains.dokka.model.properties.ExtraProperty -import org.jetbrains.dokka.pages.ContentNode - -public data class InsertTemplateExtra(val command: Command) : ExtraProperty<ContentNode> { - - public companion object : ExtraProperty.Key<ContentNode, InsertTemplateExtra> - - override val key: ExtraProperty.Key<ContentNode, *> - get() = Companion -} diff --git a/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt b/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt deleted file mode 100644 index 070a38ee..00000000 --- a/plugins/base/src/main/kotlin/templating/PathToRootSubstitutionCommand.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -public data class PathToRootSubstitutionCommand( - override val pattern: String, - val default: String -): SubstitutionCommand() diff --git a/plugins/base/src/main/kotlin/templating/ProjectNameSubstitutionCommand.kt b/plugins/base/src/main/kotlin/templating/ProjectNameSubstitutionCommand.kt deleted file mode 100644 index 6218530e..00000000 --- a/plugins/base/src/main/kotlin/templating/ProjectNameSubstitutionCommand.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -public data class ProjectNameSubstitutionCommand( - override val pattern: String, - val default: String -): SubstitutionCommand() diff --git a/plugins/base/src/main/kotlin/templating/ReplaceVersionsCommand.kt b/plugins/base/src/main/kotlin/templating/ReplaceVersionsCommand.kt deleted file mode 100644 index 62a51047..00000000 --- a/plugins/base/src/main/kotlin/templating/ReplaceVersionsCommand.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -public data class ReplaceVersionsCommand(val location: String = ""): Command diff --git a/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt b/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt deleted file mode 100644 index 1669b435..00000000 --- a/plugins/base/src/main/kotlin/templating/ResolveLinkCommand.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -import org.jetbrains.dokka.links.DRI - -public class ResolveLinkCommand( - public val dri: DRI -): Command diff --git a/plugins/base/src/main/kotlin/templating/jsonMapperForPlugins.kt b/plugins/base/src/main/kotlin/templating/jsonMapperForPlugins.kt deleted file mode 100644 index a679a23d..00000000 --- a/plugins/base/src/main/kotlin/templating/jsonMapperForPlugins.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.templating - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.SerializerProvider -import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer -import com.fasterxml.jackson.databind.type.TypeFactory -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.jacksonTypeRef -import org.jetbrains.dokka.base.DokkaBase -import java.io.File - -// TODO [beresnev] try to get rid of this copy-paste in #2933 -// THIS IS COPIED FROM BASE SINCE IT NEEDS TO BE INSTANTIATED ON THE SAME CLASS LOADER AS PLUGINS - -private val objectMapper = run { - val module = SimpleModule().apply { - addSerializer(FileSerializer) - } - jacksonObjectMapper() - .apply { - typeFactory = PluginTypeFactory() - } - .registerModule(module) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) -} - -@PublishedApi -internal class TypeReference<T> @PublishedApi internal constructor( - internal val jackson: com.fasterxml.jackson.core.type.TypeReference<T> -) { - companion object { - @PublishedApi - internal inline operator fun <reified T> invoke(): TypeReference<T> = TypeReference(jacksonTypeRef()) - } -} - -public fun toJsonString(value: Any): String = objectMapper.writeValueAsString(value) - -public inline fun <reified T : Any> parseJson(json: String): T = parseJson(json, TypeReference()) - -@PublishedApi -internal fun <T : Any> parseJson(json: String, typeReference: TypeReference<T>): T = - objectMapper.readValue(json, typeReference.jackson) - - -private object FileSerializer : StdScalarSerializer<File>(File::class.java) { - override fun serialize(value: File, g: JsonGenerator, provider: SerializerProvider) { - g.writeString(value.path) - } -} - -@Suppress("DEPRECATION") // for TypeFactory constructor, no way to use non-deprecated one, it's essentially identical -private class PluginTypeFactory: TypeFactory(null) { - override fun findClass(className: String): Class<out Any>? = - Class.forName(className, true, DokkaBase::class.java.classLoader) ?: super.findClass(className) -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt b/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt deleted file mode 100644 index dde1a2af..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer - -/** - * Since we can not merge [DClasslike] with [DTypeAlias.underlyingType] and [DTypeAlias.extra], - * we have this transformer to add [ActualTypealias] extra in expect [DClasslike] - * - * The transformer should be applied after merging all documentables - */ -// TODO assign actual [DTypeAlias.expectPresentInSet] an expect source set, currently, [DTypeAlias.expectPresentInSet] always = null -public class ActualTypealiasAdder : DocumentableTransformer { - - override fun invoke(original: DModule, context: DokkaContext): DModule { - return original.generateTypealiasesMap().let { aliases -> - original.copy(packages = original.packages.map { - it.copy(classlikes = addActualTypeAliasToClasslikes(it.classlikes, aliases)) - }) - } - } - - private fun DModule.generateTypealiasesMap(): Map<DRI, DTypeAlias> = - packages.flatMap { pkg -> - pkg.typealiases.map { typeAlias -> - typeAlias.dri to typeAlias - } - }.toMap() - - - private fun addActualTypeAliasToClasslikes( - elements: Iterable<DClasslike>, - typealiases: Map<DRI, DTypeAlias> - ): List<DClasslike> = elements.flatMap { - when (it) { - is DClass -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DEnum -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DInterface -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DObject -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - is DAnnotation -> addActualTypeAlias( - it.copy( - classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) - ).let(::listOf), - typealiases - ) - else -> throw IllegalStateException("${it::class.qualifiedName} ${it.name} cannot have extra added") - } - } - - private fun <T> addActualTypeAlias( - elements: Iterable<T>, - typealiases: Map<DRI, DTypeAlias> - ): List<T> where T : DClasslike, T : WithExtraProperties<T>, T : WithSources = - elements.map { element -> - if (element.expectPresentInSet != null) { - typealiases[element.dri]?.let { ta -> - val actualTypealiasExtra = ActualTypealias(ta.copy(expectPresentInSet = element.expectPresentInSet)) - val merged = element.withNewExtras(element.extra + actualTypealiasExtra).let { - when (it) { - is DClass -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DEnum -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DInterface -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DObject -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - is DAnnotation -> it.copy( - documentation = element.documentation + ta.documentation, - sources = element.sources + ta.sources, - sourceSets = element.sourceSets + ta.sourceSets - ) - - else -> throw IllegalStateException("${it::class.qualifiedName} ${it.name} cannot have copy its sourceSets") - } - } - @Suppress("UNCHECKED_CAST") - merged as T - } ?: element - } else { - element - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt b/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt deleted file mode 100644 index e9c7342e..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ClashingDriIdentifier.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -@Deprecated( - message = "Declaration was moved to dokka-core", - replaceWith = ReplaceWith("org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier"), - level = DeprecationLevel.WARNING // TODO change to error after Kotlin 1.9.20 -) -public typealias ClashingDriIdentifier = org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt deleted file mode 100644 index 4905e876..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -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 -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.perPackageOptions -import org.jetbrains.dokka.transformers.documentation.sourceSet - -/** - * If [PackageOptions.skipDeprecated] or [DokkaConfiguration.DokkaSourceSet.skipDeprecated] is set - * to `true`, suppresses documentables marked with [kotlin.Deprecated] or [java.lang.Deprecated]. - * Package options are given preference over global options. - * - * Documentables with [kotlin.Deprecated.level] set to [DeprecationLevel.HIDDEN] - * are suppressed regardless of global and package options. - */ -public class DeprecatedDocumentableFilterTransformer( - context: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(context) { - - override fun shouldBeSuppressed(d: Documentable): Boolean { - val annotations = (d as? WithExtraProperties<*>)?.annotations() ?: return false - if (annotations.isEmpty()) - return false - - val deprecatedAnnotations = filterDeprecatedAnnotations(annotations) - if (deprecatedAnnotations.isEmpty()) - return false - - val kotlinDeprecated = deprecatedAnnotations.find { it.dri.packageName == "kotlin" } - if (kotlinDeprecated?.isHidden() == true) - return true - - return perPackageOptions(d)?.skipDeprecated ?: sourceSet(d).skipDeprecated - } - - private fun WithExtraProperties<*>.annotations(): List<Annotations.Annotation> { - return this.extra.allOfType<Annotations>().flatMap { annotations -> - annotations.directAnnotations.values.singleOrNull() ?: emptyList() - } - } - - private fun filterDeprecatedAnnotations(annotations: List<Annotations.Annotation>): List<Annotations.Annotation> { - return annotations.filter { - (it.dri.packageName == "kotlin" && it.dri.classNames == "Deprecated") || - (it.dri.packageName == "java.lang" && it.dri.classNames == "Deprecated") - } - } - - private fun Annotations.Annotation.isHidden(): Boolean { - val level = (this.params["level"] as? EnumValue) ?: return false - return level.enumName == "DeprecationLevel.HIDDEN" - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt deleted file mode 100644 index 10b25a20..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableReplacerTransformer.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public abstract class DocumentableReplacerTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> = - modules.map { module -> - val (documentable, wasChanged) = processModule(module) - documentable.takeIf { wasChanged } ?: module - } - - protected open fun processModule(module: DModule): AnyWithChanges<DModule> { - val afterProcessing = module.packages.map { processPackage(it) } - val processedModule = module.takeIf { afterProcessing.none { it.changed } } - ?: module.copy(packages = afterProcessing.mapNotNull { it.target }) - return AnyWithChanges(processedModule, afterProcessing.any { it.changed }) - } - - protected open fun processPackage(dPackage: DPackage): AnyWithChanges<DPackage> { - val classlikes = dPackage.classlikes.map { processClassLike(it) } - val typeAliases = dPackage.typealiases.map { processTypeAlias(it) } - val functions = dPackage.functions.map { processFunction(it) } - val properies = dPackage.properties.map { processProperty(it) } - - val wasChanged = (classlikes + typeAliases + functions + properies).any { it.changed } - return (dPackage.takeIf { !wasChanged } ?: dPackage.copy( - classlikes = classlikes.mapNotNull { it.target }, - typealiases = typeAliases.mapNotNull { it.target }, - functions = functions.mapNotNull { it.target }, - properties = properies.mapNotNull { it.target } - )).let { processedPackage -> AnyWithChanges(processedPackage, wasChanged) } - } - - protected open fun processClassLike(classlike: DClasslike): AnyWithChanges<DClasslike> { - val functions = classlike.functions.map { processFunction(it) } - val classlikes = classlike.classlikes.map { processClassLike(it) } - val properties = classlike.properties.map { processProperty(it) } - val companion = (classlike as? WithCompanion)?.companion?.let { processClassLike(it) } - - val wasClasslikeChanged = (functions + classlikes + properties).any { it.changed } || companion?.changed == true - return when (classlike) { - is DClass -> { - val constructors = classlike.constructors.map { processFunction(it) } - val generics = classlike.generics.map { processTypeParameter(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } || generics.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - constructors = constructors.mapNotNull { it.target }, - generics = generics.mapNotNull { it.target }, - companion = companion?.target as? DObject - )).let { AnyWithChanges(it, wasClassChange) } - } - is DInterface -> { - val generics = classlike.generics.map { processTypeParameter(it) } - val wasInterfaceChange = wasClasslikeChanged || generics.any { it.changed } - (classlike.takeIf { !wasInterfaceChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - generics = generics.mapNotNull { it.target }, - companion = companion?.target as? DObject - )).let { AnyWithChanges(it, wasClasslikeChanged) } - } - is DObject -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - )).let { AnyWithChanges(it, wasClasslikeChanged) } - is DAnnotation -> { - val constructors = classlike.constructors.map { processFunction(it) } - val generics = classlike.generics.map { processTypeParameter(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } || generics.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - constructors = constructors.mapNotNull { it.target }, - generics = generics.mapNotNull { it.target }, - companion = companion?.target as? DObject - )).let { AnyWithChanges(it, wasClassChange) } - } - is DEnum -> { - val constructors = classlike.constructors.map { processFunction(it) } - val entries = classlike.entries.map { processEnumEntry(it) } - val wasClassChange = - wasClasslikeChanged || (constructors + entries).any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - constructors = constructors.mapNotNull { it.target }, - companion = companion?.target as? DObject, - entries = entries.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasClassChange) } - } - } - } - - protected open fun processEnumEntry(dEnumEntry: DEnumEntry): AnyWithChanges<DEnumEntry> { - val functions = dEnumEntry.functions.map { processFunction(it) } - val properties = dEnumEntry.properties.map { processProperty(it) } - val classlikes = dEnumEntry.classlikes.map { processClassLike(it) } - - val wasChanged = (functions + properties + classlikes).any { it.changed } - return (dEnumEntry.takeIf { !wasChanged } ?: dEnumEntry.copy( - functions = functions.mapNotNull { it.target }, - classlikes = classlikes.mapNotNull { it.target }, - properties = properties.mapNotNull { it.target }, - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processFunction(dFunction: DFunction): AnyWithChanges<DFunction> { - val type = processBound(dFunction.type) - val parameters = dFunction.parameters.map { processParameter(it) } - val receiver = dFunction.receiver?.let { processParameter(it) } - val generics = dFunction.generics.map { processTypeParameter(it) } - - val wasChanged = parameters.any { it.changed } || receiver?.changed == true - || type.changed || generics.any { it.changed } - return (dFunction.takeIf { !wasChanged } ?: dFunction.copy( - type = type.target ?: dFunction.type, - parameters = parameters.mapNotNull { it.target }, - receiver = receiver?.target, - generics = generics.mapNotNull { it.target }, - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processProperty(dProperty: DProperty): AnyWithChanges<DProperty> { - val getter = dProperty.getter?.let { processFunction(it) } - val setter = dProperty.setter?.let { processFunction(it) } - val type = processBound(dProperty.type) - val generics = dProperty.generics.map { processTypeParameter(it) } - - val wasChanged = getter?.changed == true || setter?.changed == true - || type.changed || generics.any { it.changed } - return (dProperty.takeIf { !wasChanged } ?: dProperty.copy( - type = type.target ?: dProperty.type, - setter = setter?.target, - getter = getter?.target, - generics = generics.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processParameter(dParameter: DParameter): AnyWithChanges<DParameter> { - val type = processBound(dParameter.type) - - val wasChanged = type.changed - return (dParameter.takeIf { !wasChanged } ?: dParameter.copy( - type = type.target ?: dParameter.type, - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processTypeParameter(dTypeParameter: DTypeParameter): AnyWithChanges<DTypeParameter> { - val bounds = dTypeParameter.bounds.map { processBound(it) } - - val wasChanged = bounds.any { it.changed } - return (dTypeParameter.takeIf { !wasChanged } ?: dTypeParameter.copy( - bounds = bounds.mapIndexed { i, v -> v.target ?: dTypeParameter.bounds[i] } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processBound(bound: Bound): AnyWithChanges<Bound> { - return when(bound) { - is GenericTypeConstructor -> processGenericTypeConstructor(bound) - is FunctionalTypeConstructor -> processFunctionalTypeConstructor(bound) - else -> AnyWithChanges(bound, false) - } - } - - protected open fun processVariance(variance: Variance<*>): AnyWithChanges<Variance<*>> { - val bound = processBound(variance.inner) - if (!bound.changed) - return AnyWithChanges(variance, false) - return when (variance) { - is Covariance<*> -> AnyWithChanges( - Covariance(bound.target ?: variance.inner), true) - is Contravariance<*> -> AnyWithChanges( - Contravariance(bound.target ?: variance.inner), true) - is Invariance<*> -> AnyWithChanges( - Invariance(bound.target ?: variance.inner), true) - else -> AnyWithChanges(variance, false) - } - } - - protected open fun processProjection(projection: Projection): AnyWithChanges<Projection> = - when (projection) { - is Bound -> processBound(projection) - is Variance<Bound> -> processVariance(projection) - else -> AnyWithChanges(projection, false) - } - - protected open fun processGenericTypeConstructor( - genericTypeConstructor: GenericTypeConstructor - ): AnyWithChanges<GenericTypeConstructor> { - val projections = genericTypeConstructor.projections.map { processProjection(it) } - - val wasChanged = projections.any { it.changed } - return (genericTypeConstructor.takeIf { !wasChanged } ?: genericTypeConstructor.copy( - projections = projections.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processFunctionalTypeConstructor( - functionalTypeConstructor: FunctionalTypeConstructor - ): AnyWithChanges<FunctionalTypeConstructor> { - val projections = functionalTypeConstructor.projections.map { processProjection(it) } - - val wasChanged = projections.any { it.changed } - return (functionalTypeConstructor.takeIf { !wasChanged } ?: functionalTypeConstructor.copy( - projections = projections.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - protected open fun processTypeAlias(dTypeAlias: DTypeAlias): AnyWithChanges<DTypeAlias> { - val underlyingType = dTypeAlias.underlyingType.mapValues { processBound(it.value) } - val generics = dTypeAlias.generics.map { processTypeParameter(it) } - - val wasChanged = underlyingType.any { it.value.changed } || generics.any { it.changed } - return (dTypeAlias.takeIf { !wasChanged } ?: dTypeAlias.copy( - underlyingType = underlyingType.mapValues { it.value.target ?: dTypeAlias.underlyingType.getValue(it.key) }, - generics = generics.mapNotNull { it.target } - )).let { AnyWithChanges(it, wasChanged) } - } - - - protected data class AnyWithChanges<out T>(val target: T?, val changed: Boolean = false) -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt deleted file mode 100644 index 6155a71f..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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 - -public class DocumentableVisibilityFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.map { original -> - val sourceSet = original.sourceSets.single() - val packageOptions = sourceSet.perPackageOptions - DocumentableVisibilityFilter(packageOptions, sourceSet).processModule(original) - } - } - - private class DocumentableVisibilityFilter( - val packageOptions: List<DokkaConfiguration.PackageOptions>, - val globalOptions: DokkaSourceSet - ) { - fun Visibility.isAllowedInPackage(packageName: String?) = when (this) { - is JavaVisibility.Public, - is KotlinVisibility.Public -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PUBLIC) - is JavaVisibility.Private, - is KotlinVisibility.Private -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PRIVATE) - is JavaVisibility.Protected, - is KotlinVisibility.Protected -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PROTECTED) - is KotlinVisibility.Internal -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.INTERNAL) - is JavaVisibility.Default -> isAllowedInPackage(packageName, DokkaConfiguration.Visibility.PACKAGE) - } - - private fun isAllowedInPackage(packageName: String?, visibility: DokkaConfiguration.Visibility): Boolean { - val packageOpts = packageName.takeIf { it != null }?.let { name -> - packageOptions.firstOrNull { Regex(it.matchingRegex).matches(name) } - } - - val (documentedVisibilities, includeNonPublic) = - @Suppress("DEPRECATION") // for includeNonPublic, preserve backwards compatibility - when { - packageOpts != null -> packageOpts.documentedVisibilities to packageOpts.includeNonPublic - else -> globalOptions.documentedVisibilities to globalOptions.includeNonPublic - } - - // if `documentedVisibilities` is explicitly overridden by the user (i.e. not default value by reference), - // deprecated `includeNonPublic` should not be taken into account, so that only one setting prevails - val isDocumentedVisibilitiesOverridden = documentedVisibilities !== DokkaDefaults.documentedVisibilities - return documentedVisibilities.contains(visibility) || (!isDocumentedVisibilitiesOverridden && includeNonPublic) - } - - fun processModule(original: DModule) = - filterPackages(original.packages).let { (modified, packages) -> - if (!modified) original - else - DModule( - original.name, - packages = packages, - documentation = original.documentation, - sourceSets = original.sourceSets, - extra = original.extra - ) - } - - - private fun filterPackages(packages: List<DPackage>): Pair<Boolean, List<DPackage>> { - var packagesListChanged = false - val filteredPackages = packages.map { - var modified = false - val functions = filterFunctions(it.functions).let { (listModified, list) -> - modified = modified || listModified - list - } - val properties = filterProperties(it.properties).let { (listModified, list) -> - modified = modified || listModified - list - } - val classlikes = filterClasslikes(it.classlikes).let { (listModified, list) -> - modified = modified || listModified - list - } - val typeAliases = filterTypeAliases(it.typealiases).let { (listModified, list) -> - modified = modified || listModified - list - } - when { - !modified -> it - else -> { - packagesListChanged = true - DPackage( - it.dri, - functions, - properties, - classlikes, - typeAliases, - it.documentation, - it.expectPresentInSet, - it.sourceSets, - it.extra - ) - } - } - } - return Pair(packagesListChanged, filteredPackages) - } - - @Suppress("UNUSED_PARAMETER") - private fun <T : WithVisibility> alwaysTrue(a: T, p: DokkaSourceSet) = true - @Suppress("UNUSED_PARAMETER") - private fun <T : WithVisibility> alwaysFalse(a: T, p: DokkaSourceSet) = false - @Suppress("UNUSED_PARAMETER") - private fun <T> alwaysNoModify(a: T, sourceSets: Set<DokkaSourceSet>) = false to a - - private fun WithVisibility.visibilityForPlatform(data: DokkaSourceSet): Visibility? = visibility[data] - - private fun <T> T.filterPlatforms( - additionalCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysTrue, - alternativeCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysFalse - ) where T : Documentable, T : WithVisibility = - sourceSets.filter { d -> - visibilityForPlatform(d)?.isAllowedInPackage(dri.packageName) == true && - additionalCondition(this, d) || - alternativeCondition(this, d) - }.toSet() - - private fun <T> List<T>.transform( - additionalCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysTrue, - alternativeCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysFalse, - modify: (T, Set<DokkaSourceSet>) -> Pair<Boolean, T> = ::alwaysNoModify, - recreate: (T, Set<DokkaSourceSet>) -> T, - ): Pair<Boolean, List<T>> where T : Documentable, T : WithVisibility { - var changed = false - val values = mapNotNull { t -> - val filteredPlatforms = t.filterPlatforms(additionalCondition, alternativeCondition) - when (filteredPlatforms.size) { - t.visibility.size -> { - val (wasChanged, element) = modify(t, filteredPlatforms) - changed = changed || wasChanged - element - } - 0 -> { - changed = true - null - } - else -> { - changed = true - recreate(t, filteredPlatforms) - } - } - } - return Pair(changed, values) - } - - private fun filterFunctions( - functions: List<DFunction>, - additionalCondition: (DFunction, DokkaSourceSet) -> Boolean = ::alwaysTrue - ) = - functions.transform(additionalCondition) { original, filteredPlatforms -> - with(original) { - copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - generics = generics.mapNotNull { it.filter(filteredPlatforms) }, - sourceSets = filteredPlatforms, - ) - } - } - - private fun hasVisibleAccessorsForPlatform(property: DProperty, data: DokkaSourceSet) = - property.getter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true || - property.setter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true - - private fun filterProperties( - properties: List<DProperty>, - additionalCondition: (DProperty, DokkaSourceSet) -> Boolean = ::alwaysTrue, - additionalConditionAccessors: (DFunction, DokkaSourceSet) -> Boolean = ::alwaysTrue - ): Pair<Boolean, List<DProperty>> { - - val modifier: (DProperty, Set<DokkaSourceSet>) -> Pair<Boolean, DProperty> = - { original, _ -> - val setter = original.setter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - val getter = original.getter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - - val modified = setter?.first == true || getter?.first == true - - val property = - if (modified) - original.copy( - setter = setter?.second?.firstOrNull(), - getter = getter?.second?.firstOrNull() - ) - else original - modified to property - } - - return properties.transform( - additionalCondition, - ::hasVisibleAccessorsForPlatform, - modifier - ) { original, filteredPlatforms -> - val setter = original.setter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - val getter = original.getter?.let { filterFunctions(listOf(it), additionalConditionAccessors) } - - with(original) { - copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - sourceSets = filteredPlatforms, - generics = generics.mapNotNull { it.filter(filteredPlatforms) }, - setter = setter?.second?.firstOrNull(), - getter = getter?.second?.firstOrNull() - ) - } - } - } - - private fun filterEnumEntries(entries: List<DEnumEntry>, filteredPlatforms: Set<DokkaSourceSet>): Pair<Boolean, List<DEnumEntry>> = - entries.fold(Pair(false, emptyList())) { acc, entry -> - val intersection = filteredPlatforms.intersect(entry.sourceSets) - if (intersection.isEmpty()) Pair(true, acc.second) - else { - val functions = filterFunctions(entry.functions) { _, data -> data in intersection } - val properties = filterProperties(entry.properties) { _, data -> data in intersection } - val classlikes = filterClasslikes(entry.classlikes) { _, data -> data in intersection } - - DEnumEntry( - entry.dri, - entry.name, - entry.documentation.filtered(intersection), - entry.expectPresentInSet.filtered(filteredPlatforms), - functions.second, - properties.second, - classlikes.second, - intersection, - entry.extra - ).let { Pair(functions.first || properties.first || classlikes.first, acc.second + it) } - } - } - - private fun filterClasslikes( - classlikeList: List<DClasslike>, - additionalCondition: (DClasslike, DokkaSourceSet) -> Boolean = ::alwaysTrue - ): Pair<Boolean, List<DClasslike>> { - var classlikesListChanged = false - val filteredClasslikes: List<DClasslike> = classlikeList.mapNotNull { - with(it) { - val filteredPlatforms = filterPlatforms(additionalCondition) - if (filteredPlatforms.isEmpty()) { - classlikesListChanged = true - null - } else { - var modified = sourceSets.size != filteredPlatforms.size - val functions = - filterFunctions(functions) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } - val properties = - filterProperties(properties) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } - val classlikes = - filterClasslikes(classlikes) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } - val companion = - if (this is WithCompanion) filterClasslikes(listOfNotNull(companion)) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list.firstOrNull() as DObject? - } else null - val constructors = if (this is WithConstructors) - filterFunctions(constructors) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> - modified = modified || listModified - list - } else emptyList() - val generics = - if (this is WithGenerics) generics.mapNotNull { param -> param.filter(filteredPlatforms) } else emptyList() - val enumEntries = - if (this is DEnum) filterEnumEntries(entries, filteredPlatforms).let { (listModified, list) -> - modified = modified || listModified - list - } else emptyList() - classlikesListChanged = classlikesListChanged || modified - when { - !modified -> this - this is DClass -> copy( - constructors = constructors, - functions = functions, - properties = properties, - classlikes = classlikes, - sources = sources.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - generics = generics, - supertypes = supertypes.filtered(filteredPlatforms), - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - this is DAnnotation -> copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - constructors = constructors, - generics = generics, - sourceSets = filteredPlatforms - ) - this is DEnum -> copy( - entries = enumEntries, - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - constructors = constructors, - supertypes = supertypes.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - this is DInterface -> copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - visibility = visibility.filtered(filteredPlatforms), - companion = companion, - generics = generics, - supertypes = supertypes.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - this is DObject -> copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - sources = sources.filtered(filteredPlatforms), - functions = functions, - properties = properties, - classlikes = classlikes, - supertypes = supertypes.filtered(filteredPlatforms), - sourceSets = filteredPlatforms - ) - else -> null - } - } - } - } - return Pair(classlikesListChanged, filteredClasslikes) - } - - private fun filterTypeAliases( - typeAliases: List<DTypeAlias>, - additionalCondition: (DTypeAlias, DokkaSourceSet) -> Boolean = ::alwaysTrue - ) = - typeAliases.transform(additionalCondition) { original, filteredPlatforms -> - with(original) { - copy( - documentation = documentation.filtered(filteredPlatforms), - expectPresentInSet = expectPresentInSet.filtered(filteredPlatforms), - underlyingType = underlyingType.filtered(filteredPlatforms), - visibility = visibility.filtered(filteredPlatforms), - generics = generics.mapNotNull { it.filter(filteredPlatforms) }, - sourceSets = filteredPlatforms, - ) - } - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/EmptyModulesFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/EmptyModulesFilterTransformer.kt deleted file mode 100644 index 7a2387dc..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/EmptyModulesFilterTransformer.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public class EmptyModulesFilterTransformer : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.filter { it.children.isNotEmpty() } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt deleted file mode 100644 index 30ac8f70..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.DModule -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.dokka.transformers.documentation.sourceSet - -public class EmptyPackagesFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.mapNotNull(::filterModule) - } - - private fun filterModule(module: DModule): DModule? { - val nonEmptyPackages = module.packages.filterNot { pkg -> - sourceSet(pkg).skipEmptyPackages && pkg.children.isEmpty() - } - - return when { - nonEmptyPackages == module.packages -> module - nonEmptyPackages.isEmpty() -> null - else -> module.copy(packages = nonEmptyPackages) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt deleted file mode 100644 index e6102622..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import kotlinx.coroutines.* -import kotlinx.coroutines.channels.* -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.DriOfAny -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.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.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -public class ExtensionExtractorTransformer : DocumentableTransformer { - override fun invoke(original: DModule, context: DokkaContext): DModule = runBlocking(Dispatchers.Default) { - val classGraph = async { - if (!context.configuration.suppressInheritedMembers) - context.plugin<InternalKotlinAnalysisPlugin>().querySingle { fullClassHierarchyBuilder }.build(original) - else - emptyMap() - } - - val channel = Channel<Pair<DRI, Callable>>(10) - launch { - original.packages.parallelForEach { collectExtensions(it, channel) } - channel.close() - } - val extensionMap = channel.toList().toMultiMap() - - val newPackages = original.packages.parallelMap { it.addExtensionInformation(classGraph.await(), extensionMap) } - original.copy(packages = newPackages) - } - - private suspend fun <T : Documentable> T.addExtensionInformation( - classGraph: SourceSetDependent<Map<DRI, List<DRI>>>, - extensionMap: Map<DRI, List<Callable>> - ): T = coroutineScope { - val newClasslikes = (this@addExtensionInformation as? WithScope) - ?.classlikes - ?.map { async { it.addExtensionInformation(classGraph, extensionMap) } } - .orEmpty() - - @Suppress("UNCHECKED_CAST") - when (this@addExtensionInformation) { - is DPackage -> { - val newTypealiases = typealiases.map { async { it.addExtensionInformation(classGraph, extensionMap) } } - copy(classlikes = newClasslikes.awaitAll(), typealiases = newTypealiases.awaitAll()) - } - - is DClass -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DEnum -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DInterface -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DObject -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DAnnotation -> copy( - classlikes = newClasslikes.awaitAll(), - extra = extra + findExtensions(classGraph, extensionMap) - ) - - is DTypeAlias -> copy(extra = extra + findExtensions(classGraph, extensionMap)) - else -> throw IllegalStateException( - "${this@addExtensionInformation::class.simpleName} is not expected to have extensions" - ) - } as T - } - - private suspend fun collectExtensions( - documentable: Documentable, - channel: SendChannel<Pair<DRI, Callable>> - ): Unit = coroutineScope { - if (documentable is WithScope) { - documentable.classlikes.forEach { - launch { collectExtensions(it, channel) } - } - - if (documentable is DObject || documentable is DPackage) { - (documentable.properties.asSequence() + documentable.functions.asSequence()) - .flatMap { it.asPairsWithReceiverDRIs() } - .forEach { channel.send(it) } - } - } - } - - private fun <T : Documentable> T.findExtensions( - classGraph: SourceSetDependent<Map<DRI, List<DRI>>>, - extensionMap: Map<DRI, List<Callable>> - ): CallableExtensions? { - val resultSet = mutableSetOf<Callable>() - - fun collectFrom(element: DRI) { - extensionMap[element]?.let { resultSet.addAll(it) } - sourceSets.forEach { sourceSet -> classGraph[sourceSet]?.get(element)?.forEach { collectFrom(it) } } - } - collectFrom(dri) - - return if (resultSet.isEmpty()) null else CallableExtensions(resultSet) - } - - private fun Callable.asPairsWithReceiverDRIs(): Sequence<Pair<DRI, Callable>> = - receiver?.type?.let { findReceiverDRIs(it) }.orEmpty().map { it to this } - - // In normal cases we return at max one DRI, but sometimes receiver type can be bound by more than one type constructor - // for example `fun <T> T.example() where T: A, T: B` is extension of both types A and B - // another one `typealias A = B` - // Note: in some cases returning empty sequence doesn't mean that we cannot determine the DRI but only that we don't - // care about it since there is nowhere to put documentation of given extension. - private fun Callable.findReceiverDRIs(bound: Bound): Sequence<DRI> = when (bound) { - is Nullable -> findReceiverDRIs(bound.inner) - is DefinitelyNonNullable -> findReceiverDRIs(bound.inner) - is TypeParameter -> - if (this is DFunction && bound.dri == this.dri) - generics.find { it.name == bound.name }?.bounds?.asSequence()?.flatMap { findReceiverDRIs(it) }.orEmpty() - else - emptySequence() - - is TypeConstructor -> sequenceOf(bound.dri) - is PrimitiveJavaType -> emptySequence() - is Void -> emptySequence() - is JavaObject -> sequenceOf(DriOfAny) - is Dynamic -> sequenceOf(DriOfAny) - is UnresolvedBound -> emptySequence() - is TypeAliased -> findReceiverDRIs(bound.typeAlias) + findReceiverDRIs(bound.inner) - } - - private fun <T, U> Iterable<Pair<T, U>>.toMultiMap(): Map<T, List<U>> = - groupBy(Pair<T, *>::first, Pair<*, U>::second) -} - -public data class CallableExtensions(val extensions: Set<Callable>) : ExtraProperty<Documentable> { - public companion object Key : ExtraProperty.Key<Documentable, CallableExtensions> { - override fun mergeStrategyFor(left: CallableExtensions, right: CallableExtensions): MergeStrategy<Documentable> = - MergeStrategy.Replace(CallableExtensions(left.extensions + right.extensions)) - } - - override val key: Key = Key -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt deleted file mode 100644 index d9b7053a..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/InheritedEntriesDocumentableFilterTransformer.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -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 - -public class InheritedEntriesDocumentableFilterTransformer( - context: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(context) { - - override fun shouldBeSuppressed(d: Documentable): Boolean { - @Suppress("UNCHECKED_CAST") - val inheritedMember = (d as? WithExtraProperties<Documentable>)?.extra?.get(InheritedMember) - val containsInheritedFrom = inheritedMember?.inheritedFrom?.any { entry -> entry.value != null } ?: false - - return context.configuration.suppressInheritedMembers && containsInheritedFrom - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt deleted file mode 100644 index 2c7d6b89..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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.transformers.documentation.DocumentableTransformer - -public class InheritorsExtractorTransformer : DocumentableTransformer { - override fun invoke(original: DModule, context: DokkaContext): DModule = - original.generateInheritanceMap().let { inheritanceMap -> original.appendInheritors(inheritanceMap) as DModule } - - private fun <T : Documentable> T.appendInheritors(inheritanceMap: Map<DokkaSourceSet, Map<DRI, List<DRI>>>): Documentable = - InheritorsInfo(inheritanceMap.getForDRI(dri)).let { info -> - when (this) { - is DModule -> copy(packages = packages.map { it.appendInheritors(inheritanceMap) as DPackage }) - is DPackage -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - is DClass -> if (info.isNotEmpty()) { - copy( - extra = extra + info, - classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } else { - copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } - is DEnum -> if (info.isNotEmpty()) { - copy( - extra = extra + info, - classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } else { - copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } - is DInterface -> if (info.isNotEmpty()) { - copy( - extra = extra + info, - classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } else { - copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - } - is DObject -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - is DAnnotation -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) - else -> this - } - } - - private fun InheritorsInfo.isNotEmpty() = this.value.values.fold(0) { acc, list -> acc + list.size } > 0 - - private fun Map<DokkaSourceSet, Map<DRI, List<DRI>>>.getForDRI(dri: DRI) = - map { (v, k) -> - v to k[dri] - }.associate { (k, v) -> k to v.orEmpty() } - - private fun DModule.generateInheritanceMap() = - getInheritanceEntriesRec().filterNot { it.second.isEmpty() }.groupBy({ it.first }) { it.second } - .map { (k, v) -> - k to v.flatMap { p -> p.groupBy({ it.first }) { it.second }.toList() } - .groupBy({ it.first }) { it.second }.map { (k2, v2) -> k2 to v2.flatten() }.toMap() - }.filter { it.second.values.isNotEmpty() }.toMap() - - private fun <T : Documentable> T.getInheritanceEntriesRec(): List<Pair<DokkaSourceSet, List<Pair<DRI, DRI>>>> = - this.toInheritanceEntries() + children.flatMap { it.getInheritanceEntriesRec() } - - private fun <T : Documentable> T.toInheritanceEntries() = - (this as? WithSupertypes)?.let { - it.supertypes.map { (k, v) -> k to v.map { it.typeConstructor.dri to dri } } - }.orEmpty() - -} - -public class InheritorsInfo( - public val value: SourceSetDependent<List<DRI>> -) : ExtraProperty<Documentable> { - public companion object : ExtraProperty.Key<Documentable, InheritorsInfo> { - override fun mergeStrategyFor(left: InheritorsInfo, right: InheritorsInfo): MergeStrategy<Documentable> = - MergeStrategy.Replace( - InheritorsInfo( - (left.value.entries.toList() + right.value.entries.toList()) - .groupBy({ it.key }) { it.value } - .map { (k, v) -> k to v.flatten() }.toMap() - ) - ) - } - - override val key: ExtraProperty.Key<Documentable, *> = InheritorsInfo -} - diff --git a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt deleted file mode 100644 index 7a360cb8..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/KotlinArrayDocumentableReplacerTransformer.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext - -public class KotlinArrayDocumentableReplacerTransformer( - context: DokkaContext -): DocumentableReplacerTransformer(context) { - - private fun Documentable.isJVM() = - sourceSets.any{ it.analysisPlatform == Platform.jvm } - - override fun processGenericTypeConstructor(genericTypeConstructor: GenericTypeConstructor): AnyWithChanges<GenericTypeConstructor> = - genericTypeConstructor.takeIf { genericTypeConstructor.dri == DRI("kotlin", "Array") } - ?.let { - with(it.projections.firstOrNull() as? Variance<Bound>) { - with(this?.inner as? GenericTypeConstructor) { - when (this?.dri) { - DRI("kotlin", "Int") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "IntArray"), emptyList()), - true) - DRI("kotlin", "Boolean") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "BooleanArray"), emptyList()), - true) - DRI("kotlin", "Float") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "FloatArray"), emptyList()), - true) - DRI("kotlin", "Double") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "DoubleArray"), emptyList()), - true) - DRI("kotlin", "Long") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "LongArray"), emptyList()), - true) - DRI("kotlin", "Short") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "ShortArray"), emptyList()), - true) - DRI("kotlin", "Char") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "CharArray"), emptyList()), - true) - DRI("kotlin", "Byte") -> - AnyWithChanges( - GenericTypeConstructor(DRI("kotlin", "ByteArray"), emptyList()), - true) - else -> null - } - } - } - } - ?: super.processGenericTypeConstructor(genericTypeConstructor) - - override fun processModule(module: DModule): AnyWithChanges<DModule> = - if(module.isJVM()) - super.processModule(module) - else AnyWithChanges(module) -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt deleted file mode 100644 index c19bc15e..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -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.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin -import org.jetbrains.dokka.analysis.kotlin.internal.ModuleAndPackageDocumentationReader - -internal class ModuleAndPackageDocumentationTransformer( - private val moduleAndPackageDocumentationReader: ModuleAndPackageDocumentationReader -) : PreMergeDocumentableTransformer { - - 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.read(module), - packages = module.packages.map { pkg -> - pkg.copy( - documentation = pkg.documentation + moduleAndPackageDocumentationReader.read(pkg) - ) - } - ) - } - } - - private operator fun SourceSetDependent<DocumentationNode>.plus( - other: SourceSetDependent<DocumentationNode> - ): Map<DokkaSourceSet, DocumentationNode> = - (asSequence() + other.asSequence()) - .distinct() - .groupBy({ it.key }, { it.value }) - .mapValues { (_, values) -> DocumentationNode(values.flatMap { it.children }) } - -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer.kt deleted file mode 100644 index 09c6ac87..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ObviousFunctionsDocumentableFilterTransformer.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.DFunction -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.ObviousMember -import org.jetbrains.dokka.plugability.DokkaContext - -public class ObviousFunctionsDocumentableFilterTransformer( - context: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(context) { - override fun shouldBeSuppressed(d: Documentable): Boolean = - context.configuration.suppressObviousFunctions && d is DFunction && d.extra[ObviousMember] != null -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt deleted file mode 100644 index 2b270f18..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -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.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -internal class ReportUndocumentedTransformer : DocumentableTransformer { - - override fun invoke(original: DModule, context: DokkaContext): DModule = original.apply { - withDescendants().forEach { documentable -> invoke(documentable, context) } - } - - private fun invoke(documentable: Documentable, context: DokkaContext) { - documentable.sourceSets.forEach { sourceSet -> - if (shouldBeReportedIfNotDocumented(documentable, sourceSet, context)) { - reportIfUndocumented(context, documentable, sourceSet) - } - } - } - - private fun shouldBeReportedIfNotDocumented( - documentable: Documentable, sourceSet: DokkaSourceSet, context: DokkaContext - ): Boolean { - val packageOptionsOrNull = packageOptionsOrNull(sourceSet, documentable) - - if (!(packageOptionsOrNull?.reportUndocumented ?: sourceSet.reportUndocumented)) { - return false - } - - if (documentable is DParameter || documentable is DPackage || documentable is DModule) { - return false - } - - if (isConstructor(documentable)) { - return false - } - - val syntheticDetector = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { syntheticDocumentableDetector } - if (syntheticDetector.isSynthetic(documentable, sourceSet)) { - return false - } - - if (isPrivateOrInternalApi(documentable, sourceSet)) { - return false - } - - return true - } - - private fun reportIfUndocumented( - context: DokkaContext, - documentable: Documentable, - sourceSet: DokkaSourceSet - ) { - if (isUndocumented(documentable, sourceSet)) { - val documentableDescription = with(documentable) { - buildString { - dri.packageName?.run { - append(this) - append("/") - } - - dri.classNames?.run { - append(this) - append("/") - } - - dri.callable?.run { - append(name) - append("/") - append(signature()) - append("/") - } - - val sourceSetName = sourceSet.displayName - if (sourceSetName != null.toString()) { - append(" ($sourceSetName)") - } - } - } - - context.logger.warn("Undocumented: $documentableDescription") - } - } - - private fun isUndocumented(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - fun resolveDependentSourceSets(sourceSet: DokkaSourceSet): List<DokkaSourceSet> { - return sourceSet.dependentSourceSets.mapNotNull { sourceSetID -> - documentable.sourceSets.singleOrNull { it.sourceSetID == sourceSetID } - } - } - - fun withAllDependentSourceSets(sourceSet: DokkaSourceSet): Sequence<DokkaSourceSet> = sequence { - yield(sourceSet) - for (dependentSourceSet in resolveDependentSourceSets(sourceSet)) { - yieldAll(withAllDependentSourceSets(dependentSourceSet)) - } - } - - - return withAllDependentSourceSets(sourceSet).all { sourceSetOrDependentSourceSet -> - documentable.documentation[sourceSetOrDependentSourceSet]?.children?.isEmpty() ?: true - } - } - - private fun isConstructor(documentable: Documentable): Boolean { - if (documentable !is DFunction) return false - return documentable.isConstructor - } - - private fun isPrivateOrInternalApi(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { - return when ((documentable as? WithVisibility)?.visibility?.get(sourceSet)) { - KotlinVisibility.Public -> false - KotlinVisibility.Private -> true - KotlinVisibility.Protected -> true - KotlinVisibility.Internal -> true - JavaVisibility.Public -> false - JavaVisibility.Private -> true - JavaVisibility.Protected -> true - JavaVisibility.Default -> true - null -> false - } - } - - private fun packageOptionsOrNull( - dokkaSourceSet: DokkaSourceSet, - documentable: Documentable - ): DokkaConfiguration.PackageOptions? { - val packageName = documentable.dri.packageName ?: return null - return dokkaSourceSet.perPackageOptions - .filter { packageOptions -> Regex(packageOptions.matchingRegex).matches(packageName) } - .maxByOrNull { packageOptions -> packageOptions.matchingRegex.length } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt deleted file mode 100644 index 1dbf1262..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressTagDocumentableFilter.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.dfs -import org.jetbrains.dokka.model.doc.Suppress -import org.jetbrains.dokka.plugability.DokkaContext - -public class SuppressTagDocumentableFilter( - public val dokkaContext: DokkaContext -) : SuppressedByConditionDocumentableFilterTransformer(dokkaContext) { - override fun shouldBeSuppressed(d: Documentable): Boolean = - d.documentation.any { (_, docs) -> docs.dfs { it is Suppress } != null } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer.kt deleted file mode 100644 index 4631cece..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConditionDocumentableFilterTransformer.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer - -public abstract class SuppressedByConditionDocumentableFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> = - modules.map { module -> - val (documentable, wasChanged) = processModule(module) - documentable.takeIf { wasChanged } ?: module - } - - public abstract fun shouldBeSuppressed(d: Documentable): Boolean - - private fun processModule(module: DModule): DocumentableWithChanges<DModule> { - val afterProcessing = module.packages.map { processPackage(it) } - val processedModule = module.takeIf { afterProcessing.none { it.changed } } - ?: module.copy(packages = afterProcessing.mapNotNull { it.documentable }) - return DocumentableWithChanges(processedModule, afterProcessing.any { it.changed }) - } - - private fun processPackage(dPackage: DPackage): DocumentableWithChanges<DPackage> { - if (shouldBeSuppressed(dPackage)) return DocumentableWithChanges.filteredDocumentable() - - val classlikes = dPackage.classlikes.map { processClassLike(it) } - val typeAliases = dPackage.typealiases.map { processMember(it) } - val functions = dPackage.functions.map { processMember(it) } - val properies = dPackage.properties.map { processProperty(it) } - - val wasChanged = (classlikes + typeAliases + functions + properies).any { it.changed } - return (dPackage.takeIf { !wasChanged } ?: dPackage.copy( - classlikes = classlikes.mapNotNull { it.documentable }, - typealiases = typeAliases.mapNotNull { it.documentable }, - functions = functions.mapNotNull { it.documentable }, - properties = properies.mapNotNull { it.documentable } - )).let { processedPackage -> DocumentableWithChanges(processedPackage, wasChanged) } - } - - private fun processClassLike(classlike: DClasslike): DocumentableWithChanges<DClasslike> { - if (shouldBeSuppressed(classlike)) return DocumentableWithChanges.filteredDocumentable() - - val functions = classlike.functions.map { processMember(it) } - val classlikes = classlike.classlikes.map { processClassLike(it) } - val properties = classlike.properties.map { processProperty(it) } - val companion = (classlike as? WithCompanion)?.companion?.let { processClassLike(it) } - - val wasClasslikeChanged = (functions + classlikes + properties).any { it.changed } || companion?.changed == true - return when (classlike) { - is DClass -> { - val constructors = classlike.constructors.map { processMember(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - constructors = constructors.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject - )).let { DocumentableWithChanges(it, wasClassChange) } - } - is DInterface -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject - )).let { DocumentableWithChanges(it, wasClasslikeChanged) } - is DObject -> (classlike.takeIf { !wasClasslikeChanged } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - )).let { DocumentableWithChanges(it, wasClasslikeChanged) } - is DAnnotation -> { - val constructors = classlike.constructors.map { processMember(it) } - val wasClassChange = - wasClasslikeChanged || constructors.any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - constructors = constructors.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject - )).let { DocumentableWithChanges(it, wasClassChange) } - } - is DEnum -> { - val constructors = classlike.constructors.map { processMember(it) } - val entries = classlike.entries.map { processEnumEntry(it) } - val wasClassChange = - wasClasslikeChanged || (constructors + entries).any { it.changed } - (classlike.takeIf { !wasClassChange } ?: classlike.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - constructors = constructors.mapNotNull { it.documentable }, - companion = companion?.documentable as? DObject, - entries = entries.mapNotNull { it.documentable } - )).let { DocumentableWithChanges(it, wasClassChange) } - } - } - } - - private fun processEnumEntry(dEnumEntry: DEnumEntry): DocumentableWithChanges<DEnumEntry> { - if (shouldBeSuppressed(dEnumEntry)) return DocumentableWithChanges.filteredDocumentable() - - val functions = dEnumEntry.functions.map { processMember(it) } - val properties = dEnumEntry.properties.map { processProperty(it) } - val classlikes = dEnumEntry.classlikes.map { processClassLike(it) } - - val wasChanged = (functions + properties + classlikes).any { it.changed } - return (dEnumEntry.takeIf { !wasChanged } ?: dEnumEntry.copy( - functions = functions.mapNotNull { it.documentable }, - classlikes = classlikes.mapNotNull { it.documentable }, - properties = properties.mapNotNull { it.documentable }, - )).let { DocumentableWithChanges(it, wasChanged) } - } - - private fun processProperty(dProperty: DProperty): DocumentableWithChanges<DProperty> { - if (shouldBeSuppressed(dProperty)) return DocumentableWithChanges.filteredDocumentable() - - val getter = dProperty.getter?.let { processMember(it) } ?: DocumentableWithChanges(null, false) - val setter = dProperty.setter?.let { processMember(it) } ?: DocumentableWithChanges(null, false) - - val wasChanged = getter.changed || setter.changed - return (dProperty.takeIf { !wasChanged } ?: dProperty.copy( - getter = getter.documentable, - setter = setter.documentable - )).let { DocumentableWithChanges(it, wasChanged) } - } - - private fun <T : Documentable> processMember(member: T): DocumentableWithChanges<T> = - if (shouldBeSuppressed(member)) DocumentableWithChanges.filteredDocumentable() - else DocumentableWithChanges(member, false) - - private data class DocumentableWithChanges<T : Documentable>(val documentable: T?, val changed: Boolean = false) { - companion object { - fun <T : Documentable> filteredDocumentable(): DocumentableWithChanges<T> = - DocumentableWithChanges(null, true) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt deleted file mode 100644 index 3195f88d..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedByConfigurationDocumentableFilterTransformer.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer -import org.jetbrains.dokka.transformers.documentation.perPackageOptions -import org.jetbrains.dokka.transformers.documentation.source -import org.jetbrains.dokka.transformers.documentation.sourceSet -import java.io.File - -public class SuppressedByConfigurationDocumentableFilterTransformer( - public val context: DokkaContext -) : PreMergeDocumentableTransformer { - override fun invoke(modules: List<DModule>): List<DModule> { - return modules.mapNotNull(::filterModule) - } - - private fun filterModule(module: DModule): DModule? { - val packages = module.packages.mapNotNull { pkg -> filterPackage(pkg) } - return when { - packages == module.packages -> module - packages.isEmpty() -> null - else -> module.copy(packages = packages) - } - } - - private fun filterPackage(pkg: DPackage): DPackage? { - val options = perPackageOptions(pkg) - if (options?.suppress == true) { - return null - } - - val filteredChildren = pkg.children.filterNot(::isSuppressed) - return when { - filteredChildren == pkg.children -> pkg - filteredChildren.isEmpty() -> null - else -> pkg.copy( - functions = filteredChildren.filterIsInstance<DFunction>(), - classlikes = filteredChildren.filterIsInstance<DClasslike>(), - typealiases = filteredChildren.filterIsInstance<DTypeAlias>(), - properties = filteredChildren.filterIsInstance<DProperty>() - ) - } - } - - private fun isSuppressed(documentable: Documentable): Boolean { - if (documentable !is WithSources) return false - val sourceFile = File(source(documentable).path).absoluteFile - return sourceSet(documentable).suppressedFiles.any { suppressedFile -> - sourceFile.startsWith(suppressedFile.absoluteFile) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt b/plugins/base/src/main/kotlin/transformers/documentables/utils.kt deleted file mode 100644 index 60a6396a..00000000 --- a/plugins/base/src/main/kotlin/transformers/documentables/utils.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.documentables - -import org.jetbrains.dokka.model.Annotations -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.ExceptionInSupertypes -import org.jetbrains.dokka.model.properties.WithExtraProperties - -public val <T : Documentable> WithExtraProperties<T>.isException: Boolean - get() = extra[ExceptionInSupertypes] != null - - -public val <T : Documentable> WithExtraProperties<T>.deprecatedAnnotation: Annotations.Annotation? - get() = extra[Annotations]?.let { annotations -> - annotations.directAnnotations.values.flatten().firstOrNull { - it.isDeprecated() - } - } - -/** - * @return true if [T] has [kotlin.Deprecated] or [java.lang.Deprecated] - * annotation for **any** source set - */ -public fun <T : Documentable> WithExtraProperties<T>.isDeprecated(): Boolean = deprecatedAnnotation != null - -/** - * @return true for [kotlin.Deprecated] and [java.lang.Deprecated] - */ -public fun Annotations.Annotation.isDeprecated(): Boolean { - return (this.dri.packageName == "kotlin" && this.dri.classNames == "Deprecated") || - (this.dri.packageName == "java.lang" && this.dri.classNames == "Deprecated") -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt deleted file mode 100644 index 1ba049c8..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/DefaultSamplesTransformer.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages - -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.doc.Sample -import org.jetbrains.dokka.model.properties.PropertyContainer -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.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin -import org.jetbrains.dokka.analysis.kotlin.internal.SampleProvider -import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory - -internal const val KOTLIN_PLAYGROUND_SCRIPT = "https://unpkg.com/kotlin-playground@1/dist/playground.min.js" - -internal class DefaultSamplesTransformer(val context: DokkaContext) : PageTransformer { - - private val sampleProviderFactory: SampleProviderFactory = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { sampleProviderFactory } - - override fun invoke(input: RootPageNode): RootPageNode { - return sampleProviderFactory.build().use { sampleProvider -> - input.transformContentPagesTree { page -> - val samples = (page as? WithDocumentables)?.documentables?.flatMap { - it.documentation.entries.flatMap { entry -> - entry.value.children.filterIsInstance<Sample>().map { entry.key to it } - } - } ?: return@transformContentPagesTree page - - val newContent = samples.fold(page.content) { acc, (sampleSourceSet, sample) -> - sampleProvider.getSample(sampleSourceSet, sample.name) - ?.let { - acc.addSample(page, sample.name, it) - } ?: acc - } - - page.modified( - content = newContent, - embeddedResources = page.embeddedResources + KOTLIN_PLAYGROUND_SCRIPT - ) - } - } - } - - - private fun ContentNode.addSample( - contentPage: ContentPage, - fqLink: String, - sample: SampleProvider.SampleSnippet, - ): ContentNode { - val node = contentCode(contentPage.content.sourceSets, contentPage.dri, createSampleBody(sample.imports, sample.body), "kotlin") - return dfs(fqLink, node) - } - - fun createSampleBody(imports: String, body: String) = - """ |$imports - |fun main() { - | //sampleStart - | $body - | //sampleEnd - |}""".trimMargin() - - private fun ContentNode.dfs(fqName: String, node: ContentCodeBlock): ContentNode { - return when (this) { - is ContentHeader -> copy(children.map { it.dfs(fqName, node) }) - is ContentDivergentGroup -> @Suppress("UNCHECKED_CAST") copy(children.map { - it.dfs(fqName, node) - } as List<ContentDivergentInstance>) - is ContentDivergentInstance -> copy( - before.let { it?.dfs(fqName, node) }, - divergent.dfs(fqName, node), - after.let { it?.dfs(fqName, node) }) - is ContentCodeBlock -> copy(children.map { it.dfs(fqName, node) }) - is ContentCodeInline -> copy(children.map { it.dfs(fqName, node) }) - is ContentDRILink -> copy(children.map { it.dfs(fqName, node) }) - is ContentResolvedLink -> copy(children.map { it.dfs(fqName, node) }) - is ContentEmbeddedResource -> copy(children.map { it.dfs(fqName, node) }) - is ContentTable -> copy(children = children.map { it.dfs(fqName, node) as ContentGroup }) - is ContentList -> copy(children.map { it.dfs(fqName, node) }) - is ContentGroup -> copy(children.map { it.dfs(fqName, node) }) - is PlatformHintedContent -> copy(inner.dfs(fqName, node)) - is ContentText -> if (text == fqName) node else this - is ContentBreakLine -> this - else -> this.also { context.logger.error("Could not recognize $this ContentNode in SamplesTransformer") } - } - } - - private fun contentCode( - sourceSets: Set<DisplaySourceSet>, - dri: Set<DRI>, - content: String, - language: String, - styles: Set<Style> = emptySet(), - extra: PropertyContainer<ContentNode> = PropertyContainer.empty() - ) = - ContentCodeBlock( - children = listOf( - ContentText( - text = content, - dci = DCI(dri, ContentKind.Sample), - sourceSets = sourceSets, - style = emptySet(), - extra = PropertyContainer.empty() - ) - ), - language = language, - dci = DCI(dri, ContentKind.Sample), - sourceSets = sourceSets, - style = styles + ContentStyle.RunnableSample + TextStyle.Monospace, - extra = extra - ) -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt deleted file mode 100644 index 9ff5960d..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.annotations - - -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 -import org.jetbrains.dokka.model.doc.CustomTagWrapper -import org.jetbrains.dokka.model.doc.DocumentationNode -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 - -public class SinceKotlinVersion(str: String) : Comparable<SinceKotlinVersion> { - private val parts: List<Int> = str.split(".").map { it.toInt() } - - /** - * Corner case: 1.0 == 1.0.0 - */ - override fun compareTo(other: SinceKotlinVersion): Int { - val i1 = parts.listIterator() - val i2 = other.parts.listIterator() - - while (i1.hasNext() || i2.hasNext()) { - val diff = (if (i1.hasNext()) i1.next() else 0) - (if (i2.hasNext()) i2.next() else 0) - if (diff != 0) return diff - } - - return 0 - } - - override fun toString(): String = parts.joinToString(".") -} - -public class SinceKotlinTransformer( - public val context: DokkaContext -) : DocumentableTransformer { - - private val minSinceKotlinVersionOfPlatform = mapOf( - Platform.common to SinceKotlinVersion("1.0"), - Platform.jvm to SinceKotlinVersion("1.0"), - Platform.js to SinceKotlinVersion("1.1"), - Platform.native to SinceKotlinVersion("1.3"), - Platform.wasm to SinceKotlinVersion("1.8"), - ) - - override fun invoke(original: DModule, context: DokkaContext): DModule = original.transform() as DModule - - private fun <T : Documentable> T.transform(parent: SourceSetDependent<SinceKotlinVersion>? = null): Documentable { - val versions = calculateVersions(parent) - return when (this) { - is DModule -> copy( - packages = packages.map { it.transform() as DPackage } - ) - - is DPackage -> copy( - classlikes = classlikes.map { it.transform() as DClasslike }, - functions = functions.map { it.transform() as DFunction }, - properties = properties.map { it.transform() as DProperty }, - typealiases = typealiases.map { it.transform() as DTypeAlias } - ) - - is DClass -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DEnum -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DInterface -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DObject -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DTypeAlias -> copy( - documentation = appendSinceKotlin(versions) - ) - - is DAnnotation -> copy( - documentation = appendSinceKotlin(versions), - classlikes = classlikes.map { it.transform(versions) as DClasslike }, - functions = functions.map { it.transform(versions) as DFunction }, - properties = properties.map { it.transform(versions) as DProperty } - ) - - is DFunction -> copy( - documentation = appendSinceKotlin(versions) - ) - - is DProperty -> copy( - documentation = appendSinceKotlin(versions) - ) - - is DParameter -> copy( - documentation = appendSinceKotlin(versions) - ) - - else -> this.also { context.logger.warn("Unrecognized documentable $this while SinceKotlin transformation") } - } - } - - private fun List<Annotations.Annotation>.findSinceKotlinAnnotation(): Annotations.Annotation? = - this.find { it.dri.packageName == "kotlin" && it.dri.classNames == "SinceKotlin" } - - private fun Documentable.getVersion(sourceSet: DokkaConfiguration.DokkaSourceSet): SinceKotlinVersion { - val annotatedVersion = - annotations()[sourceSet] - ?.findSinceKotlinAnnotation() - ?.params?.let { it["version"] as? StringValue }?.value - ?.let { SinceKotlinVersion(it) } - - val minSinceKotlin = minSinceKotlinVersionOfPlatform[sourceSet.analysisPlatform] - ?: throw IllegalStateException("No value for platform: ${sourceSet.analysisPlatform}") - - return annotatedVersion?.takeIf { version -> version >= minSinceKotlin } ?: minSinceKotlin - } - - - private fun Documentable.calculateVersions(parent: SourceSetDependent<SinceKotlinVersion>?): SourceSetDependent<SinceKotlinVersion> { - return sourceSets.associateWithNotNull { sourceSet -> - val version = getVersion(sourceSet) - val parentVersion = parent?.get(sourceSet) - if (parentVersion != null) - maxOf(version, parentVersion) - else - version - } - } - - private fun Documentable.appendSinceKotlin(versions: SourceSetDependent<SinceKotlinVersion>) = - sourceSets.fold(documentation) { acc, sourceSet -> - - val version = versions[sourceSet] - - val sinceKotlinCustomTag = CustomTagWrapper( - CustomDocTag( - listOf( - Text( - version.toString() - ) - ), - name = MARKDOWN_ELEMENT_FILE_NAME - ), - "Since Kotlin" - ) - if (acc[sourceSet] == null) - acc + (sourceSet to DocumentationNode(listOf(sinceKotlinCustomTag))) - else - acc.mapValues { - if (it.key == sourceSet) it.value.copy( - it.value.children + listOf( - sinceKotlinCustomTag - ) - ) else it.value - } - } - - internal companion object { - internal const val SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP = "dokka.shouldDisplaySinceKotlin" - internal fun shouldDisplaySinceKotlin() = - System.getProperty(SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP) in listOf("true", "1") - } -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt deleted file mode 100644 index 6ca3f8d0..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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.ContentNode -import org.jetbrains.dokka.pages.DCI -import org.jetbrains.dokka.pages.Style - -public interface CommentsToContentConverter { - public fun buildContent( - docTag: DocTag, - dci: DCI, - sourceSets: Set<DokkaSourceSet>, - styles: Set<Style> = emptySet(), - extras: PropertyContainer<ContentNode> = PropertyContainer.empty() - ): List<ContentNode> -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt deleted file mode 100644 index e4e0f53f..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.comments - - -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.dokka.utilities.firstIsInstanceOrNull - -public open class DocTagToContentConverter : CommentsToContentConverter { - override fun buildContent( - docTag: DocTag, - dci: DCI, - sourceSets: Set<DokkaSourceSet>, - styles: Set<Style>, - extras: PropertyContainer<ContentNode> - ): List<ContentNode> { - - fun buildChildren(docTag: DocTag, newStyles: Set<Style> = emptySet(), newExtras: SimpleAttr? = null) = - docTag.children.flatMap { - buildContent(it, dci, sourceSets, styles + newStyles, newExtras?.let { extras + it } ?: extras) - } - - fun buildTableRows(rows: List<DocTag>, newStyle: Style): List<ContentGroup> = - rows.flatMap { - @Suppress("UNCHECKED_CAST") - buildContent(it, dci, sourceSets, styles + newStyle, extras) as List<ContentGroup> - } - - fun buildHeader(level: Int) = - listOf( - ContentHeader( - buildChildren(docTag), - level, - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - - fun buildList(ordered: Boolean, newStyles: Set<Style> = emptySet(), start: Int = 1) = - listOf( - ContentList( - buildChildren(docTag), - ordered, - dci, - sourceSets.toDisplaySourceSets(), - styles + newStyles, - ((PropertyContainer.empty<ContentNode>()) + SimpleAttr("start", start.toString())) - ) - ) - - fun buildNewLine() = listOf( - ContentBreakLine( - sourceSets.toDisplaySourceSets() - ) - ) - - fun P.collapseParagraphs(): P = - if (children.size == 1 && children.first() is P) (children.first() as P).collapseParagraphs() else this - - return when (docTag) { - is H1 -> buildHeader(1) - is H2 -> buildHeader(2) - is H3 -> buildHeader(3) - is H4 -> buildHeader(4) - is H5 -> buildHeader(5) - is H6 -> buildHeader(6) - is Ul -> buildList(false) - is Ol -> buildList(true, start = docTag.params["start"]?.toInt() ?: 1) - is Li -> listOf( - ContentGroup(buildChildren(docTag), dci, sourceSets.toDisplaySourceSets(), styles, extras) - ) - is Dl -> buildList(false, newStyles = setOf(ListStyle.DescriptionList)) - is Dt -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + ListStyle.DescriptionTerm - ) - ) - is Dd -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + ListStyle.DescriptionDetails - ) - ) - is Br -> buildNewLine() - is B -> buildChildren(docTag, setOf(TextStyle.Strong)) - is I -> buildChildren(docTag, setOf(TextStyle.Italic)) - is P -> listOf( - ContentGroup( - buildChildren(docTag.collapseParagraphs()), - dci, - sourceSets.toDisplaySourceSets(), - styles + setOf(TextStyle.Paragraph), - extras - ) - ) - is A -> listOf( - ContentResolvedLink( - buildChildren(docTag), - docTag.params.getValue("href"), - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is DocumentationLink -> listOf( - ContentDRILink( - buildChildren(docTag), - docTag.dri, - DCI( - setOf(docTag.dri), - ContentKind.Main - ), - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is BlockQuote -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + TextStyle.Quotation, - ) - ) - is Pre, is CodeBlock -> listOf( - ContentCodeBlock( - buildChildren(docTag), - docTag.params.getOrDefault("lang", ""), - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is CodeInline -> listOf( - ContentCodeInline( - buildChildren(docTag), - "", - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is Img -> listOf( - ContentEmbeddedResource( - address = docTag.params["href"]!!, - altText = docTag.params["alt"], - dci = dci, - sourceSets = sourceSets.toDisplaySourceSets(), - style = styles, - extra = extras - ) - ) - is HorizontalRule -> listOf( - ContentText( - "", - dci, - sourceSets.toDisplaySourceSets(), - setOf() - ) - ) - is Text -> listOf( - ContentText( - docTag.body, - dci, - sourceSets.toDisplaySourceSets(), - styles, - extras + HtmlContent.takeIf { docTag.params["content-type"] == "html" } - ) - ) - is Strikethrough -> buildChildren(docTag, setOf(TextStyle.Strikethrough)) - is Table -> { - //https://html.spec.whatwg.org/multipage/tables.html#the-caption-element - if (docTag.children.any { it is TBody }) { - val head = docTag.children.filterIsInstance<THead>().flatMap { it.children } - val body = docTag.children.filterIsInstance<TBody>().flatMap { it.children } - listOf( - ContentTable( - header = buildTableRows(head.filterIsInstance<Th>(), CommentTable), - caption = docTag.children.firstIsInstanceOrNull<Caption>()?.let { - ContentGroup( - buildContent(it, dci, sourceSets), - dci, - sourceSets.toDisplaySourceSets(), - styles, - extras - ) - }, - buildTableRows(body.filterIsInstance<Tr>(), CommentTable), - dci, - sourceSets.toDisplaySourceSets(), - styles + CommentTable - ) - ) - } else { - listOf( - ContentTable( - header = buildTableRows(docTag.children.filterIsInstance<Th>(), CommentTable), - caption = null, - buildTableRows(docTag.children.filterIsInstance<Tr>(), CommentTable), - dci, - sourceSets.toDisplaySourceSets(), - styles + CommentTable - ) - ) - } - } - is Th, - is Tr -> listOf( - ContentGroup( - docTag.children.map { - ContentGroup(buildChildren(it), dci, sourceSets.toDisplaySourceSets(), styles, extras) - }, - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is Index -> listOf( - ContentGroup( - buildChildren(docTag, newStyles = styles + ContentStyle.InDocumentationAnchor), - dci, - sourceSets.toDisplaySourceSets(), - styles - ) - ) - is CustomDocTag -> if (docTag.isNonemptyFile()) { - listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles, - extra = extras - ) - ) - } else { - buildChildren(docTag) - } - is Caption -> listOf( - ContentGroup( - buildChildren(docTag), - dci, - sourceSets.toDisplaySourceSets(), - styles + ContentStyle.Caption, - extra = extras - ) - ) - is Var -> buildChildren(docTag, setOf(TextStyle.Var)) - is U -> buildChildren(docTag, setOf(TextStyle.Underlined)) - - else -> buildChildren(docTag) - } - } - - private fun CustomDocTag.isNonemptyFile() = name == MARKDOWN_ELEMENT_FILE_NAME && children.size > 1 -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt deleted file mode 100644 index 80886cc5..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.pages.ContentPage -import org.jetbrains.dokka.pages.PageNode -import org.jetbrains.dokka.utilities.DokkaLogger - -public class FallbackPageMergerStrategy( - private val logger: DokkaLogger -) : PageMergerStrategy { - override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { - pages.map { - (it as? ContentPage) - } - val renderedPath = path.joinToString(separator = "/") - if (pages.size != 1) logger.warn("For $renderedPath: expected 1 page, but got ${pages.size}") - return listOf(pages.first()) - } -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt deleted file mode 100644 index e52c233c..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.pages.PageNode -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.transformers.pages.PageTransformer - -public class PageMerger(context: DokkaContext) : PageTransformer { - - private val strategies: Iterable<PageMergerStrategy> = context.plugin<DokkaBase>().query { pageMergerStrategy } - - override fun invoke(input: RootPageNode): RootPageNode = - input.modified(children = input.children.map { it.mergeChildren(emptyList()) }) - - private fun PageNode.mergeChildren(path: List<String>): PageNode = children.groupBy { it::class }.map { - it.value.groupBy { it.name }.map { (n, v) -> mergePageNodes(v, path + n) }.map { it.assertSingle(path) } - }.let { pages -> - modified(children = pages.flatten().map { it.mergeChildren(path + it.name) }) - } - - private fun mergePageNodes(pages: List<PageNode>, path: List<String>): List<PageNode> = - strategies.fold(pages) { acc, strategy -> tryMerge(strategy, acc, path) } - - private fun tryMerge(strategy: PageMergerStrategy, pages: List<PageNode>, path: List<String>) = - if (pages.size > 1) strategy.tryMerge(pages, path) else pages -} - -private fun <T> Iterable<T>.assertSingle(path: List<String>): T = try { - single() - } catch (e: Exception) { - val renderedPath = path.joinToString(separator = "/") - throw IllegalStateException("Page merger is misconfigured. Error for $renderedPath: ${e.message}") - } diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt deleted file mode 100644 index ea1b1f03..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.pages.PageNode - -public fun interface PageMergerStrategy { - - public fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> - -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt deleted file mode 100644 index 864545e6..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.merger - -import org.jetbrains.dokka.base.renderers.sourceSets -import org.jetbrains.dokka.base.transformers.documentables.isDeprecated -import org.jetbrains.dokka.model.DisplaySourceSet -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.dfs -import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.utilities.DokkaLogger - -/** - * Merges [MemberPage] elements that have the same name. - * That includes **both** properties and functions. - */ -public class SameMethodNamePageMergerStrategy( - public val logger: DokkaLogger -) : PageMergerStrategy { - override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { - val members = pages - .filterIsInstance<MemberPageNode>() - .takeIf { it.isNotEmpty() } - ?.sortedBy { it.containsDeprecatedDocumentables() } // non-deprecated first - ?: return pages - - val name = pages.first().name.also { - if (pages.any { page -> page.name != it }) { // Is this even possible? - logger.error("Page names for $it do not match!") - } - } - val dri = members.flatMap { it.dri }.toSet() - - - val merged = MemberPageNode( - dri = dri, - name = name, - children = members.flatMap { it.children }.distinct(), - content = squashDivergentInstances(members).withSourceSets(members.allSourceSets()), - embeddedResources = members.flatMap { it.embeddedResources }.distinct(), - documentables = members.flatMap { it.documentables } - ) - - return (pages - members) + listOf(merged) - } - - @Suppress("UNCHECKED_CAST") - private fun MemberPageNode.containsDeprecatedDocumentables() = - this.documentables.any { (it as? WithExtraProperties<Documentable>)?.isDeprecated() == true } - - private fun List<MemberPageNode>.allSourceSets(): Set<DisplaySourceSet> = - fold(emptySet()) { acc, e -> acc + e.sourceSets() } - - private fun squashDivergentInstances(nodes: List<MemberPageNode>): ContentNode = - nodes.map { it.content } - .reduce { acc, node -> - acc.mapTransform<ContentDivergentGroup, ContentNode> { g -> - g.copy(children = (g.children + - ((node.dfs { it is ContentDivergentGroup && it.groupID == g.groupID } as? ContentDivergentGroup) - ?.children ?: emptyList()) - ) - ) - } - } -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt deleted file mode 100644 index 8d52a39d..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/merger/SourceSetMergingPageTransformer.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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.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 - -public class SourceSetMergingPageTransformer(context: DokkaContext) : PageTransformer { - - private val mergedSourceSets = context.configuration.sourceSets.toDisplaySourceSets() - .associateBy { sourceSet -> sourceSet.key } - - override fun invoke(input: RootPageNode): RootPageNode { - return input.transformContentPagesTree { contentPage -> - val content: ContentNode = contentPage.content - contentPage.modified(content = transformWithMergedSourceSets(content)) - } - } - - private fun transformWithMergedSourceSets( - contentNode: ContentNode - ): ContentNode { - val mergedSourceSets = contentNode.sourceSets.map { mergedSourceSets.getValue(it.key) }.toSet() - return when (contentNode) { - is ContentComposite -> contentNode - .transformChildren(::transformWithMergedSourceSets) - .withSourceSets(mergedSourceSets) - else -> contentNode.withSourceSets(mergedSourceSets.toSet()) - } - } -} - -private val DisplaySourceSet.key get() = SourceSetMergingKey(name, platform) - -private data class SourceSetMergingKey(private val displayName: String, private val platform: Platform) diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt deleted file mode 100644 index 80eeca7e..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.sourcelinks - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -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.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 java.io.File - -public class SourceLinksTransformer( - public val context: DokkaContext -) : PageTransformer { - - private val builder : PageContentBuilder = PageContentBuilder( - context.plugin<DokkaBase>().querySingle { commentsToContentConverter }, - context.plugin<DokkaBase>().querySingle { signatureProvider }, - context.logger - ) - - override fun invoke(input: RootPageNode): RootPageNode { - val sourceLinks = getSourceLinksFromConfiguration() - if (sourceLinks.isEmpty()) { - return input - } - return input.transformContentPagesTree { node -> - when (node) { - is WithDocumentables -> { - val sources = node.documentables - .filterIsInstance<WithSources>() - .fold(mutableMapOf<DRI, List<Pair<DokkaSourceSet, String>>>()) { acc, documentable -> - val dri = (documentable as Documentable).dri - acc.compute(dri) { _, v -> - val sources = resolveSources(sourceLinks, documentable) - v?.plus(sources) ?: sources - } - acc - } - if (sources.isNotEmpty()) - node.modified(content = transformContent(node.content, sources)) - else - node - } - else -> node - } - } - } - - private fun getSourceLinksFromConfiguration(): List<SourceLink> { - return context.configuration.sourceSets - .flatMap { it.sourceLinks.map { sl -> SourceLink(sl, it) } } - } - - private fun resolveSources( - sourceLinks: List<SourceLink>, documentable: WithSources - ): List<Pair<DokkaSourceSet, String>> { - return documentable.sources.mapNotNull { (sourceSet, documentableSource) -> - val sourceLink = sourceLinks.find { sourceLink -> - File(documentableSource.path).startsWith(sourceLink.path) && sourceLink.sourceSetData == sourceSet - } ?: return@mapNotNull null - - sourceSet to documentableSource.toLink(sourceLink) - } - } - - private fun DocumentableSource.toLink(sourceLink: SourceLink): String { - val sourcePath = File(this.path).invariantSeparatorsPath - val sourceLinkPath = File(sourceLink.path).invariantSeparatorsPath - - val lineNumber = this.computeLineNumber() - return sourceLink.url + - sourcePath.split(sourceLinkPath)[1] + - sourceLink.lineSuffix + - "${lineNumber ?: 1}" - } - - private fun ContentNode.signatureGroupOrNull() = - (this as? ContentGroup)?.takeIf { it.dci.kind == ContentKind.Symbol } - - private fun transformContent( - contentNode: ContentNode, sources: Map<DRI, List<Pair<DokkaSourceSet, String>>> - ): ContentNode = - contentNode.signatureGroupOrNull()?.let { sg -> - val sgIds = sg.sourceSets.computeSourceSetIds() - sources[sg.dci.dri.singleOrNull()]?.let { sourceLinks -> - sourceLinks - .filter { it.first.sourceSetID in sgIds } - .takeIf { it.isNotEmpty() } - ?.let { filteredSourcesLinks -> - sg.copy(children = sg.children + filteredSourcesLinks.map { - buildContentLink( - sg.dci.dri.first(), - it.first, - it.second - ) - }) - } - } - } ?: when (contentNode) { - is ContentComposite -> contentNode.transformChildren { transformContent(it, sources) } - else -> contentNode - } - - private fun buildContentLink(dri: DRI, sourceSet: DokkaSourceSet, link: String) = builder.contentFor( - dri, - setOf(sourceSet), - ContentKind.Source, - setOf(TextStyle.FloatingRight) - ) { - text("(") - link("source", link) - text(")") - } -} - -public data class SourceLink( - val path: String, - val url: String, - val lineSuffix: String?, - val sourceSetData: DokkaSourceSet -) { - public constructor( - sourceLinkDefinition: DokkaConfiguration.SourceLinkDefinition, - sourceSetData: DokkaSourceSet - ) : this( - sourceLinkDefinition.localDirectory, - sourceLinkDefinition.remoteUrl.toExternalForm(), - sourceLinkDefinition.remoteLineSuffix, - sourceSetData - ) -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt b/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt deleted file mode 100644 index fcec234f..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.tags - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.model.doc.CustomTagWrapper -import org.jetbrains.dokka.model.doc.DocTag - -/** - * Provides an ability to render custom doc tags - * - * Custom tags can be generated during build, for instance via transformers from converting an annotation - * (such as in [org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer]) - * - * Also, custom tags can come from the kdoc itself, where "custom" is defined as unknown to the compiler/spec. - * `@property` and `@throws` are not custom tags - they are defined by the spec and have special meaning - * and separate blocks on the documentation page, it's clear how to render it. Whereas `@usesMathJax` is - * a custom tag - it's application/plugin specific and is not handled by dokka by default. - * - * Using this provider, we can map custom tags (such as `@usesMathJax`) and generate content for it that - * will be displayed on the pages. - */ -public interface CustomTagContentProvider { - - /** - * Whether this content provider supports given [CustomTagWrapper]. - * - * Tags can be filtered out either by name or by nested [DocTag] type - */ - public fun isApplicable(customTag: CustomTagWrapper): Boolean - - /** - * Full blown content description, most likely to be on a separate page - * dedicated to just one element (i.e one class/function), so any - * amount of detail should be fine. - */ - public fun DocumentableContentBuilder.contentForDescription( - sourceSet: DokkaSourceSet, - customTag: CustomTagWrapper - ) {} - - /** - * Brief comment section, usually displayed as a summary/preview. - * - * For instance, when listing all functions of a class on one page, - * it'll be too much to display complete documentation for each function. - * Instead, a small brief is shown for each one (i.e the first paragraph - * or some other important information) - the user can go to the dedicated - * page for more details if they find the brief interesting. - * - * Tag-wise, it would make sense to include `Since Kotlin`, since it's - * important information for the users of stdlib. It would make little - * sense to include `@usesMathjax` here, as this information seems - * to be more specific and detailed than is needed for a brief. - */ - public fun DocumentableContentBuilder.contentForBrief( - sourceSet: DokkaSourceSet, - customTag: CustomTagWrapper - ) {} -} diff --git a/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt b/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt deleted file mode 100644 index 7c35f719..00000000 --- a/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.transformers.pages.tags - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.base.translators.documentables.KDOC_TAG_HEADER_LEVEL -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.model.doc.CustomTagWrapper -import org.jetbrains.dokka.pages.TextStyle - -public object SinceKotlinTagContentProvider : CustomTagContentProvider { - - private const val SINCE_KOTLIN_TAG_NAME = "Since Kotlin" - - override fun isApplicable(customTag: CustomTagWrapper): Boolean = customTag.name == SINCE_KOTLIN_TAG_NAME - - override fun DocumentableContentBuilder.contentForDescription( - sourceSet: DokkaConfiguration.DokkaSourceSet, - customTag: CustomTagWrapper - ) { - group(sourceSets = setOf(sourceSet), styles = emptySet()) { - header(KDOC_TAG_HEADER_LEVEL, customTag.name) - comment(customTag.root) - } - } - - override fun DocumentableContentBuilder.contentForBrief( - sourceSet: DokkaConfiguration.DokkaSourceSet, - customTag: CustomTagWrapper - ) { - group(sourceSets = setOf(sourceSet), styles = setOf(TextStyle.InlineComment)) { - text(customTag.name + " ", styles = setOf(TextStyle.Bold)) - comment(customTag.root, styles = emptySet()) - } - } -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt deleted file mode 100644 index 0b2597d5..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -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.* -import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator -import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin - -public class DefaultDocumentableToPageTranslator( - 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 = context.logger - - override fun invoke(module: DModule): ModulePageNode = - DefaultPageCreator( - configuration, - commentsToContentConverter, - signatureProvider, - logger, - customTagContentProviders, - documentableSourceLanguageParser - ).pageForModule(module) -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt deleted file mode 100644 index 5c8ac512..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ /dev/null @@ -1,779 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint -import org.jetbrains.dokka.base.signatures.SignatureProvider -import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions -import org.jetbrains.dokka.transformers.documentation.ClashingDriIdentifier -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder -import org.jetbrains.dokka.base.utils.canonicalAlphabeticalOrder -import org.jetbrains.dokka.links.Callable -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.doc.* -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 org.jetbrains.dokka.analysis.kotlin.internal.DocumentableSourceLanguageParser -import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableLanguage -import kotlin.reflect.KClass - -internal typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>> - -public open class DefaultPageCreator( - configuration: DokkaBaseConfiguration?, - commentsToContentConverter: CommentsToContentConverter, - signatureProvider: SignatureProvider, - public val logger: DokkaLogger, - public val customTagContentProviders: List<CustomTagContentProvider> = emptyList(), - public val documentableAnalyzer: DocumentableSourceLanguageParser -) { - protected open val contentBuilder: PageContentBuilder = PageContentBuilder( - commentsToContentConverter, signatureProvider, logger - ) - - protected val mergeImplicitExpectActualDeclarations: Boolean = - configuration?.mergeImplicitExpectActualDeclarations - ?: DokkaBaseConfiguration.mergeImplicitExpectActualDeclarationsDefault - - protected val separateInheritedMembers: Boolean = - configuration?.separateInheritedMembers ?: DokkaBaseConfiguration.separateInheritedMembersDefault - - public open fun pageForModule(m: DModule): ModulePageNode = - ModulePageNode(m.name.ifEmpty { "<root>" }, contentForModule(m), listOf(m), m.packages.map(::pageForPackage)) - - /** - * We want to generate separated pages for no-actual typealias. - * Actual typealias are displayed on pages for their expect class (trough [ActualTypealias] extra). - * - * @see ActualTypealias - */ - private fun List<Documentable>.filterOutActualTypeAlias(): List<Documentable> { - fun List<Documentable>.hasExpectClass(dri: DRI) = find { it is DClasslike && it.dri == dri && it.expectPresentInSet != null } != null - return this.filterNot { it is DTypeAlias && this.hasExpectClass(it.dri) } - } - - public open fun pageForPackage(p: DPackage): PackagePageNode { - val children = if (mergeImplicitExpectActualDeclarations) { - (p.classlikes + p.typealiases).filterOutActualTypeAlias() - .mergeClashingDocumentable().map(::pageForClasslikes) + - p.functions.mergeClashingDocumentable().map(::pageForFunctions) + - p.properties.mergeClashingDocumentable().map(::pageForProperties) - } else { - (p.classlikes + p.typealiases).filterOutActualTypeAlias() - .renameClashingDocumentable().map(::pageForClasslike) + - p.functions.renameClashingDocumentable().map(::pageForFunction) + - p.properties.mapNotNull(::pageForProperty) - } - return PackagePageNode( - name = p.name, - content = contentForPackage(p), - dri = setOf(p.dri), - documentables = listOf(p), - children = children - ) - } - - public open fun pageForEnumEntry(e: DEnumEntry): ClasslikePageNode = pageForEnumEntries(listOf(e)) - - public open fun pageForClasslike(c: Documentable): ClasslikePageNode = pageForClasslikes(listOf(c)) - - public open fun pageForEnumEntries(documentables: List<DEnumEntry>): ClasslikePageNode { - val dri = documentables.dri.also { - if (it.size != 1) { - logger.error("Documentable dri should have the same one ${it.first()} inside the one page!") - } - } - - val classlikes = documentables.flatMap { it.classlikes } - val functions = documentables.flatMap { it.filteredFunctions } - val props = documentables.flatMap { it.filteredProperties } - - val childrenPages = if (mergeImplicitExpectActualDeclarations) - functions.mergeClashingDocumentable().map(::pageForFunctions) + - props.mergeClashingDocumentable().map(::pageForProperties) - else - classlikes.renameClashingDocumentable().map(::pageForClasslike) + - functions.renameClashingDocumentable().map(::pageForFunction) + - props.renameClashingDocumentable().mapNotNull(::pageForProperty) - - return ClasslikePageNode( - documentables.first().nameAfterClash(), contentForClasslikesAndEntries(documentables), dri, documentables, - childrenPages - ) - } - - /** - * @param documentables a list of [DClasslike] and [DTypeAlias] with the same dri in different sourceSets - */ - public open fun pageForClasslikes(documentables: List<Documentable>): ClasslikePageNode { - val dri = documentables.dri.also { - if (it.size != 1) { - logger.error("Documentable dri should have the same one ${it.first()} inside the one page!") - } - } - - val classlikes = documentables.filterIsInstance<DClasslike>() - - val constructors = - if (classlikes.shouldDocumentConstructors()) { - classlikes.flatMap { (it as? WithConstructors)?.constructors ?: emptyList() } - } else { - emptyList() - } - - val nestedClasslikes = classlikes.flatMap { it.classlikes } - val functions = classlikes.flatMap { it.filteredFunctions } - val props = classlikes.flatMap { it.filteredProperties } - val entries = classlikes.flatMap { if (it is DEnum) it.entries else emptyList() } - - val childrenPages = constructors.map(::pageForFunction) + - if (mergeImplicitExpectActualDeclarations) - nestedClasslikes.mergeClashingDocumentable().map(::pageForClasslikes) + - functions.mergeClashingDocumentable().map(::pageForFunctions) + - props.mergeClashingDocumentable().map(::pageForProperties) + - entries.mergeClashingDocumentable().map(::pageForEnumEntries) - else - nestedClasslikes.renameClashingDocumentable().map(::pageForClasslike) + - functions.renameClashingDocumentable().map(::pageForFunction) + - props.renameClashingDocumentable().mapNotNull(::pageForProperty) + - entries.renameClashingDocumentable().map(::pageForEnumEntry) - - - return ClasslikePageNode( - documentables.first().nameAfterClash(), contentForClasslikesAndEntries(documentables), dri, documentables, - childrenPages - ) - } - - private fun <T> T.toClashedName() where T : Documentable, T : WithExtraProperties<T> = - (extra[ClashingDriIdentifier]?.value?.joinToString(", ", "[", "]") { it.displayName } ?: "") + name.orEmpty() - - private fun <T : Documentable> List<T>.renameClashingDocumentable(): List<T> = - groupBy { it.dri }.values.flatMap { elements -> - if (elements.size == 1) elements else elements.mapNotNull { element -> - element.renameClashingDocumentable() - } - } - - @Suppress("UNCHECKED_CAST") - private fun <T : Documentable> T.renameClashingDocumentable(): T? = when (this) { - is DClass -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DObject -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DAnnotation -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DInterface -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DEnum -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DFunction -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DProperty -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - is DTypeAlias -> copy(extra = this.extra + DriClashAwareName(this.toClashedName())) - else -> null - } as? T? - - private fun <T : Documentable> List<T>.mergeClashingDocumentable(): List<List<T>> = - groupBy { it.dri }.values.toList() - - public open fun pageForFunction(f: DFunction): MemberPageNode = - MemberPageNode(f.nameAfterClash(), contentForFunction(f), setOf(f.dri), listOf(f)) - - public open fun pageForFunctions(fs: List<DFunction>): MemberPageNode { - val dri = fs.dri.also { - if (it.size != 1) { - logger.error("Function dri should have the same one ${it.first()} inside the one page!") - } - } - return MemberPageNode(fs.first().nameAfterClash(), contentForMembers(fs), dri, fs) - } - - public open fun pageForProperty(p: DProperty): MemberPageNode? = - MemberPageNode(p.nameAfterClash(), contentForProperty(p), setOf(p.dri), listOf(p)) - - public open fun pageForProperties(ps: List<DProperty>): MemberPageNode { - val dri = ps.dri.also { - if (it.size != 1) { - logger.error("Property dri should have the same one ${it.first()} inside the one page!") - } - } - return MemberPageNode(ps.first().nameAfterClash(), contentForMembers(ps), dri, ps) - } - - private fun <T> T.isInherited(): Boolean where T : Documentable, T : WithExtraProperties<T> = - sourceSets.all { sourceSet -> extra[InheritedMember]?.isInherited(sourceSet) == true } - - private val WithScope.filteredFunctions: List<DFunction> - get() = functions.filterNot { it.isInherited() } - - private val WithScope.filteredProperties: List<DProperty> - get() = properties.filterNot { it.isInherited() } - - private fun Collection<Documentable>.splitPropsAndFuns(): Pair<List<DProperty>, List<DFunction>> { - val first = ArrayList<DProperty>() - val second = ArrayList<DFunction>() - for (element in this) { - when (element) { - is DProperty -> first.add(element) - is DFunction -> second.add(element) - else -> throw IllegalStateException("Expected only properties or functions") - } - } - return Pair(first, second) - } - - private fun <T> Collection<T>.splitInheritedExtension(dri: Set<DRI>): Pair<List<T>, List<T>> where T : org.jetbrains.dokka.model.Callable = - partition { it.receiver?.dri !in dri } - - private fun <T> Collection<T>.splitInherited(): Pair<List<T>, List<T>> where T : Documentable, T : WithExtraProperties<T> = - partition { it.isInherited() } - - protected open fun contentForModule(m: DModule): ContentGroup { - return contentBuilder.contentFor(m) { - group(kind = ContentKind.Cover) { - cover(m.name) - if (contentForDescription(m).isNotEmpty()) { - sourceSetDependentHint( - m.dri, - m.sourceSets.toSet(), - kind = ContentKind.SourceSetDependentHint, - styles = setOf(TextStyle.UnderCoverText) - ) { - +contentForDescription(m) - } - } - } - - block( - name = "Packages", - level = 2, - kind = ContentKind.Packages, - elements = m.packages, - sourceSets = m.sourceSets.toSet(), - needsAnchors = true, - headers = listOf( - headers("Name") - ) - ) { - val documentations = it.sourceSets.map { platform -> - it.descriptions[platform]?.also { it.root } - } - val haveSameContent = - documentations.all { it?.root == documentations.firstOrNull()?.root && it?.root != null } - - link(it.name, it.dri) - if (it.sourceSets.size == 1 || (documentations.isNotEmpty() && haveSameContent)) { - documentations.first()?.let { firstParagraphComment(kind = ContentKind.Comment, content = it.root) } - } - } - } - } - - protected open fun contentForPackage(p: DPackage): ContentGroup { - return contentBuilder.contentFor(p) { - group(kind = ContentKind.Cover) { - cover("Package-level declarations") - if (contentForDescription(p).isNotEmpty()) { - sourceSetDependentHint( - dri = p.dri, - sourcesetData = p.sourceSets.toSet(), - kind = ContentKind.SourceSetDependentHint, - styles = setOf(TextStyle.UnderCoverText) - ) { - +contentForDescription(p) - } - } - } - group(styles = setOf(ContentStyle.TabbedContent), extra = mainExtra) { - +contentForScope(p, p.dri, p.sourceSets) - } - } - } - - protected open fun contentForScopes( - scopes: List<WithScope>, - sourceSets: Set<DokkaSourceSet>, - extensions: List<Documentable> = emptyList() - ): ContentGroup { - val types = scopes.flatMap { it.classlikes } + scopes.filterIsInstance<DPackage>().flatMap { it.typealiases } - return contentForScope( - @Suppress("UNCHECKED_CAST") - (scopes as List<Documentable>).dri, - sourceSets, - types, - scopes.flatMap { it.functions }, - scopes.flatMap { it.properties }, - extensions - ) - } - - protected open fun contentForScope( - s: WithScope, - dri: DRI, - sourceSets: Set<DokkaSourceSet>, - extensions: List<Documentable> = emptyList() - ): ContentGroup { - val types = listOf( - s.classlikes, - (s as? DPackage)?.typealiases ?: emptyList() - ).flatten() - return contentForScope(setOf(dri), sourceSets, types, s.functions, s.properties, extensions) - } - - private fun contentForScope( - dri: Set<DRI>, - sourceSets: Set<DokkaSourceSet>, - types: List<Documentable>, - functions: List<DFunction>, - properties: List<DProperty>, - extensions: List<Documentable> - ) = contentBuilder.contentFor(dri, sourceSets) { - divergentBlock( - "Types", - types, - ContentKind.Classlikes - ) - val (extensionProps, extensionFuns) = extensions.splitPropsAndFuns() - if (separateInheritedMembers) { - val (inheritedFunctions, memberFunctions) = functions.splitInherited() - val (inheritedProperties, memberProperties) = properties.splitInherited() - - val (inheritedExtensionFunctions, extensionFunctions) = extensionFuns.splitInheritedExtension(dri) - val (inheritedExtensionProperties, extensionProperties) = extensionProps.splitInheritedExtension(dri) - propertiesBlock( - "Properties", memberProperties + extensionProperties - ) - propertiesBlock( - "Inherited properties", inheritedProperties + inheritedExtensionProperties - ) - functionsBlock("Functions", memberFunctions + extensionFunctions) - functionsBlock( - "Inherited functions", inheritedFunctions + inheritedExtensionFunctions - ) - } else { - propertiesBlock( - "Properties", properties + extensionProps - ) - functionsBlock("Functions", functions + extensionFuns) - } - } - - private fun Iterable<DFunction>.sorted() = - sortedWith(compareBy({ it.name }, { it.parameters.size }, { it.dri.toString() })) - - /** - * @param documentables a list of [DClasslike] and [DEnumEntry] and [DTypeAlias] with the same dri in different sourceSets - */ - protected open fun contentForClasslikesAndEntries(documentables: List<Documentable>): ContentGroup = - contentBuilder.contentFor(documentables.dri, documentables.sourceSets) { - val classlikes = documentables.filterIsInstance<DClasslike>() - - @Suppress("UNCHECKED_CAST") - val extensions = (classlikes as List<WithExtraProperties<DClasslike>>).flatMap { - it.extra[CallableExtensions]?.extensions - ?.filterIsInstance<Documentable>().orEmpty() - } - .distinctBy { it.sourceSets to it.dri } // [Documentable] has expensive equals/hashCode at the moment, see #2620 - - // Extensions are added to sourceSets since they can be placed outside the sourceSets from classlike - // Example would be an Interface in common and extension function in jvm - group(kind = ContentKind.Cover, sourceSets = mainSourcesetData + extensions.sourceSets) { - cover(documentables.first().name.orEmpty()) - sourceSetDependentHint(documentables.dri, documentables.sourceSets) { - documentables.forEach { - +buildSignature(it) - +contentForDescription(it) - } - } - } - val csEnum = classlikes.filterIsInstance<DEnum>() - val csWithConstructor = classlikes.filterIsInstance<WithConstructors>() - val scopes = documentables.filterIsInstance<WithScope>() - val constructorsToDocumented = csWithConstructor.flatMap { it.constructors } - - group( - styles = setOf(ContentStyle.TabbedContent), - sourceSets = mainSourcesetData + extensions.sourceSets, - extra = mainExtra - ) { - if (constructorsToDocumented.isNotEmpty() && documentables.shouldDocumentConstructors()) { - +contentForConstructors(constructorsToDocumented, classlikes.dri, classlikes.sourceSets) - } - if (csEnum.isNotEmpty()) { - +contentForEntries(csEnum.flatMap { it.entries }, csEnum.dri, csEnum.sourceSets) - } - +contentForScopes(scopes, documentables.sourceSets, extensions) - } - } - protected open fun contentForConstructors( - constructorsToDocumented: List<DFunction>, - dri: Set<DRI>, - sourceSets: Set<DokkaSourceSet> - ): ContentGroup { - return contentBuilder.contentFor(dri, sourceSets) { - multiBlock( - name = "Constructors", - level = 2, - kind = ContentKind.Constructors, - groupedElements = constructorsToDocumented.groupBy { it.name } - .map { (_, v) -> v.first().name to v }, - sourceSets = (constructorsToDocumented as List<Documentable>).sourceSets, - needsAnchors = true, - extra = PropertyContainer.empty<ContentNode>() + TabbedContentTypeExtra( - BasicTabbedContentType.CONSTRUCTOR - ), - ) { key, ds -> - link(key, ds.first().dri, kind = ContentKind.Main, styles = setOf(ContentStyle.RowTitle)) - sourceSetDependentHint( - dri = ds.dri, - sourceSets = ds.sourceSets, - kind = ContentKind.SourceSetDependentHint, - styles = emptySet(), - extra = PropertyContainer.empty() - ) { - ds.forEach { - +buildSignature(it) - contentForBrief(it) - } - } - } - } - } - - protected open fun contentForEntries( - entries: List<DEnumEntry>, - dri: Set<DRI>, - sourceSets: Set<DokkaSourceSet> - ): ContentGroup { - return contentBuilder.contentFor(dri, sourceSets) { - multiBlock( - name = "Entries", - level = 2, - kind = ContentKind.Classlikes, - groupedElements = entries.groupBy { it.name }.toList(), - sourceSets = entries.sourceSets, - needsSorting = false, - needsAnchors = true, - extra = mainExtra + TabbedContentTypeExtra(BasicTabbedContentType.ENTRY), - styles = emptySet() - ) { key, ds -> - link(key, ds.first().dri) - sourceSetDependentHint( - dri = ds.dri, - sourceSets = ds.sourceSets, - kind = ContentKind.SourceSetDependentHint, - extra = PropertyContainer.empty<ContentNode>() - ) { - ds.forEach { - +buildSignature(it) - contentForBrief(it) - } - } - } - } - } - - - - protected open fun contentForDescription( - d: Documentable - ): List<ContentNode> { - val sourceSets = d.sourceSets.toSet() - val tags = d.groupedTags - - return contentBuilder.contentFor(d) { - deprecatedSectionContent(d, sourceSets) - - descriptionSectionContent(d, sourceSets) - customTagSectionContent(d, sourceSets, customTagContentProviders) - unnamedTagSectionContent(d, sourceSets) { toHeaderString() } - - paramsSectionContent(tags) - seeAlsoSectionContent(tags) - throwsSectionContent(tags) - samplesSectionContent(tags) - - inheritorsSectionContent(d, logger) - }.children - } - - protected open fun DocumentableContentBuilder.contentForBrief( - documentable: Documentable - ) { - documentable.sourceSets.forEach { sourceSet -> - documentable.documentation[sourceSet]?.let { - /* - Get description or a tag that holds documentation. - This tag can be either property or constructor but constructor tags are handled already in analysis so we - only need to keep an eye on property - - We purposefully ignore all other tags as they should not be visible in brief - */ - it.firstMemberOfTypeOrNull<Description>() ?: it.firstMemberOfTypeOrNull<Property>() - .takeIf { documentable is DProperty } - }?.let { - group(sourceSets = setOf(sourceSet), kind = ContentKind.BriefComment) { - createBriefComment(documentable, sourceSet, it) - } - } - } - } - - private fun DocumentableContentBuilder.createBriefComment( - documentable: Documentable, - sourceSet: DokkaSourceSet, - tag: TagWrapper - ) { - 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): ContentGroup = contentForMember(f) - - protected open fun contentForProperty(p: DProperty): ContentGroup = contentForMember(p) - - protected open fun contentForMember(d: Documentable): ContentGroup = contentForMembers(listOf(d)) - - protected open fun contentForMembers(doumentables: List<Documentable>): ContentGroup = - contentBuilder.contentFor(doumentables.dri, doumentables.sourceSets) { - group(kind = ContentKind.Cover) { - cover(doumentables.first().name.orEmpty()) - } - divergentGroup(ContentDivergentGroup.GroupID("member")) { - doumentables.forEach { d -> - instance(setOf(d.dri), d.sourceSets) { - divergent { - +buildSignature(d) - } - after { - +contentForDescription(d) - } - } - } - } - } - - private fun DocumentableContentBuilder.functionsBlock( - name: String, - list: Collection<DFunction> - ) { - divergentBlock( - name, - list.sorted(), - ContentKind.Functions, - extra = mainExtra - ) - } - - private fun DocumentableContentBuilder.propertiesBlock( - name: String, - list: Collection<DProperty> - ) { - divergentBlock( - name, - list, - ContentKind.Properties, - extra = mainExtra - ) - - } - private data class NameAndIsExtension(val name:String?, val isExtension: Boolean) - - private fun groupAndSortDivergentCollection(collection: Collection<Documentable>): List<Map.Entry<NameAndIsExtension, List<Documentable>>> { - val groupKeyComparator: Comparator<Map.Entry<NameAndIsExtension, List<Documentable>>> = - compareBy<Map.Entry<NameAndIsExtension, List<Documentable>>, String?>( - nullsFirst(canonicalAlphabeticalOrder) - ) { it.key.name } - .thenBy { it.key.isExtension } - - return collection - .groupBy { - NameAndIsExtension( - it.name, - it.isExtension() - ) - } // This groupBy should probably use LocationProvider - // This hacks displaying actual typealias signatures along classlike ones - .mapValues { if (it.value.any { it is DClasslike }) it.value.filter { it !is DTypeAlias } else it.value } - .entries.sortedWith(groupKeyComparator) - } - - protected open fun DocumentableContentBuilder.divergentBlock( - name: String, - collection: Collection<Documentable>, - kind: ContentKind, - extra: PropertyContainer<ContentNode> = mainExtra - ) { - if (collection.any()) { - val onlyExtensions = collection.all { it.isExtension() } - val groupExtra = when(kind) { - ContentKind.Functions -> extra + TabbedContentTypeExtra(if (onlyExtensions) BasicTabbedContentType.EXTENSION_FUNCTION else BasicTabbedContentType.FUNCTION) - ContentKind.Properties -> extra + TabbedContentTypeExtra(if (onlyExtensions) BasicTabbedContentType.EXTENSION_PROPERTY else BasicTabbedContentType.PROPERTY) - ContentKind.Classlikes -> extra + TabbedContentTypeExtra(BasicTabbedContentType.TYPE) - else -> extra - } - - group(extra = groupExtra) { - // be careful: groupExtra will be applied for children by default - header(2, name, kind = kind, extra = extra) { } - val isFunctions = collection.any { it is DFunction } - table(kind, extra = extra, styles = emptySet()) { - header { - group { text("Name") } - group { text("Summary") } - } - groupAndSortDivergentCollection(collection) - .forEach { (elementNameAndIsExtension, elements) -> // This groupBy should probably use LocationProvider - val elementName = elementNameAndIsExtension.name - val isExtension = elementNameAndIsExtension.isExtension - val rowExtra = - if (isExtension) extra + TabbedContentTypeExtra(if(isFunctions) BasicTabbedContentType.EXTENSION_FUNCTION else BasicTabbedContentType.EXTENSION_PROPERTY) else extra - val rowKind = if (isExtension) ContentKind.Extensions else kind - val sortedElements = sortDivergentElementsDeterministically(elements) - row( - dri = sortedElements.map { it.dri }.toSet(), - sourceSets = sortedElements.flatMap { it.sourceSets }.toSet(), - kind = rowKind, - styles = emptySet(), - extra = elementName?.let { name -> rowExtra + SymbolAnchorHint(name, kind) } ?: rowExtra - ) { - link( - text = elementName.orEmpty(), - address = sortedElements.first().dri, - kind = rowKind, - styles = setOf(ContentStyle.RowTitle), - sourceSets = sortedElements.sourceSets.toSet(), - extra = extra - ) - divergentGroup( - ContentDivergentGroup.GroupID(name), - sortedElements.map { it.dri }.toSet(), - kind = rowKind, - extra = extra - ) { - sortedElements.map { element -> - instance( - setOf(element.dri), - element.sourceSets.toSet(), - extra = PropertyContainer.withAll( - SymbolAnchorHint(element.name ?: "", rowKind) - ) - ) { - divergent(extra = PropertyContainer.empty()) { - group { - +buildSignature(element) - } - } - after( - extra = PropertyContainer.empty() - ) { - contentForBrief(element) - contentForCustomTagsBrief(element) - } - } - } - } - } - } - } - } - } - } - - /** - * Divergent elements, such as extensions for the same receiver, can have identical signatures - * if they are declared in different places. If such elements are shown on the same page together, - * they need to be rendered deterministically to have reproducible builds. - * - * For example, you can have three identical extensions, if they are declared as: - * 1) top-level in package A - * 2) top-level in package B - * 3) inside a companion object in package A/B - * - * @see divergentBlock - * - * @param elements can contain types (annotation/class/interface/object/typealias), functions and properties - * @return the original list if it has one or zero elements - */ - private fun sortDivergentElementsDeterministically(elements: List<Documentable>): List<Documentable> = - elements.takeIf { it.size > 1 } // the majority are single-element lists, but no real benchmarks done - ?.sortedWith(divergentDocumentableComparator) - ?: elements - - private fun DocumentableContentBuilder.contentForCustomTagsBrief(documentable: Documentable) { - val customTags = documentable.customTags - if (customTags.isEmpty()) return - - documentable.sourceSets.forEach { sourceSet -> - customTags.forEach { (_, sourceSetTag) -> - sourceSetTag[sourceSet]?.let { tag -> - customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> - with(provider) { - contentForBrief(sourceSet, tag) - } - } - } - } - } - } - - protected open fun TagWrapper.toHeaderString(): String = this.javaClass.toGenericString().split('.').last() -} - -internal val List<Documentable>.sourceSets: Set<DokkaSourceSet> - get() = flatMap { it.sourceSets }.toSet() - -internal val List<Documentable>.dri: Set<DRI> - get() = map { it.dri }.toSet() - -internal val Documentable.groupedTags: GroupedTags - get() = documentation.flatMap { (pd, doc) -> - doc.children.map { pd to it }.toList() - }.groupBy { it.second::class } - -internal val Documentable.descriptions: SourceSetDependent<Description> - get() = groupedTags.withTypeUnnamed() - -internal val Documentable.customTags: Map<String, SourceSetDependent<CustomTagWrapper>> - get() = groupedTags.withTypeNamed() - -/** - * @see DefaultPageCreator.sortDivergentElementsDeterministically for usage - */ -private val divergentDocumentableComparator = - compareBy<Documentable, String?>(nullsLast()) { it.dri.packageName } - .thenBy(nullsFirst()) { it.dri.classNames } // nullsFirst for top level to be first - .thenBy( - nullsLast( - compareBy<Callable> { it.params.size } - .thenBy { it.signature() } - ) - ) { it.dri.callable } - -@Suppress("UNCHECKED_CAST") -private fun <T : Documentable> T.nameAfterClash(): String = - ((this as? WithExtraProperties<Documentable>)?.extra?.get(DriClashAwareName)?.value ?: name).orEmpty() - -@Suppress("UNCHECKED_CAST") -internal inline fun <reified T : TagWrapper> GroupedTags.withTypeUnnamed(): SourceSetDependent<T> = - (this[T::class] as List<Pair<DokkaSourceSet, T>>?)?.toMap().orEmpty() - -@Suppress("UNCHECKED_CAST") -internal inline fun <reified T : NamedTagWrapper> GroupedTags.withTypeNamed(): Map<String, SourceSetDependent<T>> = - (this[T::class] as List<Pair<DokkaSourceSet, T>>?) - ?.groupByTo(linkedMapOf()) { it.second.name } - ?.mapValues { (_, v) -> v.toMap() } - .orEmpty() - -// Annotations might have constructors to substitute reflection invocations -// and for internal/compiler purposes, but they are not expected to be documented -// and instantiated directly under normal circumstances, so constructors should not be rendered. -internal fun List<Documentable>.shouldDocumentConstructors() = !this.any { it is DAnnotation } diff --git a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt deleted file mode 100644 index 0f51578f..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DeprecationSectionCreator.kt +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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 - -/** - * Main header for [Deprecated] section - */ -private const val DEPRECATED_HEADER_LEVEL = 3 - -/** - * Header for a direct parameter of [Deprecated] annotation, - * such as [Deprecated.message] and [Deprecated.replaceWith] - */ -private const val DIRECT_PARAM_HEADER_LEVEL = 4 - -internal fun PageContentBuilder.DocumentableContentBuilder.deprecatedSectionContent( - documentable: Documentable, - platforms: Set<DokkaConfiguration.DokkaSourceSet> -) { - val allAnnotations = documentable.annotations() - if (allAnnotations.isEmpty()) { - return - } - - platforms.forEach { platform -> - val platformAnnotations = allAnnotations[platform] ?: emptyList() - val deprecatedPlatformAnnotations = platformAnnotations.filter { it.isDeprecated() } - - if (deprecatedPlatformAnnotations.isNotEmpty()) { - group(kind = ContentKind.Deprecation, sourceSets = setOf(platform), styles = emptySet()) { - val kotlinAnnotation = deprecatedPlatformAnnotations.find { it.dri.packageName == "kotlin" } - val javaAnnotation = deprecatedPlatformAnnotations.find { it.dri.packageName == "java.lang" } - - // If both annotations are present, priority is given to Kotlin's annotation since it - // contains more useful information, and Java's annotation is probably there - // for interop with Java callers, so it should be OK to ignore it - if (kotlinAnnotation != null) { - createKotlinDeprecatedSectionContent(kotlinAnnotation, platformAnnotations) - } else if (javaAnnotation != null) { - createJavaDeprecatedSectionContent(javaAnnotation) - } - } - } - } -} - -/** - * @see [DeprecatedSinceKotlin] - */ -private fun findDeprecatedSinceKotlinAnnotation(annotations: List<Annotations.Annotation>): Annotations.Annotation? { - return annotations.firstOrNull { - it.dri.packageName == "kotlin" && it.dri.classNames == "DeprecatedSinceKotlin" - } -} - -/** - * Section with details for Kotlin's [kotlin.Deprecated] annotation - */ -private fun DocumentableContentBuilder.createKotlinDeprecatedSectionContent( - deprecatedAnnotation: Annotations.Annotation, - allAnnotations: List<Annotations.Annotation> -) { - val deprecatedSinceKotlinAnnotation = findDeprecatedSinceKotlinAnnotation(allAnnotations) - header( - level = DEPRECATED_HEADER_LEVEL, - text = createKotlinDeprecatedHeaderText(deprecatedAnnotation, deprecatedSinceKotlinAnnotation) - ) - - deprecatedSinceKotlinAnnotation?.let { - createDeprecatedSinceKotlinFootnoteContent(it) - } - - deprecatedAnnotation.takeStringParam("message")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text(it) - } - } - - createReplaceWithSectionContent(deprecatedAnnotation) -} - -private fun createKotlinDeprecatedHeaderText( - kotlinDeprecatedAnnotation: Annotations.Annotation, - deprecatedSinceKotlinAnnotation: Annotations.Annotation? -): String { - if (deprecatedSinceKotlinAnnotation != null) { - // In this case there's no single level, it's dynamic based on api version, - // so there should be a footnote with levels and their respective versions - return "Deprecated" - } - - val deprecationLevel = kotlinDeprecatedAnnotation.params["level"]?.let { (it as? EnumValue)?.enumName } - return when (deprecationLevel) { - "DeprecationLevel.ERROR" -> "Deprecated (with error)" - "DeprecationLevel.HIDDEN" -> "Deprecated (hidden)" - else -> "Deprecated" - } -} - -/** - * Footnote for [DeprecatedSinceKotlin] annotation used in stdlib - * - * Notice that values are empty by default, so it's not guaranteed that all three will be set - */ -private fun DocumentableContentBuilder.createDeprecatedSinceKotlinFootnoteContent( - deprecatedSinceKotlinAnnotation: Annotations.Annotation -) { - group(styles = setOf(ContentStyle.Footnote)) { - deprecatedSinceKotlinAnnotation.takeStringParam("warningSince")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text("Warning since $it") - } - } - deprecatedSinceKotlinAnnotation.takeStringParam("errorSince")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text("Error since $it") - } - } - deprecatedSinceKotlinAnnotation.takeStringParam("hiddenSince")?.let { - group(styles = setOf(TextStyle.Paragraph)) { - text("Hidden since $it") - } - } - } -} - -/** - * Section for [ReplaceWith] parameter of [kotlin.Deprecated] annotation - */ -private fun DocumentableContentBuilder.createReplaceWithSectionContent(kotlinDeprecatedAnnotation: Annotations.Annotation) { - val replaceWithAnnotation = (kotlinDeprecatedAnnotation.params["replaceWith"] as? AnnotationValue)?.annotation - ?: return - - header( - level = DIRECT_PARAM_HEADER_LEVEL, - text = "Replace with" - ) - - // Signature: vararg val imports: String - val imports = (replaceWithAnnotation.params["imports"] as? ArrayValue) - ?.value - ?.mapNotNull { (it as? StringValue)?.value } - ?: emptyList() - - if (imports.isNotEmpty()) { - codeBlock(language = "kotlin", styles = setOf(TextStyle.Monospace)) { - imports.forEach { - text("import $it") - breakLine() - } - } - } - - replaceWithAnnotation.takeStringParam("expression")?.removeSurrounding("`")?.let { - codeBlock(language = "kotlin", styles = setOf(TextStyle.Monospace)) { - text(it) - } - } -} - -/** - * Section with details for Java's [java.lang.Deprecated] annotation - */ -private fun DocumentableContentBuilder.createJavaDeprecatedSectionContent( - deprecatedAnnotation: Annotations.Annotation, -) { - val isForRemoval = deprecatedAnnotation.takeBooleanParam("forRemoval", default = false) - header( - level = DEPRECATED_HEADER_LEVEL, - text = if (isForRemoval) "Deprecated (for removal)" else "Deprecated" - ) - deprecatedAnnotation.takeStringParam("since")?.let { - group(styles = setOf(ContentStyle.Footnote)) { - text("Since version $it") - } - } -} - -private fun Annotations.Annotation.takeBooleanParam(name: String, default: Boolean): Boolean = - (this.params[name] as? BooleanValue)?.value ?: default - -private fun Annotations.Annotation.takeStringParam(name: String): String? = - (this.params[name] as? StringValue)?.takeIf { it.value.isNotEmpty() }?.value diff --git a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt b/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt deleted file mode 100644 index e2489260..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DescriptionSections.kt +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo -import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.PointingToDeclaration -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.SourceSetDependent -import org.jetbrains.dokka.model.WithScope -import org.jetbrains.dokka.model.doc.* -import org.jetbrains.dokka.model.orEmpty -import org.jetbrains.dokka.model.properties.WithExtraProperties -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 kotlin.reflect.KClass -import kotlin.reflect.full.isSubclassOf - -internal const val KDOC_TAG_HEADER_LEVEL = 4 - -private val unnamedTagsExceptions: Set<KClass<out TagWrapper>> = - setOf(Property::class, Description::class, Constructor::class, Param::class, See::class) - -internal fun PageContentBuilder.DocumentableContentBuilder.descriptionSectionContent( - documentable: Documentable, - sourceSets: Set<DokkaConfiguration.DokkaSourceSet>, -) { - val descriptions = documentable.descriptions - if (descriptions.any { it.value.root.children.isNotEmpty() }) { - sourceSets.forEach { sourceSet -> - descriptions[sourceSet]?.also { - group(sourceSets = setOf(sourceSet), styles = emptySet()) { - comment(it.root) - } - } - } - } -} - -/** - * Custom tags are tags which are not part of the [KDoc specification](https://kotlinlang.org/docs/kotlin-doc.html). For instance, a user-defined tag - * which is specific to the user's code base would be considered a custom tag. - * - * For details, see [CustomTagContentProvider] - */ -internal fun PageContentBuilder.DocumentableContentBuilder.customTagSectionContent( - documentable: Documentable, - sourceSets: Set<DokkaConfiguration.DokkaSourceSet>, - customTagContentProviders: List<CustomTagContentProvider>, -) { - val customTags = documentable.customTags - if (customTags.isEmpty()) return - - sourceSets.forEach { sourceSet -> - customTags.forEach { (_, sourceSetTag) -> - sourceSetTag[sourceSet]?.let { tag -> - customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> - group(sourceSets = setOf(sourceSet), styles = setOf(ContentStyle.KDocTag)) { - with(provider) { - contentForDescription(sourceSet, tag) - } - } - } - } - } - } -} - -/** - * Tags in KDoc are used in form of "@tag name value". - * This function handles tags that have only value parameter without name. - * List of such tags: `@return`, `@author`, `@since`, `@receiver` - */ -internal fun PageContentBuilder.DocumentableContentBuilder.unnamedTagSectionContent( - documentable: Documentable, - sourceSets: Set<DokkaConfiguration.DokkaSourceSet>, - toHeaderString: TagWrapper.() -> String, -) { - val unnamedTags = documentable.groupedTags - .filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in unnamedTagsExceptions } - .values.flatten().groupBy { it.first } - .mapValues { it.value.map { it.second } } - .takeIf { it.isNotEmpty() } ?: return - - sourceSets.forEach { sourceSet -> - unnamedTags[sourceSet]?.let { tags -> - if (tags.isNotEmpty()) { - tags.groupBy { it::class }.forEach { (_, sameCategoryTags) -> - group(sourceSets = setOf(sourceSet), styles = setOf(ContentStyle.KDocTag)) { - header( - level = KDOC_TAG_HEADER_LEVEL, - text = sameCategoryTags.first().toHeaderString(), - styles = setOf() - ) - sameCategoryTags.forEach { comment(it.root, styles = setOf()) } - } - } - } - } - } -} - - -internal fun PageContentBuilder.DocumentableContentBuilder.paramsSectionContent(tags: GroupedTags) { - val params = tags.withTypeNamed<Param>() - if (params.isEmpty()) return - - val availableSourceSets = params.availableSourceSets() - tableSectionContentBlock( - blockName = "Parameters", - kind = ContentKind.Parameters, - sourceSets = availableSourceSets - ) { - availableSourceSets.forEach { sourceSet -> - val possibleFallbacks = availableSourceSets.getPossibleFallback(sourceSet) - params.mapNotNull { (_, param) -> - (param[sourceSet] ?: param.fallback(possibleFallbacks))?.let { - row(sourceSets = setOf(sourceSet), kind = ContentKind.Parameters) { - text( - it.name, - kind = ContentKind.Parameters, - styles = mainStyles + setOf(ContentStyle.RowTitle, TextStyle.Underlined) - ) - if (it.isNotEmpty()) { - comment(it.root) - } - } - } - } - } - } -} - -internal fun PageContentBuilder.DocumentableContentBuilder.seeAlsoSectionContent(tags: GroupedTags) { - val seeAlsoTags = tags.withTypeNamed<See>() - if (seeAlsoTags.isEmpty()) return - - val availableSourceSets = seeAlsoTags.availableSourceSets() - tableSectionContentBlock( - blockName = "See also", - kind = ContentKind.Comment, - sourceSets = availableSourceSets - ) { - availableSourceSets.forEach { sourceSet -> - val possibleFallbacks = availableSourceSets.getPossibleFallback(sourceSet) - seeAlsoTags.forEach { (_, see) -> - (see[sourceSet] ?: see.fallback(possibleFallbacks))?.let { seeTag -> - row( - sourceSets = setOf(sourceSet), - kind = ContentKind.Comment - ) { - seeTag.address?.let { dri -> - link( - text = seeTag.name.removePrefix("${dri.packageName}."), - address = dri, - kind = ContentKind.Comment, - styles = mainStyles + ContentStyle.RowTitle - ) - } ?: text( - text = seeTag.name, - kind = ContentKind.Comment, - styles = mainStyles + ContentStyle.RowTitle - ) - if (seeTag.isNotEmpty()) { - comment(seeTag.root) - } - } - } - } - } - } -} - -/** - * Used for multi-value tags (e.g. params) when values are missed on some platforms. - * It this case description is inherited from parent platform. - * E.g. if param hasn't description in JVM, the description is taken from common. - */ -private fun Set<DokkaConfiguration.DokkaSourceSet>.getPossibleFallback(sourceSet: DokkaConfiguration.DokkaSourceSet) = - this.filter { it.sourceSetID in sourceSet.dependentSourceSets } - -private fun <V> Map<DokkaConfiguration.DokkaSourceSet, V>.fallback(sourceSets: List<DokkaConfiguration.DokkaSourceSet>): V? = - sourceSets.firstOrNull { it in this.keys }.let { this[it] } - -internal fun PageContentBuilder.DocumentableContentBuilder.throwsSectionContent(tags: GroupedTags) { - val throwsTags = tags.withTypeNamed<Throws>() - if (throwsTags.isEmpty()) return - - val availableSourceSets = throwsTags.availableSourceSets() - tableSectionContentBlock( - blockName = "Throws", - kind = ContentKind.Main, - sourceSets = availableSourceSets - ) { - throwsTags.forEach { (throwsName, throwsPerSourceSet) -> - throwsPerSourceSet.forEach { (sourceSet, throws) -> - row(sourceSets = setOf(sourceSet)) { - group(styles = mainStyles + ContentStyle.RowTitle) { - throws.exceptionAddress?.let { - val className = it.takeIf { it.target is PointingToDeclaration }?.classNames - link(text = className ?: throwsName, address = it) - } ?: text(throwsName) - } - if (throws.isNotEmpty()) { - comment(throws.root) - } - } - } - } - } -} - -private fun TagWrapper.isNotEmpty() = this.children.isNotEmpty() - -internal fun PageContentBuilder.DocumentableContentBuilder.samplesSectionContent(tags: GroupedTags) { - val samples = tags.withTypeNamed<Sample>() - if (samples.isEmpty()) return - - val availableSourceSets = samples.availableSourceSets() - - header(KDOC_TAG_HEADER_LEVEL, "Samples", kind = ContentKind.Sample, sourceSets = availableSourceSets) - availableSourceSets.forEach { sourceSet -> - group( - sourceSets = setOf(sourceSet), - kind = ContentKind.Sample, - styles = setOf(TextStyle.Monospace, ContentStyle.RunnableSample), - ) { - samples.filter { it.value.isEmpty() || sourceSet in it.value } - .forEach { text(text = it.key, sourceSets = setOf(sourceSet)) } - } - } -} - -internal fun PageContentBuilder.DocumentableContentBuilder.inheritorsSectionContent( - documentable: Documentable, - logger: DokkaLogger, -) { - val inheritors = if (documentable is WithScope) documentable.inheritors() else return - if (inheritors.values.none()) return - - // split content section for the case: - // parent is in the shared source set (without expect-actual) and inheritor is in the platform code - if (documentable.isDefinedInSharedSourceSetOnly(inheritors.keys.toSet())) - sharedSourceSetOnlyInheritorsSectionContent(inheritors, logger) - else - multiplatformInheritorsSectionContent(documentable, inheritors, logger) -} - -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 - * Value of `analysisPlatform` will be [Platform.common] in cases if a source set shared between 2 different platforms. - * But if it shared between 2 same platforms (e.g. jvm("awt") and jvm("android")) - * then the source set will be still marked as jvm platform. - * - * So, we also try to check if any of inheritors source sets depends on current documentable source set. - * that will mean that the source set is shared. - */ -private fun Documentable.isDefinedInSharedSourceSetOnly(inheritorsSourceSets: Set<DokkaConfiguration.DokkaSourceSet>) = - sourceSets.size == 1 && - (sourceSets.first().analysisPlatform == Platform.common - || sourceSets.first().hasDependentSourceSet(inheritorsSourceSets)) - -private fun DokkaConfiguration.DokkaSourceSet.hasDependentSourceSet( - sourceSets: Set<DokkaConfiguration.DokkaSourceSet>, -) = - sourceSets.any { sourceSet -> sourceSet.dependentSourceSets.any { it == this.sourceSetID } } - -private fun PageContentBuilder.DocumentableContentBuilder.multiplatformInheritorsSectionContent( - documentable: Documentable, - inheritors: Map<DokkaConfiguration.DokkaSourceSet, List<DRI>>, - logger: DokkaLogger, -) { - // intersect is used for removing duplication in case of merged classlikes from different platforms - val availableSourceSets = inheritors.keys.toSet().intersect(documentable.sourceSets) - - tableSectionContentBlock( - blockName = "Inheritors", - kind = ContentKind.Inheritors, - sourceSets = availableSourceSets - ) { - availableSourceSets.forEach { sourceSet -> - inheritors[sourceSet]?.forEach { classlike: DRI -> - inheritorRow(classlike, logger, sourceSet) - } - } - } -} - -private fun PageContentBuilder.DocumentableContentBuilder.sharedSourceSetOnlyInheritorsSectionContent( - inheritors: Map<DokkaConfiguration.DokkaSourceSet, List<DRI>>, - logger: DokkaLogger, -) { - val uniqueInheritors = inheritors.values.flatten().toSet() - tableSectionContentBlock( - blockName = "Inheritors", - kind = ContentKind.Inheritors, - ) { - uniqueInheritors.forEach { classlike -> - inheritorRow(classlike, logger) - } - } -} - -private fun PageContentBuilder.TableBuilder.inheritorRow( - classlike: DRI, logger: DokkaLogger, sourceSet: DokkaConfiguration.DokkaSourceSet? = null, -) = row { - link( - text = classlike.friendlyClassName() - ?: classlike.toString().also { logger.warn("No class name found for DRI $classlike") }, - address = classlike, - sourceSets = sourceSet?.let { setOf(it) } ?: mainSourcesetData - ) -} - -private fun PageContentBuilder.DocumentableContentBuilder.tableSectionContentBlock( - blockName: String, - kind: ContentKind, - sourceSets: Set<DokkaConfiguration.DokkaSourceSet> = mainSourcesetData, - body: PageContentBuilder.TableBuilder.() -> Unit, -) { - header(KDOC_TAG_HEADER_LEVEL, text = blockName, kind = kind, sourceSets = sourceSets) - table( - kind = kind, - sourceSets = sourceSets, - ) { - body() - } -} - -private fun DRI.friendlyClassName() = classNames?.substringAfterLast(".") - -private fun <T> Map<String, SourceSetDependent<T>>.availableSourceSets() = values.flatMap { it.keys }.toSet() diff --git a/plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt b/plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt deleted file mode 100644 index 362bb9b9..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/DriClashAwareName.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.properties.ExtraProperty - -public data class DriClashAwareName(val value: String?): ExtraProperty<Documentable> { - public companion object : ExtraProperty.Key<Documentable, DriClashAwareName> - override val key: ExtraProperty.Key<Documentable, *> = Companion -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt deleted file mode 100644 index 4ddda674..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ /dev/null @@ -1,781 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet -import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint -import org.jetbrains.dokka.base.signatures.SignatureProvider -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.SourceSetDependent -import org.jetbrains.dokka.model.doc.DocTag -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.dokka.utilities.DokkaLogger - -@DslMarker -public annotation class ContentBuilderMarker - -public open class PageContentBuilder( - public val commentsConverter: CommentsToContentConverter, - public val signatureProvider: SignatureProvider, - public val logger: DokkaLogger -) { - public fun contentFor( - dri: DRI, - sourceSets: Set<DokkaSourceSet>, - kind: Kind = ContentKind.Main, - styles: Set<Style> = emptySet(), - extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), - block: DocumentableContentBuilder.() -> Unit - ): ContentGroup = - DocumentableContentBuilder(setOf(dri), sourceSets, styles, extra) - .apply(block) - .build(sourceSets, kind, styles, extra) - - public fun contentFor( - dri: Set<DRI>, - sourceSets: Set<DokkaSourceSet>, - kind: Kind = ContentKind.Main, - styles: Set<Style> = emptySet(), - extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), - block: DocumentableContentBuilder.() -> Unit - ): ContentGroup = - DocumentableContentBuilder(dri, sourceSets, styles, extra) - .apply(block) - .build(sourceSets, kind, styles, extra) - - public fun contentFor( - d: Documentable, - kind: Kind = ContentKind.Main, - styles: Set<Style> = emptySet(), - extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), - sourceSets: Set<DokkaSourceSet> = d.sourceSets.toSet(), - block: DocumentableContentBuilder.() -> Unit = {} - ): ContentGroup = - DocumentableContentBuilder(setOf(d.dri), sourceSets, styles, extra) - .apply(block) - .build(sourceSets, kind, styles, extra) - - @ContentBuilderMarker - public open inner class DocumentableContentBuilder( - public val mainDRI: Set<DRI>, - public val mainSourcesetData: Set<DokkaSourceSet>, - public val mainStyles: Set<Style>, - public val mainExtra: PropertyContainer<ContentNode> - ) { - protected val contents: MutableList<ContentNode> = mutableListOf<ContentNode>() - - public fun build( - sourceSets: Set<DokkaSourceSet>, - kind: Kind, - styles: Set<Style>, - extra: PropertyContainer<ContentNode> - ): ContentGroup { - return ContentGroup( - children = contents.toList(), - dci = DCI(mainDRI, kind), - sourceSets = sourceSets.toDisplaySourceSets(), - style = styles, - extra = extra - ) - } - - public operator fun ContentNode.unaryPlus() { - contents += this - } - - public operator fun Collection<ContentNode>.unaryPlus() { - contents += this - } - - public fun header( - level: Int, - text: String, - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit = {} - ) { - contents += ContentHeader( - level, - contentFor( - mainDRI, - sourceSets, - kind, - styles, - extra + SymbolAnchorHint(text.replace("\\s".toRegex(), "").toLowerCase(), kind) - ) { - text(text, kind = kind) - block() - } - ) - } - - public fun cover( - text: String, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles + TextStyle.Cover, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit = {} - ) { - header(1, text, sourceSets = sourceSets, styles = styles, extra = extra, block = block) - } - - public fun constant(text: String) { - text(text, styles = mainStyles + TokenStyle.Constant) - } - - public fun keyword(text: String) { - text(text, styles = mainStyles + TokenStyle.Keyword) - } - - public fun stringLiteral(text: String) { - text(text, styles = mainStyles + TokenStyle.String) - } - - public fun booleanLiteral(value: Boolean) { - text(value.toString(), styles = mainStyles + TokenStyle.Boolean) - } - - public fun punctuation(text: String) { - text(text, styles = mainStyles + TokenStyle.Punctuation) - } - - public fun operator(text: String) { - text(text, styles = mainStyles + TokenStyle.Operator) - } - - public fun text( - text: String, - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ) { - contents += createText(text, kind, sourceSets, styles, extra) - } - - public fun breakLine(sourceSets: Set<DokkaSourceSet> = mainSourcesetData) { - contents += ContentBreakLine(sourceSets.toDisplaySourceSets()) - } - - public fun buildSignature(d: Documentable): List<ContentNode> = signatureProvider.signature(d) - - public fun table( - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - operation: TableBuilder.() -> Unit = {} - ) { - contents += TableBuilder(mainDRI, sourceSets, kind, styles, extra).apply { - operation() - }.build() - } - - public fun unorderedList( - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - operation: ListBuilder.() -> Unit = {} - ) { - contents += ListBuilder(false, mainDRI, sourceSets, kind, styles, extra).apply(operation).build() - } - - public fun orderedList( - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - operation: ListBuilder.() -> Unit = {} - ) { - contents += ListBuilder(true, mainDRI, sourceSets, kind, styles, extra).apply(operation).build() - } - - public fun descriptionList( - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - operation: ListBuilder.() -> Unit = {} - ) { - contents += ListBuilder(false, mainDRI, sourceSets, kind, styles + ListStyle.DescriptionList, extra) - .apply(operation) - .build() - } - - internal fun headers(vararg label: String) = contentFor(mainDRI, mainSourcesetData) { - label.forEach { text(it) } - } - - public fun <T : Documentable> block( - name: String, - level: Int, - kind: Kind = ContentKind.Main, - elements: Iterable<T>, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - renderWhenEmpty: Boolean = false, - needsSorting: Boolean = true, - headers: List<ContentGroup> = emptyList(), - needsAnchors: Boolean = false, - operation: DocumentableContentBuilder.(T) -> Unit - ) { - if (renderWhenEmpty || elements.any()) { - header(level, name, kind = kind) { } - contents += ContentTable( - header = headers, - children = elements - .let { - if (needsSorting) - it.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.name }) - else it - } - .map { - val newExtra = if (needsAnchors) extra + SymbolAnchorHint.from(it, kind) else extra - buildGroup(setOf(it.dri), it.sourceSets.toSet(), kind, styles, newExtra) { - operation(it) - } - }, - dci = DCI(mainDRI, kind), - sourceSets = sourceSets.toDisplaySourceSets(), - style = styles, - extra = extra - ) - } - } - - public fun <T : Pair<String, List<Documentable>>> multiBlock( - name: String, - level: Int, - kind: Kind = ContentKind.Main, - groupedElements: Iterable<T>, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - renderWhenEmpty: Boolean = false, - needsSorting: Boolean = true, - headers: List<ContentGroup> = emptyList(), - needsAnchors: Boolean = false, - operation: DocumentableContentBuilder.(String, List<Documentable>) -> Unit - ) { - - if (renderWhenEmpty || groupedElements.any()) { - group(extra = extra) { - header(level, name, kind = kind) { } - contents += ContentTable( - header = headers, - children = groupedElements - .let { - if (needsSorting) - it.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.first }) - else it - } - .map { - val documentables = it.second - val newExtra = if (needsAnchors) extra + SymbolAnchorHint( - it.first, - kind - ) else extra - buildGroup( - documentables.map { it.dri }.toSet(), - documentables.flatMap { it.sourceSets }.toSet(), - kind, - styles, - newExtra - ) { - operation(it.first, documentables) - } - }, - dci = DCI(mainDRI, kind), - sourceSets = sourceSets.toDisplaySourceSets(), - style = styles, - extra = extra - ) - } - } - } - - public fun <T> list( - elements: List<T>, - prefix: String = "", - suffix: String = "", - separator: String = ", ", - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, // TODO: children should be aware of this platform data - surroundingCharactersStyle: Set<Style> = mainStyles, - separatorStyles: Set<Style> = mainStyles, - operation: DocumentableContentBuilder.(T) -> Unit - ) { - if (elements.isNotEmpty()) { - if (prefix.isNotEmpty()) text(prefix, sourceSets = sourceSets, styles = surroundingCharactersStyle) - elements.dropLast(1).forEach { - operation(it) - text(separator, sourceSets = sourceSets, styles = separatorStyles) - } - operation(elements.last()) - if (suffix.isNotEmpty()) text(suffix, sourceSets = sourceSets, styles = surroundingCharactersStyle) - } - } - - public fun link( - text: String, - address: DRI, - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ) { - contents += linkNode(text, address, DCI(mainDRI, kind), sourceSets, styles, extra) - } - - public fun linkNode( - text: String, - address: DRI, - dci: DCI = DCI(mainDRI, ContentKind.Main), - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ): ContentLink { - return ContentDRILink( - listOf(createText(text, dci.kind, sourceSets, styles, extra)), - address, - dci, - sourceSets.toDisplaySourceSets(), - extra = extra - ) - } - - public fun link( - text: String, - address: String, - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ) { - contents += ContentResolvedLink( - children = listOf(createText(text, kind, sourceSets, styles, extra)), - address = address, - extra = PropertyContainer.empty(), - dci = DCI(mainDRI, kind), - sourceSets = sourceSets.toDisplaySourceSets(), - style = emptySet() - ) - } - - public fun link( - address: DRI, - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contents += ContentDRILink( - contentFor(mainDRI, sourceSets, kind, styles, extra, block).children, - address, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - extra = extra - ) - } - - public fun comment( - docTag: DocTag, - kind: Kind = ContentKind.Comment, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ) { - val content = commentsConverter.buildContent( - docTag, - DCI(mainDRI, kind), - sourceSets - ) - contents += ContentGroup(content, DCI(mainDRI, kind), sourceSets.toDisplaySourceSets(), styles, extra) - } - - public fun codeBlock( - language: String = "", - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contents += ContentCodeBlock( - contentFor(mainDRI, sourceSets, kind, styles, extra, block).children, - language, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, - extra - ) - } - - public fun codeInline( - language: String = "", - kind: Kind = ContentKind.Main, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contents += ContentCodeInline( - contentFor(mainDRI, sourceSets, kind, styles, extra, block).children, - language, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, - extra - ) - } - - public fun firstParagraphComment( - content: DocTag, - kind: Kind = ContentKind.Comment, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ) { - firstParagraphBrief(content)?.let { brief -> - val builtDescription = commentsConverter.buildContent( - brief, - DCI(mainDRI, kind), - sourceSets - ) - - contents += ContentGroup( - builtDescription, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, - extra - ) - } - } - - public fun firstSentenceComment( - content: DocTag, - kind: Kind = ContentKind.Comment, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ){ - val builtDescription = commentsConverter.buildContent( - content, - DCI(mainDRI, kind), - sourceSets - ) - - contents += ContentGroup( - firstSentenceBriefFromContentNodes(builtDescription), - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, - extra - ) - } - - public fun group( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contents += buildGroup(dri, sourceSets, kind, styles, extra, block) - } - - public fun divergentGroup( - groupID: ContentDivergentGroup.GroupID, - dri: Set<DRI> = mainDRI, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - implicitlySourceSetHinted: Boolean = true, - block: DivergentBuilder.() -> Unit - ) { - contents += - DivergentBuilder(dri, kind, styles, extra) - .apply(block) - .build(groupID = groupID, implicitlySourceSetHinted = implicitlySourceSetHinted) - } - - public fun buildGroup( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ): ContentGroup = contentFor(dri, sourceSets, kind, styles, extra, block) - - public fun sourceSetDependentHint( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contents += PlatformHintedContent( - buildGroup(dri, sourceSets, kind, styles, extra, block), - sourceSets.toDisplaySourceSets() - ) - } - - public fun sourceSetDependentHint( - dri: DRI, - sourcesetData: Set<DokkaSourceSet> = mainSourcesetData, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contents += PlatformHintedContent( - buildGroup(setOf(dri), sourcesetData, kind, styles, extra, block), - sourcesetData.toDisplaySourceSets() - ) - } - - protected fun createText( - text: String, - kind: Kind, - sourceSets: Set<DokkaSourceSet>, - styles: Set<Style>, - extra: PropertyContainer<ContentNode> - ): ContentText { - return ContentText(text, DCI(mainDRI, kind), sourceSets.toDisplaySourceSets(), styles, extra) - } - - public fun <T> sourceSetDependentText( - value: SourceSetDependent<T>, - sourceSets: Set<DokkaSourceSet> = value.keys, - styles: Set<Style> = mainStyles, - transform: (T) -> String - ) { - value.entries - .filter { it.key in sourceSets } - .mapNotNull { (p, v) -> transform(v).takeIf { it.isNotBlank() }?.let { it to p } } - .groupBy({ it.first }) { it.second } - .forEach { text(it.key, sourceSets = it.value.toSet(), styles = styles) } - } - } - - @ContentBuilderMarker - public open inner class TableBuilder( - private val mainDRI: Set<DRI>, - private val mainSourceSets: Set<DokkaSourceSet>, - private val mainKind: Kind, - private val mainStyles: Set<Style>, - private val mainExtra: PropertyContainer<ContentNode> - ) { - private val headerRows: MutableList<ContentGroup> = mutableListOf() - private val rows: MutableList<ContentGroup> = mutableListOf() - private var caption: ContentGroup? = null - - public fun header( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - headerRows += contentFor(dri, sourceSets, kind, styles, extra, block) - } - - public fun row( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - rows += contentFor(dri, sourceSets, kind, styles, extra, block) - } - - public fun caption( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - caption = contentFor(dri, sourceSets, kind, styles, extra, block) - } - - public fun build( - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ): ContentTable { - return ContentTable( - headerRows, - caption, - rows, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, extra - ) - } - } - - @ContentBuilderMarker - public open inner class DivergentBuilder( - private val mainDRI: Set<DRI>, - private val mainKind: Kind, - private val mainStyles: Set<Style>, - private val mainExtra: PropertyContainer<ContentNode> - ) { - private val instances: MutableList<ContentDivergentInstance> = mutableListOf() - - public fun instance( - dri: Set<DRI>, - sourceSets: Set<DokkaSourceSet>, // Having correct sourcesetData is crucial here, that's why there's no default - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DivergentInstanceBuilder.() -> Unit - ) { - instances += DivergentInstanceBuilder(dri, sourceSets, styles, extra) - .apply(block) - .build(kind) - } - - public fun build( - groupID: ContentDivergentGroup.GroupID, - implicitlySourceSetHinted: Boolean, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ): ContentDivergentGroup { - return ContentDivergentGroup( - children = instances.toList(), - dci = DCI(mainDRI, kind), - style = styles, - extra = extra, - groupID = groupID, - implicitlySourceSetHinted = implicitlySourceSetHinted - ) - } - } - - @ContentBuilderMarker - public open inner class DivergentInstanceBuilder( - private val mainDRI: Set<DRI>, - private val mainSourceSets: Set<DokkaSourceSet>, - private val mainStyles: Set<Style>, - private val mainExtra: PropertyContainer<ContentNode> - ) { - private var before: ContentNode? = null - private var divergent: ContentNode? = null - private var after: ContentNode? = null - - public fun before( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contentFor(dri, sourceSets, kind, styles, extra, block) - .takeIf { it.hasAnyContent() } - .also { before = it } - } - - public fun divergent( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - divergent = contentFor(dri, sourceSets, kind, styles, extra, block) - } - - public fun after( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = ContentKind.Main, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contentFor(dri, sourceSets, kind, styles, extra, block) - .takeIf { it.hasAnyContent() } - .also { after = it } - } - - public fun build( - kind: Kind, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ): ContentDivergentInstance { - return ContentDivergentInstance( - before, - divergent ?: throw IllegalStateException("Divergent block needs divergent part"), - after, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, - extra - ) - } - } - - @ContentBuilderMarker - public open inner class ListBuilder( - public val ordered: Boolean, - private val mainDRI: Set<DRI>, - private val mainSourceSets: Set<DokkaSourceSet>, - private val mainKind: Kind, - private val mainStyles: Set<Style>, - private val mainExtra: PropertyContainer<ContentNode> - ) { - private val contentNodes: MutableList<ContentNode> = mutableListOf() - - public fun item( - dri: Set<DRI> = mainDRI, - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra, - block: DocumentableContentBuilder.() -> Unit - ) { - contentNodes += contentFor(dri, sourceSets, kind, styles, extra, block) - } - - public fun build( - sourceSets: Set<DokkaSourceSet> = mainSourceSets, - kind: Kind = mainKind, - styles: Set<Style> = mainStyles, - extra: PropertyContainer<ContentNode> = mainExtra - ): ContentList { - return ContentList( - contentNodes, - ordered, - DCI(mainDRI, kind), - sourceSets.toDisplaySourceSets(), - styles, extra - ) - } - } -} diff --git a/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt b/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt deleted file mode 100644 index a073f73a..00000000 --- a/plugins/base/src/main/kotlin/translators/documentables/briefFromContentNodes.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.translators.documentables - -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.* - -public fun firstParagraphBrief(docTag: DocTag): DocTag? = - when(docTag){ - is P -> docTag - is CustomDocTag -> docTag.children.firstNotNullOfOrNull { firstParagraphBrief(it) } - is Text -> docTag - else -> null - } - -public fun firstSentenceBriefFromContentNodes(description: List<ContentNode>): List<ContentNode> { - 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 as? ContentText)?.isHtml == true } }){ - return description - } - - var sentenceFound = false - fun lookthrough(node: ContentNode, neighbours: List<ContentNode>, currentIndex: Int): ContentNode = - if (node.finishesWithSentenceNotFollowedByHtml(firstSentenceRegex, neighbours, currentIndex) || node.containsSentenceFinish(firstSentenceRegex)) { - node as ContentText - sentenceFound = true - node.copy(text = firstSentenceRegex.find(node.text)?.value.orEmpty()) - } else if (node is ContentGroup) { - node.copy(children = node.children.mapIndexedNotNull { i, element -> - if (!sentenceFound) lookthrough(element, node.children, i) else null - }, style = node.style - TextStyle.Paragraph) - } else { - node - } - return description.mapIndexedNotNull { i, element -> - if (!sentenceFound) lookthrough(element, description, i) else null - } -} - -private fun ContentNode.finishesWithSentenceNotFollowedByHtml(firstSentenceRegex: Regex, neighbours: List<ContentNode>, currentIndex: Int): Boolean = - this is ContentText && !isHtml && matchContainsEnd(this, firstSentenceRegex) && !neighbours.nextElementIsHtml(currentIndex) - -private fun ContentNode.containsSentenceFinish(firstSentenceRegex: Regex): Boolean = - this is ContentText && !isHtml && firstSentenceRegex.containsMatchIn(text) && !matchContainsEnd(this, firstSentenceRegex) - -private fun matchContainsEnd(node: ContentText, regex: Regex): Boolean = - regex.find(node.text)?.let { node.text.endsWith(it.value) } ?: false - -private fun List<ContentNode>.nextElementIsHtml(currentElementIndex: Int): Boolean = - currentElementIndex != lastIndex && get(currentElementIndex + 1).isHtml - -private val ContentNode.isHtml - get() = extra[HtmlContent] != null diff --git a/plugins/base/src/main/kotlin/utils/CollectionExtensions.kt b/plugins/base/src/main/kotlin/utils/CollectionExtensions.kt deleted file mode 100644 index 96a0a039..00000000 --- a/plugins/base/src/main/kotlin/utils/CollectionExtensions.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.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? { - for (element in this) { - val result = transform(element) - if (result != null) { - return result - } - } - return null -} diff --git a/plugins/base/src/main/kotlin/utils/alphabeticalOrder.kt b/plugins/base/src/main/kotlin/utils/alphabeticalOrder.kt deleted file mode 100644 index ed620b34..00000000 --- a/plugins/base/src/main/kotlin/utils/alphabeticalOrder.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -package org.jetbrains.dokka.base.utils - - -/** - * Canonical alphabetical order to sort named elements - */ -internal val canonicalAlphabeticalOrder: Comparator<in String> = String.CASE_INSENSITIVE_ORDER.thenBy { it } |