diff options
author | Ignat Beresnev <ignat.beresnev@jetbrains.com> | 2022-02-17 14:51:14 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-17 14:51:14 +0300 |
commit | 13b87181219f5a96e499c6bda230f6bcd2ed3bc0 (patch) | |
tree | 7618b9fedeed0b2228017aee8f0754834223c5fc /plugins/base/src/main/kotlin | |
parent | 2372302f4bc3b4bf49beb0d477eebdd9ac99a78f (diff) | |
download | dokka-13b87181219f5a96e499c6bda230f6bcd2ed3bc0.tar.gz dokka-13b87181219f5a96e499c6bda230f6bcd2ed3bc0.tar.bz2 dokka-13b87181219f5a96e499c6bda230f6bcd2ed3bc0.zip |
Custom doctag extension (#2343)
* Add an extension point for rendering custom doc tags
* Iterate over documentable sourcesets when building custom tags
* Extract a nested custom tags brief block into a separate method
* Filter out tag content providers and make since kotlin brief a one-liner
* Add padding to "Since Kotlin" block in brief description
Diffstat (limited to 'plugins/base/src/main/kotlin')
6 files changed, 150 insertions, 24 deletions
diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index a26f6dcd..0443b136 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -29,6 +29,8 @@ import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToP import org.jetbrains.dokka.base.translators.psi.DefaultPsiToDocumentableTranslator import org.jetbrains.dokka.base.generation.SingleModuleGeneration import org.jetbrains.dokka.base.renderers.html.command.consumers.ReplaceVersionsConsumer +import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider +import org.jetbrains.dokka.base.transformers.pages.tags.SinceKotlinTagContentProvider import org.jetbrains.dokka.base.translators.descriptors.DefaultExternalDocumentablesProvider import org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTranslator import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider @@ -41,6 +43,7 @@ class DokkaBase : DokkaPlugin() { val preMergeDocumentableTransformer by extensionPoint<PreMergeDocumentableTransformer>() val pageMergerStrategy by extensionPoint<PageMergerStrategy>() val commentsToContentConverter by extensionPoint<CommentsToContentConverter>() + val customTagContentProvider by extensionPoint<CustomTagContentProvider>() val signatureProvider by extensionPoint<SignatureProvider>() val locationProviderFactory by extensionPoint<LocationProviderFactory>() val externalLocationProviderFactory by extensionPoint<ExternalLocationProviderFactory>() @@ -151,6 +154,10 @@ class DokkaBase : DokkaPlugin() { commentsToContentConverter with DocTagToContentConverter() } + val sinceKotlinTagContentProvider by extending { + customTagContentProvider with SinceKotlinTagContentProvider + } + val pageMerger by extending { CoreExtensions.pageTransformer providing ::PageMerger } diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 2906e8f2..2d8f88a6 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -113,6 +113,7 @@ open class HtmlRenderer( 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() diff --git a/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt b/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt new file mode 100644 index 00000000..e818ef1d --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt @@ -0,0 +1,59 @@ +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. + */ +interface CustomTagContentProvider { + + /** + * Whether this content provider supports given [CustomTagWrapper]. + * + * Tags can be filtered out either by name or by nested [DocTag] type + */ + 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. + */ + 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. + */ + 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 new file mode 100644 index 00000000..c9010421 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt @@ -0,0 +1,34 @@ +package org.jetbrains.dokka.base.transformers.pages.tags + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder +import org.jetbrains.dokka.model.doc.CustomTagWrapper +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.TextStyle + +object SinceKotlinTagContentProvider : CustomTagContentProvider { + + private const val SINCE_KOTLIN_TAG_NAME = "Since Kotlin" + + override fun isApplicable(customTag: CustomTagWrapper) = customTag.name == SINCE_KOTLIN_TAG_NAME + + override fun DocumentableContentBuilder.contentForDescription( + sourceSet: DokkaConfiguration.DokkaSourceSet, + customTag: CustomTagWrapper + ) { + group(sourceSets = setOf(sourceSet), kind = ContentKind.Comment, styles = setOf(TextStyle.Block)) { + header(4, 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()) + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt index 18647207..a385e0e4 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt @@ -4,10 +4,7 @@ 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.configuration -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.plugability.* import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator class DefaultDocumentableToPageTranslator( @@ -16,8 +13,15 @@ class DefaultDocumentableToPageTranslator( 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 logger = context.logger override fun invoke(module: DModule): ModulePageNode = - DefaultPageCreator(configuration, commentsToContentConverter, signatureProvider, logger).pageForModule(module) + DefaultPageCreator( + configuration, + commentsToContentConverter, + signatureProvider, + logger, + customTagContentProviders + ).pageForModule(module) }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt index c16996a0..946d6416 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -19,6 +19,7 @@ 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.transformers.documentables.ClashingDriIdentifier +import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider private typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>> @@ -29,7 +30,8 @@ open class DefaultPageCreator( configuration: DokkaBaseConfiguration?, commentsToContentConverter: CommentsToContentConverter, signatureProvider: SignatureProvider, - val logger: DokkaLogger + val logger: DokkaLogger, + val customTagContentProviders: List<CustomTagContentProvider> = emptyList() ) { protected open val contentBuilder = PageContentBuilder(commentsToContentConverter, signatureProvider, logger) @@ -341,6 +343,23 @@ open class DefaultPageCreator( } } + val customTags = d.customTags + if (customTags.isNotEmpty()) { + group(styles = setOf(TextStyle.Block)) { + platforms.forEach { platform -> + customTags.forEach { (tagName, sourceSetTag) -> + sourceSetTag[platform]?.let { tag -> + customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> + with(provider) { + contentForDescription(platform, tag) + } + } + } + } + } + } + } + val unnamedTags = tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags } .values.flatten().groupBy { it.first }.mapValues { it.value.map { it.second } } if (unnamedTags.isNotEmpty()) { @@ -357,8 +376,6 @@ open class DefaultPageCreator( } } } - - contentForSinceKotlin(d) }.children } @@ -560,21 +577,6 @@ open class DefaultPageCreator( } ?: firstParagraphComment(tag.root) } - protected open fun DocumentableContentBuilder.contentForSinceKotlin(documentable: Documentable) { - documentable.documentation.mapValues { - it.value.children.find { it is CustomTagWrapper && it.name == "Since Kotlin" } as CustomTagWrapper? - }.run { - documentable.sourceSets.forEach { sourceSet -> - this[sourceSet]?.also { tag -> - group(sourceSets = setOf(sourceSet), kind = ContentKind.Comment, styles = setOf(TextStyle.Block)) { - header(4, tag.name) - comment(tag.root) - } - } - } - } - } - protected open fun contentForFunction(f: DFunction) = contentForMember(f) protected open fun contentForProperty(p: DProperty) = contentForMember(p) @@ -681,7 +683,7 @@ open class DefaultPageCreator( } after(extra = PropertyContainer.empty()) { contentForBrief(it) - contentForSinceKotlin(it) + contentForCustomTagsBrief(it) } } } @@ -692,6 +694,22 @@ open class DefaultPageCreator( } } + private fun DocumentableContentBuilder.contentForCustomTagsBrief(documentable: Documentable) { + val customTags = documentable.customTags + if (customTags.isEmpty()) return + + documentable.sourceSets.forEach { sourceSet -> + customTags.forEach { (tagName, sourceSetTag) -> + sourceSetTag[sourceSet]?.let { tag -> + customTagContentProviders.filter { it.isApplicable(tag) }.forEach { provider -> + with(provider) { + contentForBrief(sourceSet, tag) + } + } + } + } + } + } protected open fun TagWrapper.toHeaderString() = this.javaClass.toGenericString().split('.').last() @@ -706,6 +724,9 @@ open class DefaultPageCreator( private val Documentable.descriptions: SourceSetDependent<Description> get() = groupedTags.withTypeUnnamed<Description>() + private val Documentable.customTags: Map<String, SourceSetDependent<CustomTagWrapper>> + get() = groupedTags.withTypeNamed() + private val Documentable.hasSeparatePage: Boolean get() = this !is DTypeAlias |