aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/plugin-base/src/main
diff options
context:
space:
mode:
authorOleg Yukhnevich <whyoleg@gmail.com>2023-11-16 16:58:19 +0200
committerGitHub <noreply@github.com>2023-11-16 16:58:19 +0200
commitf333e425440701e50361f61acc2f9cb2d10fac1a (patch)
tree85fded28ef08dd658bb53f16af2976b89a3b1aa7 /dokka-subprojects/plugin-base/src/main
parent1e126789a92e512b0a426044220043632e7dbf1b (diff)
downloaddokka-f333e425440701e50361f61acc2f9cb2d10fac1a.tar.gz
dokka-f333e425440701e50361f61acc2f9cb2d10fac1a.tar.bz2
dokka-f333e425440701e50361f61acc2f9cb2d10fac1a.zip
Implement custom code block renderers support (#3320)
* multiple custom renderers can be installed to support different languages independently * only language and code properties are provided for extension
Diffstat (limited to 'dokka-subprojects/plugin-base/src/main')
-rw-r--r--dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/DokkaBase.kt8
-rw-r--r--dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlCodeBlockRenderer.kt49
-rw-r--r--dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlRenderer.kt26
3 files changed, 83 insertions, 0 deletions
diff --git a/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/DokkaBase.kt b/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/DokkaBase.kt
index ca86d4d5..6fa4270b 100644
--- a/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/DokkaBase.kt
+++ b/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/DokkaBase.kt
@@ -49,6 +49,14 @@ public class DokkaBase : DokkaPlugin() {
public val outputWriter: ExtensionPoint<OutputWriter> by extensionPoint()
public val htmlPreprocessors: ExtensionPoint<PageTransformer> by extensionPoint()
+ /**
+ * Extension point for providing custom HTML code block renderers.
+ *
+ * This extension point allows overriding the rendering of code blocks in different programming languages.
+ * Multiple renderers can be installed to support different languages independently.
+ */
+ public val htmlCodeBlockRenderers: ExtensionPoint<HtmlCodeBlockRenderer> by extensionPoint()
+
@Deprecated("It is not used anymore")
public val tabSortingStrategy: ExtensionPoint<TabSortingStrategy> by extensionPoint()
public val immediateHtmlCommandConsumer: ExtensionPoint<ImmediateHtmlCommandConsumer> by extensionPoint()
diff --git a/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlCodeBlockRenderer.kt b/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlCodeBlockRenderer.kt
new file mode 100644
index 00000000..29af6f98
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlCodeBlockRenderer.kt
@@ -0,0 +1,49 @@
+/*
+ * 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
+
+/**
+ * Provides an ability to override code blocks rendering differently dependent on the code language.
+ *
+ * Multiple renderers can be installed to support different languages in an independent way.
+ */
+public interface HtmlCodeBlockRenderer {
+
+ /**
+ * Whether this renderer supports rendering Markdown code blocks
+ * for the given [language] explicitly specified in the fenced code block definition,
+ */
+ public fun isApplicableForDefinedLanguage(language: String): Boolean
+
+ /**
+ * Whether this renderer supports rendering Markdown code blocks
+ * for the given [code] when language is not specified in fenced code blocks
+ * or indented code blocks are used.
+ */
+ public fun isApplicableForUndefinedLanguage(code: String): Boolean
+
+ /**
+ * Defines how to render [code] for specified [language] via HTML tags.
+ *
+ * The value of the [language] will be the same as in the input Markdown fenced code block definition.
+ * In the following example [language] = `kotlin` and [code] = `val a`:
+ * ~~~markdown
+ * ```kotlin
+ * val a
+ * ```
+ * ~~~
+ * The value of the [language] will be `null` if language is not specified in the fenced code block definition
+ * or indented code blocks are used.
+ * In the following example [language] = `null` and [code] = `val a`:
+ * ~~~markdown
+ * ```
+ * val a
+ * ```
+ * ~~~
+ */
+ public fun FlowContent.buildCodeBlock(language: String?, code: String)
+}
diff --git a/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlRenderer.kt b/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlRenderer.kt
index 083876d5..e7b77383 100644
--- a/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlRenderer.kt
+++ b/dokka-subprojects/plugin-base/src/main/kotlin/org/jetbrains/dokka/base/renderers/html/HtmlRenderer.kt
@@ -52,6 +52,7 @@ public open class HtmlRenderer(
private var shouldRenderSourceSetTabs: Boolean = false
override val preprocessors: List<PageTransformer> = context.plugin<DokkaBase>().query { htmlPreprocessors }
+ private val customCodeBlockRenderers = context.plugin<DokkaBase>().query { htmlCodeBlockRenderers }
/**
* Tabs themselves are created in HTML plugin since, currently, only HTML format supports them.
@@ -816,6 +817,31 @@ public open class HtmlRenderer(
code: ContentCodeBlock,
pageContext: ContentPage
) {
+ if (customCodeBlockRenderers.isNotEmpty()) {
+ val language = code.language.takeIf(String::isNotBlank)
+ val codeText = buildString {
+ code.children.forEach {
+ when (it) {
+ is ContentText -> append(it.text)
+ is ContentBreakLine -> appendLine()
+ }
+ }
+ }
+
+ // we use first applicable renderer to override rendering
+ val applicableRenderer = when (language) {
+ null -> customCodeBlockRenderers.firstOrNull { it.isApplicableForUndefinedLanguage(codeText) }
+ else -> customCodeBlockRenderers.firstOrNull { it.isApplicableForDefinedLanguage(language) }
+ }
+ if (applicableRenderer != null) {
+ return with(applicableRenderer) {
+ buildCodeBlock(language, codeText)
+ }
+ }
+ }
+
+ // if there are no applicable custom renderers - fall back to default
+
div("sample-container") {
val codeLang = "lang-" + code.language.ifEmpty { "kotlin" }
val stylesWithBlock = code.style + TextStyle.Block + codeLang