aboutsummaryrefslogtreecommitdiff
path: root/plugins/gfm/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/gfm/src')
-rw-r--r--plugins/gfm/src/main/kotlin/GfmPlugin.kt345
-rw-r--r--plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin1
-rw-r--r--plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt374
-rw-r--r--plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt32
-rw-r--r--plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt76
-rw-r--r--plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt70
-rw-r--r--plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt137
7 files changed, 1035 insertions, 0 deletions
diff --git a/plugins/gfm/src/main/kotlin/GfmPlugin.kt b/plugins/gfm/src/main/kotlin/GfmPlugin.kt
new file mode 100644
index 00000000..dcc9c0a6
--- /dev/null
+++ b/plugins/gfm/src/main/kotlin/GfmPlugin.kt
@@ -0,0 +1,345 @@
+package org.jetbrains.dokka.gfm
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.renderers.DefaultRenderer
+import org.jetbrains.dokka.base.renderers.PackageListCreator
+import org.jetbrains.dokka.base.renderers.RootCreator
+import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProvider
+import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.query
+import org.jetbrains.dokka.transformers.pages.PageTransformer
+
+class GfmPlugin : DokkaPlugin() {
+
+ val gfmPreprocessors by extensionPoint<PageTransformer>()
+
+ private val dokkaBase by lazy { plugin<DokkaBase>() }
+
+ val renderer by extending {
+ (CoreExtensions.renderer
+ providing { CommonmarkRenderer(it) }
+ override dokkaBase.htmlRenderer)
+ }
+
+ val locationProvider by extending {
+ (dokkaBase.locationProviderFactory
+ providing { MarkdownLocationProviderFactory(it) }
+ override dokkaBase.locationProvider)
+ }
+
+ val rootCreator by extending {
+ gfmPreprocessors with RootCreator
+ }
+
+ val packageListCreator by extending {
+ (gfmPreprocessors
+ providing { PackageListCreator(it, "gfm", "md") }
+ order { after(rootCreator) })
+ }
+}
+
+open class CommonmarkRenderer(
+ context: DokkaContext
+) : DefaultRenderer<StringBuilder>(context) {
+
+ override val preprocessors = context.plugin<GfmPlugin>().query { gfmPreprocessors }
+
+ override fun StringBuilder.wrapGroup(
+ node: ContentGroup,
+ pageContext: ContentPage,
+ childrenCallback: StringBuilder.() -> Unit
+ ) {
+ return when {
+ node.hasStyle(TextStyle.Block) -> {
+ childrenCallback()
+ buildNewLine()
+ }
+ node.hasStyle(TextStyle.Paragraph) -> {
+ buildParagraph()
+ childrenCallback()
+ buildParagraph()
+ }
+ else -> childrenCallback()
+ }
+ }
+
+ override fun StringBuilder.buildHeader(level: Int, node: ContentHeader, content: StringBuilder.() -> Unit) {
+ buildParagraph()
+ append("#".repeat(level) + " ")
+ content()
+ buildNewLine()
+ }
+
+ override fun StringBuilder.buildLink(address: String, content: StringBuilder.() -> Unit) {
+ append("[")
+ content()
+ append("]($address)")
+ }
+
+ override fun StringBuilder.buildList(
+ node: ContentList,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>?
+ ) {
+ buildListLevel(node, pageContext)
+ }
+
+ private fun StringBuilder.buildListItem(items: List<ContentNode>, pageContext: ContentPage) {
+ items.forEach {
+ if (it is ContentList) {
+ buildList(it, pageContext)
+ } else {
+ append("<li>")
+ append(buildString { it.build(this, pageContext, it.sourceSets) }.trim())
+ append("</li>")
+ }
+ }
+ }
+
+ private fun StringBuilder.buildListLevel(node: ContentList, pageContext: ContentPage) {
+ if (node.ordered) {
+ append("<ol>")
+ buildListItem(node.children, pageContext)
+ append("</ol>")
+ } else {
+ append("<ul>")
+ buildListItem(node.children, pageContext)
+ append("</ul>")
+ }
+ }
+
+ override fun StringBuilder.buildNewLine() {
+ append(" \n")
+ }
+
+ private fun StringBuilder.buildParagraph() {
+ append("\n\n")
+ }
+
+ override fun StringBuilder.buildPlatformDependent(
+ content: PlatformHintedContent,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>?
+ ) {
+ buildPlatformDependentItem(content.inner, content.sourceSets, pageContext)
+ }
+
+ private fun StringBuilder.buildPlatformDependentItem(
+ content: ContentNode,
+ sourceSets: Set<DokkaSourceSet>,
+ pageContext: ContentPage,
+ ) {
+ if (content is ContentGroup && content.children.firstOrNull { it is ContentTable } != null) {
+ buildContentNode(content, pageContext, sourceSets)
+ } else {
+ val distinct = sourceSets.map {
+ it to buildString { buildContentNode(content, pageContext, setOf(it)) }
+ }.groupBy(Pair<DokkaSourceSet, String>::second, Pair<DokkaSourceSet, String>::first)
+
+ distinct.filter { it.key.isNotBlank() }.forEach { (text, platforms) ->
+ append(" ")
+ buildSourceSetTags(platforms.toSet())
+ append(" $text ")
+ buildNewLine()
+ }
+ }
+ }
+
+ override fun StringBuilder.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage) {
+ append("Resource")
+ }
+
+ override fun StringBuilder.buildTable(
+ node: ContentTable,
+ pageContext: ContentPage,
+ sourceSetRestriction: Set<DokkaSourceSet>?
+ ) {
+ buildNewLine()
+ if (node.dci.kind == ContentKind.Sample || node.dci.kind == ContentKind.Parameters) {
+ node.sourceSets.forEach { sourcesetData ->
+ append(sourcesetData.displayName)
+ buildNewLine()
+ buildTable(
+ node.copy(
+ children = node.children.filter { it.sourceSets.contains(sourcesetData) },
+ dci = node.dci.copy(kind = ContentKind.Main)
+ ), pageContext, sourceSetRestriction
+ )
+ buildNewLine()
+ }
+ } else {
+ val size = node.header.size
+
+ if (node.header.isNotEmpty()) {
+ append("| ")
+ node.header.forEach {
+ it.children.forEach {
+ append(" ")
+ it.build(this, pageContext, it.sourceSets)
+ }
+ append("| ")
+ }
+ append("\n")
+ } else {
+ append("| ".repeat(size))
+ if (size > 0) append("|\n")
+ }
+
+ append("|---".repeat(size))
+ if (size > 0) append("|\n")
+
+ node.children.forEach {
+ val builder = StringBuilder()
+ it.children.forEach {
+ builder.append("| ")
+ builder.append(buildString { it.build(this, pageContext) }.replace(Regex("#+ "), "") ) // Workaround for headers inside tables
+ }
+ append(builder.toString().withEntersAsHtml())
+ append(" | ".repeat(size - it.children.size))
+ append("\n")
+ }
+ }
+ }
+
+ override fun StringBuilder.buildText(textNode: ContentText) {
+ if(textNode.text.isNotBlank()) {
+ val decorators = decorators(textNode.style)
+ append(textNode.text.takeWhile { it == ' ' } )
+ append(decorators)
+ append(textNode.text.trim())
+ append(decorators.reversed())
+ append(textNode.text.takeLastWhile { it == ' ' })
+ }
+ }
+
+ override fun StringBuilder.buildNavigation(page: PageNode) {
+ locationProvider.ancestors(page).asReversed().forEach { node ->
+ append("/")
+ if (node.isNavigable) buildLink(node, page)
+ else append(node.name)
+ }
+ buildParagraph()
+ }
+
+ override fun buildPage(page: ContentPage, content: (StringBuilder, ContentPage) -> Unit): String =
+ buildString {
+ content(this, page)
+ }
+
+ override fun buildError(node: ContentNode) {
+ context.logger.warn("Markdown renderer has encountered problem. The unmatched node is $node")
+ }
+
+ override fun StringBuilder.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) {
+
+ val distinct =
+ node.groupDivergentInstances(pageContext, { instance, contentPage, sourceSet ->
+ instance.before?.let { before ->
+ buildString { buildContentNode(before, pageContext, setOf(sourceSet)) }
+ } ?: ""
+ }, { instance, contentPage, sourceSet ->
+ instance.after?.let { after ->
+ buildString { buildContentNode(after, pageContext, setOf(sourceSet)) }
+ } ?: ""
+ })
+
+ distinct.values.forEach { entry ->
+ val (instance, sourceSets) = entry.getInstanceAndSourceSets()
+
+ buildSourceSetTags(sourceSets)
+ buildNewLine()
+ instance.before?.let {
+ append("Brief description")
+ buildNewLine()
+ buildContentNode(it, pageContext, setOf(sourceSets.first())) // It's workaround to render content only once
+ buildNewLine()
+ }
+
+ append("Content")
+ buildNewLine()
+ entry.groupBy { buildString { buildContentNode(it.first.divergent, pageContext, setOf(it.second)) } }
+ .values.forEach { innerEntry ->
+ val (innerInstance, innerSourceSets) = innerEntry.getInstanceAndSourceSets()
+ if(sourceSets.size > 1) {
+ buildSourceSetTags(innerSourceSets)
+ buildNewLine()
+ }
+ innerInstance.divergent.build(this@buildDivergent, pageContext, setOf(innerSourceSets.first())) // It's workaround to render content only once
+ buildNewLine()
+ }
+
+ instance.after?.let {
+ append("More info")
+ buildNewLine()
+ buildContentNode(it, pageContext, setOf(sourceSets.first())) // It's workaround to render content only once
+ buildNewLine()
+ }
+
+ buildParagraph()
+ }
+ }
+
+ private fun decorators(styles: Set<Style>) = buildString {
+ styles.forEach {
+ when (it) {
+ TextStyle.Bold -> append("**")
+ TextStyle.Italic -> append("*")
+ TextStyle.Strong -> append("**")
+ TextStyle.Strikethrough -> append("~~")
+ else -> Unit
+ }
+ }
+ }
+
+ private val PageNode.isNavigable: Boolean
+ get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing
+
+ private fun StringBuilder.buildLink(to: PageNode, from: PageNode) =
+ buildLink(locationProvider.resolve(to, from)) {
+ append(to.name)
+ }
+
+ override suspend fun renderPage(page: PageNode) {
+ val path by lazy { locationProvider.resolve(page, skipExtension = true) }
+ when (page) {
+ is ContentPage -> outputWriter.write(path, buildPage(page) { c, p -> buildPageContent(c, p) }, ".md")
+ 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), ".md")
+ RenderingStrategy.DoNothing -> Unit
+ }
+ else -> throw AssertionError(
+ "Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content"
+ )
+ }
+ }
+
+ private fun String.withEntersAsHtml(): String = replace("\n", "<br>")
+
+ private fun List<Pair<ContentDivergentInstance, DokkaSourceSet>>.getInstanceAndSourceSets() = this.let { Pair(it.first().first, it.map { it.second }.toSet()) }
+
+ private fun StringBuilder.buildSourceSetTags(sourceSets: Set<DokkaSourceSet>) =
+ append(sourceSets.joinToString(prefix = "[", postfix = "]") { it.displayName })
+}
+
+class MarkdownLocationProviderFactory(val context: DokkaContext) : LocationProviderFactory {
+
+ override fun getLocationProvider(pageNode: RootPageNode) = MarkdownLocationProvider(pageNode, context)
+}
+
+class MarkdownLocationProvider(
+ pageGraphRoot: RootPageNode,
+ dokkaContext: DokkaContext
+) : DefaultLocationProvider(
+ pageGraphRoot,
+ dokkaContext
+) {
+ override val extension = ".md"
+}
diff --git a/plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
new file mode 100644
index 00000000..ae291d68
--- /dev/null
+++ b/plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
@@ -0,0 +1 @@
+org.jetbrains.dokka.gfm.GfmPlugin
diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt
new file mode 100644
index 00000000..0c8b942e
--- /dev/null
+++ b/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt
@@ -0,0 +1,374 @@
+package renderers.gfm
+
+import org.jetbrains.dokka.DokkaSourceSetID
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.SourceRootImpl
+import org.jetbrains.dokka.gfm.CommonmarkRenderer
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.ContentDivergentGroup
+import org.junit.jupiter.api.Test
+import renderers.defaultSourceSet
+import renderers.TestPage
+
+class DivergentTest : GfmRenderingOnlyTestBase() {
+ private val js = defaultSourceSet.copy(
+ "root",
+ "js",
+ DokkaSourceSetID("root", "js"),
+ analysisPlatform = Platform.js,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ private val jvm = defaultSourceSet.copy(
+ "root",
+ "jvm",
+ DokkaSourceSetID("root", "jvm"),
+ analysisPlatform = Platform.jvm,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ private val native = defaultSourceSet.copy(
+ "root",
+ "native",
+ DokkaSourceSetID("root", "native"),
+ analysisPlatform = Platform.native,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+
+ @Test
+ fun simpleWrappingCase() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[js] \nContent \na \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun noPlatformHintCase() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test"), implicitlySourceSetHinted = false) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[js] \nContent \na \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentBetweenSourceSets() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("c")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[js, jvm, native] \nContent \n[js] \na \n[jvm] \nb \n[native] \nc \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentInOneSourceSet() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(js)) {
+ divergent {
+ text("c")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[js] \nContent \na \nb \nc \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSets() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native, js, jvm] \nContent \n[native] \na \n[js] \nb \n[jvm] \nc \n[js] \nd \n[native] \ne \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSetsWithGrouping() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("a+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ after {
+ text("e+")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native] \nContent \na \nMore info \na+ \n\n\n[js] \nContent \nb \nd \nMore info \nbd+ \n\n\n[jvm] \nContent \nc \n\n\n[native] \nContent \ne \nMore info \ne+ \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentSameBefore() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("b")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native] \nBrief description \nab- \nContent \na \nb \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentSameAfter() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native] \nContent \na \nb \nMore info \nab+ \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentGroupedByBeforeAndAfter() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native] \nBrief description \nab- \nContent \na \nb \nMore info \nab+ \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentDifferentBeforeAndAfter() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("a-")
+ }
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("b-")
+ }
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native] \nBrief description \na- \nContent \na \nMore info \nab+ \n\n\n[native] \nBrief description \nb- \nContent \nb \nMore info \nab+ \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSetsWithGroupingAncCommonParts() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("a+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ after {
+ text("e+")
+ }
+ }
+ }
+ }
+ val expect = "//[testPage](test-page.md)\n\n[native] \nContent \na \nMore info \na+ \n\n\n[js, jvm] \nContent \n[js] \nb \n[jvm] \nc \n[js] \nd \nMore info \nbd+ \n\n\n[native] \nContent \ne \nMore info \ne+ \n\n\n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+} \ No newline at end of file
diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt
new file mode 100644
index 00000000..6d0dd3a6
--- /dev/null
+++ b/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt
@@ -0,0 +1,32 @@
+package renderers.gfm
+
+import org.jetbrains.dokka.DokkaConfigurationImpl
+import org.jetbrains.dokka.gfm.GfmPlugin
+import org.jetbrains.dokka.gfm.MarkdownLocationProviderFactory
+import org.jetbrains.dokka.testApi.context.MockContext
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.renderers.RootCreator
+import org.jetbrains.dokka.base.resolvers.external.DokkaExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.external.JavadocExternalLocationProviderFactory
+import renderers.RenderingOnlyTestBase
+import utils.TestOutputWriter
+
+abstract class GfmRenderingOnlyTestBase : RenderingOnlyTestBase<String>() {
+
+ val files = TestOutputWriter()
+ override val context = MockContext(
+ DokkaBase().outputWriter to { _ -> files },
+ DokkaBase().locationProviderFactory to ::MarkdownLocationProviderFactory,
+ DokkaBase().externalLocationProviderFactory to { ::JavadocExternalLocationProviderFactory },
+ DokkaBase().externalLocationProviderFactory to { ::DokkaExternalLocationProviderFactory },
+ GfmPlugin().gfmPreprocessors to { _ -> RootCreator },
+
+ testConfiguration = DokkaConfigurationImpl(
+ "", null, false, emptyList(), emptyList(), emptyMap(), emptyList(), false
+ )
+ )
+
+ override val renderedContent: String by lazy {
+ files.contents.getValue("test-page.md")
+ }
+}
diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt
new file mode 100644
index 00000000..42839282
--- /dev/null
+++ b/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt
@@ -0,0 +1,76 @@
+package renderers.gfm
+
+import org.jetbrains.dokka.gfm.CommonmarkRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import org.junit.jupiter.api.Test
+import renderers.*
+
+class GroupWrappingTest : GfmRenderingOnlyTestBase() {
+
+ @Test
+ fun notWrapped() {
+ val page = TestPage {
+ group {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ CommonmarkRenderer(context).render(page)
+
+ assert(renderedContent == "//[testPage](test-page.md)\n\nabc")
+ }
+
+ @Test
+ fun paragraphWrapped() {
+ val page = TestPage {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ CommonmarkRenderer(context).render(page)
+
+ assert(renderedContent == "//[testPage](test-page.md)\n\n\n\nab\n\nc")
+ }
+
+ @Test
+ fun blockWrapped() {
+ val page = TestPage {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ CommonmarkRenderer(context).render(page)
+
+ assert(renderedContent == "//[testPage](test-page.md)\n\nab \nc")
+ }
+
+ @Test
+ fun nested() {
+ val page = TestPage {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a")
+ group(styles = setOf(TextStyle.Block)) {
+ group(styles = setOf(TextStyle.Block)) {
+ text("b")
+ text("c")
+ }
+ }
+ text("d")
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+
+// renderedContent.match(Div("a", Div(Div("bc")), "d"))
+ assert(renderedContent == "//[testPage](test-page.md)\n\nabc \n \nd \n")
+ }
+
+}
diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt
new file mode 100644
index 00000000..7464c079
--- /dev/null
+++ b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt
@@ -0,0 +1,70 @@
+package renderers.gfm
+
+import org.jetbrains.dokka.gfm.CommonmarkRenderer
+import org.junit.jupiter.api.Test
+import renderers.TestPage
+import org.jetbrains.dokka.base.translators.documentables.*
+import org.jetbrains.dokka.pages.TextStyle
+
+class SimpleElementsTest : GfmRenderingOnlyTestBase() {
+
+ @Test
+ fun header() {
+ val page = TestPage {
+ header(1, "The Hobbit or There and Back Again")
+ }
+ val expect = "//[testPage](test-page.md)\n\n\n\n# The Hobbit or There and Back Again \n"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun link() {
+ val page = TestPage {
+ link("They are not all accounted for, the lost Seeing Stones.", "http://www.google.com")
+ }
+ val expect = "//[testPage](test-page.md)\n\n[They are not all accounted for, the lost Seeing Stones.](http://www.google.com)"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun bold() {
+ val page = TestPage {
+ text("That there’s some good in this world, Mr. Frodo… and it’s worth fighting for.", styles = setOf(TextStyle.Bold))
+ }
+ val expect = "//[testPage](test-page.md)\n\n**That there’s some good in this world, Mr. Frodo… and it’s worth fighting for.**"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun italic() {
+ val page = TestPage {
+ text("Even the smallest person can change the course of the future.", styles = setOf(TextStyle.Italic))
+ }
+ val expect = "//[testPage](test-page.md)\n\n*Even the smallest person can change the course of the future.*"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun italicAndBold() {
+ val page = TestPage {
+ text("There is no curse in Elvish, Entish, or the tongues of Men for this treachery.", styles = setOf(TextStyle.Bold, TextStyle.Italic))
+ }
+ val expect = "//[testPage](test-page.md)\n\n***There is no curse in Elvish, Entish, or the tongues of Men for this treachery.***"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+
+ @Test
+ fun strikethrough() {
+ val page = TestPage {
+ text("A day may come when the courage of men fails… but it is not THIS day", styles = setOf(TextStyle.Strikethrough))
+ }
+ val expect = "//[testPage](test-page.md)\n\n~~A day may come when the courage of men fails… but it is not THIS day~~"
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == expect)
+ }
+} \ No newline at end of file
diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt
new file mode 100644
index 00000000..e181e3a2
--- /dev/null
+++ b/plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt
@@ -0,0 +1,137 @@
+package renderers.gfm
+
+import org.jetbrains.dokka.DokkaSourceSetID
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.SourceRootImpl
+import org.jetbrains.dokka.gfm.CommonmarkRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import org.junit.jupiter.api.Test
+import renderers.TestPage
+import renderers.defaultSourceSet
+
+class SourceSetDependentHintTest : GfmRenderingOnlyTestBase() {
+
+ private val pl1 = defaultSourceSet.copy(
+ "root",
+ "pl1",
+ DokkaSourceSetID("root", "pl1"),
+ analysisPlatform = Platform.js,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ private val pl2 = defaultSourceSet.copy(
+ "root",
+ "pl2",
+ DokkaSourceSetID("root", "pl2"),
+ analysisPlatform = Platform.jvm,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ private val pl3 = defaultSourceSet.copy(
+ "root",
+ "pl3",
+ DokkaSourceSetID("root", "pl3"),
+ analysisPlatform = Platform.native,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+
+ @Test
+ fun platformIndependentCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a")
+ text("b")
+ text("c")
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2, pl3] abc \n \n")
+ }
+
+ @Test
+ fun completelyDivergentCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl2))
+ text("c", sourceSets = setOf(pl3))
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1] a \n \n [pl2] b \n \n [pl3] c \n \n")
+ }
+
+ @Test
+ fun overlappingCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl1, pl2))
+ text("c", sourceSets = setOf(pl2))
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1] ab \n \n [pl2] bc \n \n")
+ }
+
+ @Test
+ fun caseThatCanBeSimplified() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2] ab \n \n")
+ }
+
+ @Test
+ fun caseWithGroupBreakingSimplification() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ }
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1] ab \n \n \n [pl2] a \nb \n \n")
+ }
+
+ @Test
+ fun caseWithGroupNotBreakingSimplification() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2)) {
+ group {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ }
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2] ab \n")
+ }
+
+ @Test
+ fun partiallyUnifiedCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("a", sourceSets = setOf(pl2))
+ text("b", sourceSets = setOf(pl3))
+ }
+ }
+
+ CommonmarkRenderer(context).render(page)
+ assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2] a \n \n [pl3] b \n \n")
+ }
+} \ No newline at end of file