diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2020-03-03 23:38:23 +0100 |
---|---|---|
committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-03-04 14:28:14 +0100 |
commit | d08591dea709917553c0d6de024820bb29b328fb (patch) | |
tree | 5844f98629d654ffb4fbaa0471484b149b487361 /plugins/base/src | |
parent | 156396f7520b3ac45e37068d6b72087008322cbe (diff) | |
download | dokka-d08591dea709917553c0d6de024820bb29b328fb.tar.gz dokka-d08591dea709917553c0d6de024820bb29b328fb.tar.bz2 dokka-d08591dea709917553c0d6de024820bb29b328fb.zip |
Platform dependent hints for renderer
Diffstat (limited to 'plugins/base/src')
6 files changed, 313 insertions, 130 deletions
diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt index 960a39fd..33be5dfe 100644 --- a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt @@ -22,54 +22,68 @@ abstract class DefaultRenderer<T>( abstract fun T.buildHeader(level: Int, content: T.() -> Unit) abstract fun T.buildLink(address: String, content: T.() -> Unit) - abstract fun T.buildList(node: ContentList, pageContext: ContentPage) + abstract fun T.buildList(node: ContentList, pageContext: ContentPage, platformRestriction: PlatformData? = null) abstract fun T.buildNewLine() abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage) - abstract fun T.buildTable(node: ContentTable, pageContext: ContentPage) + abstract fun T.buildTable(node: ContentTable, pageContext: ContentPage, platformRestriction: PlatformData? = null) abstract fun T.buildText(textNode: ContentText) abstract fun T.buildNavigation(page: PageNode) abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String abstract fun buildError(node: ContentNode) - open fun T.buildGroup(node: ContentGroup, pageContext: ContentPage) = wrapGroup(node, pageContext) { - node.children.forEach { it.build(this, pageContext) } - } + open fun T.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) = + buildContentNode(content.inner, pageContext) + + open fun T.buildGroup(node: ContentGroup, pageContext: ContentPage, platformRestriction: PlatformData? = null) = + wrapGroup(node, pageContext) { node.children.forEach { it.build(this, pageContext, platformRestriction) } } open fun T.wrapGroup(node: ContentGroup, pageContext: ContentPage, childrenCallback: T.() -> Unit) = childrenCallback() - open fun T.buildLinkText(nodes: List<ContentNode>, pageContext: ContentPage) { - nodes.forEach { it.build(this, pageContext) } + open fun T.buildLinkText( + nodes: List<ContentNode>, + pageContext: ContentPage, + platformRestriction: PlatformData? = null + ) { + nodes.forEach { it.build(this, pageContext, platformRestriction) } } open fun T.buildCode(code: List<ContentNode>, language: String, pageContext: ContentPage) { code.forEach { it.build(this, pageContext) } } - open fun T.buildHeader(node: ContentHeader, pageContext: ContentPage) { - buildHeader(node.level) { node.children.forEach { it.build(this, pageContext) } } + open fun T.buildHeader(node: ContentHeader, pageContext: ContentPage, platformRestriction: PlatformData? = null) { + buildHeader(node.level) { node.children.forEach { it.build(this, pageContext, platformRestriction) } } } - open fun ContentNode.build(builder: T, pageContext: ContentPage) = - builder.buildContentNode(this, pageContext) - - open fun T.buildContentNode(node: ContentNode, pageContext: ContentPage) { - when (node) { - is ContentText -> buildText(node) - is ContentHeader -> buildHeader(node, pageContext) - is ContentCode -> buildCode(node.children, node.language, pageContext) - is ContentDRILink -> buildLink( - locationProvider.resolve(node.address, node.platforms.toList(), pageContext) - ) { - buildLinkText(node.children, pageContext) + open fun ContentNode.build(builder: T, pageContext: ContentPage, platformRestriction: PlatformData? = null) = + builder.buildContentNode(this, pageContext, platformRestriction) + + open fun T.buildContentNode( + node: ContentNode, + pageContext: ContentPage, + platformRestriction: PlatformData? = null + ) { + if (platformRestriction == null || platformRestriction in node.platforms) { + when (node) { + is ContentText -> buildText(node) + is ContentHeader -> buildHeader(node, pageContext, platformRestriction) + is ContentCode -> buildCode(node.children, node.language, pageContext) + is ContentDRILink -> + buildLink(locationProvider.resolve(node.address, node.platforms.toList(), pageContext)) { + buildLinkText(node.children, pageContext, platformRestriction) + } + is ContentResolvedLink -> buildLink(node.address) { + buildLinkText(node.children, pageContext, platformRestriction) + } + is ContentEmbeddedResource -> buildResource(node, pageContext) + is ContentList -> buildList(node, pageContext, platformRestriction) + is ContentTable -> buildTable(node, pageContext, platformRestriction) + is ContentGroup -> buildGroup(node, pageContext, platformRestriction) + is PlatformHintedContent -> buildPlatformDependent(node, pageContext) + else -> buildError(node) } - is ContentResolvedLink -> buildLink(node.address) { buildLinkText(node.children, pageContext) } - is ContentEmbeddedResource -> buildResource(node, pageContext) - is ContentList -> buildList(node, pageContext) - is ContentTable -> buildTable(node, pageContext) - is ContentGroup -> buildGroup(node, pageContext) - else -> buildError(node) } } diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index dcd65c21..0dd3b34b 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -33,24 +33,46 @@ open class HtmlRenderer( else -> childrenCallback() } - override fun FlowContent.buildList(node: ContentList, pageContext: ContentPage) = - if (node.ordered) ol { - buildListItems(node.children, pageContext) - } - else ul { - buildListItems(node.children, pageContext) - } + override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) { + val distinct = content.platforms.map { + it to createHTML(prettyPrint = false).div { + buildContentNode(content.inner, pageContext, it) + }.drop(5).dropLast(6) // TODO: Find a way to do it without arbitrary trims + }.groupBy(Pair<PlatformData, String>::second, Pair<PlatformData, String>::first) + + if (distinct.size == 1) + consumer.onTagContentUnsafe { +distinct.keys.single() } + else + distinct.forEach { text, platforms -> + consumer.onTagContentUnsafe { +platforms.joinToString(prefix = "$text [", postfix = "]") { it.name } } + } + } + + override fun FlowContent.buildList( + node: ContentList, + pageContext: ContentPage, + platformRestriction: PlatformData? + ) = if (node.ordered) ol { buildListItems(node.children, pageContext, platformRestriction) } + else ul { buildListItems(node.children, pageContext, platformRestriction) } - open fun OL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) { + open fun OL.buildListItems( + items: List<ContentNode>, + pageContext: ContentPage, + platformRestriction: PlatformData? = null + ) { items.forEach { if (it is ContentList) buildList(it, pageContext) else - li { it.build(this, pageContext) } + li { it.build(this, pageContext, platformRestriction) } } } - open fun UL.buildListItems(items: List<ContentNode>, pageContext: ContentPage) { + open fun UL.buildListItems( + items: List<ContentNode>, + pageContext: ContentPage, + platformRestriction: PlatformData? = null + ) { items.forEach { if (it is ContentList) buildList(it, pageContext) @@ -73,14 +95,18 @@ open class HtmlRenderer( } } - override fun FlowContent.buildTable(node: ContentTable, pageContext: ContentPage) { + override fun FlowContent.buildTable( + node: ContentTable, + pageContext: ContentPage, + platformRestriction: PlatformData? + ) { table { thead { node.header.forEach { tr { it.children.forEach { th { - it.build(this@table, pageContext) + it.build(this@table, pageContext, platformRestriction) } } } @@ -91,7 +117,7 @@ open class HtmlRenderer( tr { it.children.forEach { td { - it.build(this, pageContext) + it.build(this, pageContext, platformRestriction) } } } @@ -141,7 +167,11 @@ open class HtmlRenderer( override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) = a(href = address, block = content) - override fun FlowContent.buildCode(code: List<ContentNode>, language: String, pageContext: ContentPage) { + override fun FlowContent.buildCode( + code: List<ContentNode>, + language: String, + pageContext: ContentPage + ) { buildNewLine() code.forEach { +((it as? ContentText)?.text ?: run { context.logger.error("Cannot cast $it as ContentText!"); "" }) diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index eb422920..0c08241f 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -232,6 +232,20 @@ open class PageContentBuilder( block: DocumentableContentBuilder.() -> Unit ): ContentGroup = contentFor(dri, platformData, kind, styles, extras, block) + fun platformDependentHint( + dri: DRI = mainDRI, + platformData: Set<PlatformData> = mainPlatformData, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extras: Set<Extra> = mainExtras, + block: DocumentableContentBuilder.() -> Unit + ) { + contents += PlatformHintedContent( + buildGroup(dri, platformData, kind, styles, extras, block), + platformData + ) + } + protected fun createText( text: String, kind: Kind, diff --git a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt new file mode 100644 index 00000000..9f148369 --- /dev/null +++ b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt @@ -0,0 +1,88 @@ +package renderers + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.resolvers.DefaultLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.LocationProvider +import org.jetbrains.dokka.base.resolvers.LocationProviderFactory +import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.testApi.context.MockContext +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import utils.TestOutputWriter + +abstract class RenderingOnlyTestBase { + val files = TestOutputWriter() + val context = MockContext( + DokkaBase().outputWriter to { _ -> files }, + DokkaBase().locationProviderFactory to ::DefaultLocationProviderFactory + ) + + protected fun linesAfterContentTag() = + files.contents.getValue("test-page.html").lines() + .dropWhile { !it.contains("""<div id="content">""") } + .joinToString(separator = "") { it.trim() } + +} + +class TestPage(callback: PageContentBuilder.DocumentableContentBuilder.() -> Unit): RootPageNode(), ContentPage { + override val dri: Set<DRI> = setOf(DRI.topLevel) + override val documentable: Documentable? = null + override val embeddedResources: List<String> = emptyList() + override val name: String + get() = "testPage" + override val children: List<PageNode> + get() = emptyList() + + override val content: ContentNode = PageContentBuilder( + EmptyCommentConverter, + KotlinSignatureProvider(EmptyCommentConverter, DokkaConsoleLogger), + DokkaConsoleLogger + ).contentFor( + DRI.topLevel, + emptySet(), + block = callback + ) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ) = this + + override fun modified(name: String, children: List<PageNode>) = this +} + + +internal object EmptyCommentConverter : CommentsToContentConverter { + override fun buildContent( + docTag: DocTag, + dci: DCI, + platforms: Set<PlatformData>, + styles: Set<Style>, + extras: Set<Extra> + ): List<ContentNode> = emptyList() +} + +internal object EmptyLocationProviderFactory: LocationProviderFactory { + override fun getLocationProvider(pageNode: RootPageNode) = object : LocationProvider { + override fun resolve(dri: DRI, platforms: List<PlatformData>, context: PageNode?): String = "" + + override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = node.name + + override fun resolveRoot(node: PageNode): String { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun ancestors(node: PageNode): List<PageNode> { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt index 7fac6450..e98b97c0 100644 --- a/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt +++ b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt @@ -1,33 +1,16 @@ package renderers.html -import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.renderers.html.HtmlRenderer -import org.jetbrains.dokka.base.resolvers.DefaultLocationProviderFactory -import org.jetbrains.dokka.base.resolvers.LocationProvider -import org.jetbrains.dokka.base.resolvers.LocationProviderFactory -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.Documentable -import org.jetbrains.dokka.model.doc.DocTag -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.testApi.context.MockContext -import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.jetbrains.dokka.pages.TextStyle import org.junit.Test -import utils.TestOutputWriter +import renderers.RenderingOnlyTestBase +import renderers.TestPage -class GroupWrappingTest { - - val files = TestOutputWriter() - val context = MockContext( - DokkaBase().outputWriter to { _ -> files }, - DokkaBase().locationProviderFactory to ::DefaultLocationProviderFactory - ) +class GroupWrappingTest: RenderingOnlyTestBase() { @Test fun notWrapped() { - - val page = createPage { + val page = TestPage { group { text("a") text("b") @@ -42,8 +25,7 @@ class GroupWrappingTest { @Test fun paragraphWrapped() { - - val page = createPage { + val page = TestPage { group(styles = setOf(TextStyle.Paragraph)) { text("a") text("b") @@ -58,8 +40,7 @@ class GroupWrappingTest { @Test fun blockWrapped() { - - val page = createPage { + val page = TestPage { group(styles = setOf(TextStyle.Block)) { text("a") text("b") @@ -74,8 +55,7 @@ class GroupWrappingTest { @Test fun nested() { - - val page = createPage { + val page = TestPage { group(styles = setOf(TextStyle.Block)) { text("a") group(styles = setOf(TextStyle.Block)) { @@ -93,65 +73,4 @@ class GroupWrappingTest { assert(linesAfterContentTag().contains("<div>a<div><div>bc</div></div>d</div>")) } - private fun linesAfterContentTag() = - files.contents.getValue("test-page.html").lines() - .dropWhile { !it.contains("""<div id="content">""") } - .joinToString(separator = "") { it.trim() } } - -// TODO: may be useful for other tests, consider extracting -private fun createPage( - callback: PageContentBuilder.DocumentableContentBuilder.() -> Unit -) = object : RootPageNode(), ContentPage { - override val dri: Set<DRI> = setOf(DRI.topLevel) - override val documentable: Documentable? = null - override val embeddedResources: List<String> = emptyList() - override val name: String - get() = "testPage" - override val children: List<PageNode> - get() = emptyList() - - override val content: ContentNode = PageContentBuilder(EmptyCommentConverter, DokkaConsoleLogger).contentFor( - DRI.topLevel, - emptySet(), - block = callback - ) - - override fun modified( - name: String, - content: ContentNode, - dri: Set<DRI>, - embeddedResources: List<String>, - children: List<PageNode> - ) = this - - override fun modified(name: String, children: List<PageNode>) = this -} - -private object EmptyCommentConverter : CommentsToContentConverter { - override fun buildContent( - docTag: DocTag, - dci: DCI, - platforms: Set<PlatformData>, - styles: Set<Style>, - extras: Set<Extra> - ): List<ContentNode> = emptyList() -} - -private object EmptyLocationProviderFactory: LocationProviderFactory { - override fun getLocationProvider(pageNode: RootPageNode) = object : LocationProvider { - override fun resolve(dri: DRI, platforms: List<PlatformData>, context: PageNode?): String = "" - - override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = node.name - - override fun resolveRoot(node: PageNode): String { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun ancestors(node: PageNode): List<PageNode> { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - } - -}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/renderers/html/PlatformDependentHintTest.kt b/plugins/base/src/test/kotlin/renderers/html/PlatformDependentHintTest.kt new file mode 100644 index 00000000..2fda1ee1 --- /dev/null +++ b/plugins/base/src/test/kotlin/renderers/html/PlatformDependentHintTest.kt @@ -0,0 +1,118 @@ +package renderers.html + +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.renderers.html.HtmlRenderer +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.pages.Style +import org.jetbrains.dokka.pages.TextStyle +import org.junit.Test +import renderers.RenderingOnlyTestBase +import renderers.TestPage + +class PlatformDependentHintTest: RenderingOnlyTestBase() { + private val pl1 = PlatformData("pl1", Platform.js, listOf("pl1")) + private val pl2 = PlatformData("pl2", Platform.jvm, listOf("pl2")) + private val pl3 = PlatformData("pl3", Platform.native, listOf("pl3")) + + @Test + fun platformIndependentCase() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a") + text("b") + text("c") + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div>abc</div></div>")) + } + + @Test + fun completelyDivergentCase() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a", platformData = setOf(pl1)) + text("b", platformData = setOf(pl2)) + text("c", platformData = setOf(pl3)) + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div>a</div> [pl1]<div>b</div> [pl2]<div>c</div> [pl3]</div>")) + } + + @Test + fun overlappingCase() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + text("a", platformData = setOf(pl1)) + text("b", platformData = setOf(pl1, pl2)) + text("c", platformData = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div>ab</div> [pl1]<div>bc</div> [pl2]</div>")) + } + + @Test + fun caseThatCanBeSimplified() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + text("a", platformData = setOf(pl1, pl2)) + text("b", platformData = setOf(pl1)) + text("b", platformData = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div>ab</div></div>")) + } + + @Test + fun caseWithGroupBreakingSimplification() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + group(styles = setOf(TextStyle.Block)) { + text("a", platformData = setOf(pl1, pl2)) + text("b", platformData = setOf(pl1)) + } + text("b", platformData = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div><div>ab</div></div> [pl1]<div><div>a</div>b</div> [pl2]</div>")) + } + + @Test + fun caseWithGroupNotBreakingSimplification() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + group { + text("a", platformData = setOf(pl1, pl2)) + text("b", platformData = setOf(pl1)) + } + text("b", platformData = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div>ab</div></div>")) + } + + @Test + fun partiallyUnifiedCase() { + val page = TestPage { + platformDependentHint(platformData = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a", platformData = setOf(pl1)) + text("a", platformData = setOf(pl2)) + text("b", platformData = setOf(pl3)) + } + } + + HtmlRenderer(context).render(page) + assert(linesAfterContentTag().contains("<div>a</div> [pl1, pl2]<div>b</div> [pl3]</div>")) + } +}
\ No newline at end of file |