package org.jetbrains.dokka import com.google.inject.Inject import com.google.inject.name.Named import org.jetbrains.dokka.Utilities.impliedPlatformsName import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty open class KotlinWebsiteOutputBuilder( to: StringBuilder, location: Location, generator: NodeLocationAwareGenerator, languageService: LanguageService, extension: String, impliedPlatforms: List ) : JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { private var needHardLineBreaks = false private var insideDiv = 0 override fun appendFrontMatter(nodes: Iterable, to: StringBuilder) { super.appendFrontMatter(nodes, to) to.appendln("layout: api") } override fun appendBreadcrumbs(path: Iterable) { if (path.count() > 1) { to.append("
") super.appendBreadcrumbs(path) to.append("
") } } override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("", "", body) override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) { to.append("
") if (!markdown) insideDiv++ block() if (!markdown) insideDiv-- to.append("
\n") } override fun appendAsSignature(node: ContentNode, block: () -> Unit) { val contentLength = node.textLength if (contentLength == 0) return div(to, "signature") { needHardLineBreaks = contentLength >= 62 try { block() } finally { needHardLineBreaks = false } } } override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set, block: () -> Unit) { div(to, "overload-group", calculateDataAttributes(platforms), true) { ensureParagraph() block() ensureParagraph() } } override fun appendLink(href: String, body: () -> Unit) = wrap("", "", body) override fun appendHeader(level: Int, body: () -> Unit) { if (insideDiv > 0) { wrapInTag("p", body, newlineAfterClose = true) } else { super.appendHeader(level, body) } } override fun appendLine() { if (insideDiv > 0) { to.appendln("
") } else { super.appendLine() } } override fun appendTable(vararg columns: String, body: () -> Unit) { to.appendln("") body() to.appendln("
") } override fun appendTableBody(body: () -> Unit) { to.appendln("") body() to.appendln("") } override fun appendTableRow(body: () -> Unit) { to.appendln("") body() to.appendln("") } override fun appendTableCell(body: () -> Unit) { to.appendln("") body() to.appendln("\n") } override fun appendBlockCode(language: String, body: () -> Unit) { if (language.isNotEmpty()) { super.appendBlockCode(language, body) } else { wrap("
", "
", body) } } override fun appendSymbol(text: String) { to.append("${text.htmlEscape()}") } override fun appendKeyword(text: String) { to.append("${text.htmlEscape()}") } override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { val id = signature?.let { " id=\"$it\"" }.orEmpty() to.append("${text.htmlEscape()}") } override fun appendSoftLineBreak() { if (needHardLineBreaks) to.append("
") } override fun appendIndentedSoftLineBreak() { if (needHardLineBreaks) { to.append("
    ") } } private fun identifierClassName(kind: IdentifierKind) = when (kind) { IdentifierKind.ParameterName -> "parameterName" IdentifierKind.SummarizedTypeName -> "summarizedTypeName" else -> "identifier" } fun calculateDataAttributes(platforms: Set): String { fun String.isKotlinVersion() = this.startsWith("Kotlin") fun String.isJREVersion() = this.startsWith("JRE") val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion) val jreVersion = platforms.singleOrNull(String::isJREVersion) val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() } val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: "" val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: "" val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: "" return "$platformsAttr$kotlinVersionAttr$jreVersionAttr" } override fun appendIndexRow(platforms: Set, block: () -> Unit) { if (platforms.isNotEmpty()) wrap("", "", block) else appendTableRow(block) } override fun appendPlatforms(platforms: Set) { } } class KotlinWebsiteFormatService @Inject constructor( generator: NodeLocationAwareGenerator, signatureGenerator: LanguageService, @Named(impliedPlatformsName) impliedPlatforms: List, logger: DokkaLogger ) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) { init { logger.warn("Format kotlin-website deprecated and will be removed in next release") } override fun createOutputBuilder(to: StringBuilder, location: Location) = KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) } class KotlinWebsiteRunnableSamplesOutputBuilder( to: StringBuilder, location: Location, generator: NodeLocationAwareGenerator, languageService: LanguageService, extension: String, impliedPlatforms: List ) : KotlinWebsiteOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { div(to, "sample", markdown = true) { appendBlockCode(language) { imports() wrap("\n\nfun main(args: Array) {", "}") { wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) } } } } } class KotlinWebsiteRunnableSamplesFormatService @Inject constructor( generator: NodeLocationAwareGenerator, signatureGenerator: LanguageService, @Named(impliedPlatformsName) impliedPlatforms: List, logger: DokkaLogger ) : JekyllFormatService(generator, signatureGenerator, "html", impliedPlatforms) { init { logger.warn("Format kotlin-website-samples deprecated and will be removed in next release") } override fun createOutputBuilder(to: StringBuilder, location: Location) = KotlinWebsiteRunnableSamplesOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) }