package org.jetbrains.dokka.base.translators.documentables import org.jetbrains.dokka.base.signatures.SignatureProvider import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.DocTag import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.DokkaLogger @DslMarker annotation class ContentBuilderMarker open class PageContentBuilder( val commentsConverter: CommentsToContentConverter, val signatureProvider: SignatureProvider, val logger: DokkaLogger ) { fun contentFor( dri: DRI, platformData: Set<PlatformData>, kind: Kind = ContentKind.Main, styles: Set<Style> = emptySet(), extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), block: DocumentableContentBuilder.() -> Unit ): ContentGroup = DocumentableContentBuilder(dri, platformData, styles, extra) .apply(block) .build(platformData, kind, styles, extra) fun contentFor( d: Documentable, kind: Kind = ContentKind.Main, styles: Set<Style> = emptySet(), extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), platformData: Set<PlatformData> = d.platformData.toSet(), block: DocumentableContentBuilder.() -> Unit = {} ): ContentGroup = DocumentableContentBuilder(d.dri, platformData, styles, extra) .apply(block) .build(d.platformData.toSet(), kind, styles, extra) @ContentBuilderMarker open inner class DocumentableContentBuilder( val mainDRI: DRI, val mainPlatformData: Set<PlatformData>, val mainStyles: Set<Style>, val mainExtra: PropertyContainer<ContentNode> ) { protected val contents = mutableListOf<ContentNode>() fun build( platformData: Set<PlatformData>, kind: Kind, styles: Set<Style>, extra: PropertyContainer<ContentNode> ) = ContentGroup( contents.toList(), DCI(setOf(mainDRI), kind), platformData, styles, extra ) operator fun ContentNode.unaryPlus() { contents += this } operator fun Collection<ContentNode>.unaryPlus() { contents += this } fun header( level: Int, kind: Kind = ContentKind.Main, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, block: DocumentableContentBuilder.() -> Unit ) { contents += ContentHeader( level, contentFor(mainDRI, mainPlatformData, kind, styles, extra, block) ) } fun text( text: String, kind: Kind = ContentKind.Main, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra ) { contents += createText(text, kind, platformData, styles, extra) } fun buildSignature(d: Documentable) = signatureProvider.signature(d) fun linkTable( elements: List<DRI>, kind: Kind = ContentKind.Main, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra ) { contents += ContentTable( emptyList(), elements.map { contentFor(it, platformData, kind, styles, extra) { link(it.classNames ?: "", it) } }, DCI(setOf(mainDRI), kind), platformData, styles, extra ) } fun table( dri: DRI = mainDRI, kind: Kind = ContentKind.Main, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, operation: DocumentableContentBuilder.() -> List<ContentGroup> ) { contents += ContentTable( emptyList(), operation(), DCI(setOf(mainDRI), kind), platformData, styles, extra ) } fun <T : Documentable> block( name: String, level: Int, kind: Kind = ContentKind.Main, elements: Iterable<T>, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, renderWhenEmpty: Boolean = false, operation: DocumentableContentBuilder.(T) -> Unit ) { if (renderWhenEmpty || elements.any()) { header(level) { text(name) } contents += ContentTable( emptyList(), elements.map { buildGroup(it.dri, it.platformData.toSet(), kind, styles, extra) { // TODO this will fail operation(it) } }, DCI(setOf(mainDRI), kind), platformData, styles, extra ) } } fun <T> list( elements: List<T>, prefix: String = "", suffix: String = "", separator: String = ", ", platformData: Set<PlatformData> = mainPlatformData, // TODO: children should be aware of this platform data operation: DocumentableContentBuilder.(T) -> Unit ) { if (elements.isNotEmpty()) { if (prefix.isNotEmpty()) text(prefix, platformData = platformData) elements.dropLast(1).forEach { operation(it) text(separator, platformData = platformData) } operation(elements.last()) if (suffix.isNotEmpty()) text(suffix, platformData = platformData) } } fun link( text: String, address: DRI, kind: Kind = ContentKind.Main, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra ) { contents += ContentDRILink( listOf(createText(text, kind, platformData, styles, extra)), address, DCI(setOf(mainDRI), kind), platformData ) } fun link( address: DRI, kind: Kind = ContentKind.Main, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, block: DocumentableContentBuilder.() -> Unit ) { contents += ContentDRILink( contentFor(mainDRI, platformData, kind, styles, extra, block).children, address, DCI(setOf(mainDRI), kind), platformData ) } fun comment( docTag: DocTag, kind: Kind = ContentKind.Comment, platformData: Set<PlatformData> = mainPlatformData, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra ) { val content = commentsConverter.buildContent( docTag, DCI(setOf(mainDRI), kind), platformData ) contents += ContentGroup(content, DCI(setOf(mainDRI), kind), platformData, styles, extra) } fun group( dri: DRI = mainDRI, platformData: Set<PlatformData> = mainPlatformData, kind: Kind = ContentKind.Main, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, block: DocumentableContentBuilder.() -> Unit ) { contents += buildGroup(dri, platformData, kind, styles, extra, block) } fun buildGroup( dri: DRI = mainDRI, platformData: Set<PlatformData> = mainPlatformData, kind: Kind = ContentKind.Main, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, block: DocumentableContentBuilder.() -> Unit ): ContentGroup = contentFor(dri, platformData, kind, styles, extra, block) fun breakLine(platformData: Set<PlatformData> = mainPlatformData) { contents += ContentBreakLine(platformData) } fun platformDependentHint( dri: DRI = mainDRI, platformData: Set<PlatformData> = mainPlatformData, kind: Kind = ContentKind.Main, styles: Set<Style> = mainStyles, extra: PropertyContainer<ContentNode> = mainExtra, block: DocumentableContentBuilder.() -> Unit ) { contents += PlatformHintedContent( buildGroup(dri, platformData, kind, styles, extra, block), platformData ) } protected fun createText( text: String, kind: Kind, platformData: Set<PlatformData>, styles: Set<Style>, extra: PropertyContainer<ContentNode> ) = ContentText(text, DCI(setOf(mainDRI), kind), platformData, styles, extra) fun <T> platformText( value: PlatformDependent<T>, transform: (T) -> String ) = value.entries.forEach { (p, v) -> transform(v).takeIf { it.isNotBlank() }?.also { text(it, platformData = setOf(p)) } } } }