aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/main/kotlin
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2022-02-17 14:51:14 +0300
committerGitHub <noreply@github.com>2022-02-17 14:51:14 +0300
commit13b87181219f5a96e499c6bda230f6bcd2ed3bc0 (patch)
tree7618b9fedeed0b2228017aee8f0754834223c5fc /plugins/base/src/main/kotlin
parent2372302f4bc3b4bf49beb0d477eebdd9ac99a78f (diff)
downloaddokka-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')
-rw-r--r--plugins/base/src/main/kotlin/DokkaBase.kt7
-rw-r--r--plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt1
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/tags/CustomTagContentProvider.kt59
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/tags/SinceKotlinTagContentProvider.kt34
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt14
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt59
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