blob: bc6429bf57600ee8c9e9c5afe5f6d73df2373946 [file] [log] [blame]
package org.jetbrains.dokka.Formats
import com.google.inject.Inject
import com.google.inject.name.Named
import kotlinx.html.*
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Samples.DevsiteSampleProcessingService
import org.jetbrains.dokka.Kotlin.ParameterInfoNode
import org.jetbrains.dokka.Utilities.firstSentence
import java.lang.Math.max
import java.net.URI
import kotlin.reflect.KClass
/**
* On Devsite, certain headers and footers are needed for generating Devsite metadata.
*/
class DevsiteHtmlTemplateService @Inject constructor(
@Named("outlineRoot") val outlineRoot: String,
@Named("dacRoot") val dacRoot: String
) : JavaLayoutHtmlTemplateService {
override fun composePage(page: JavaLayoutHtmlFormatOutputBuilder.Page, tagConsumer: TagConsumer<Appendable>, headContent: HEAD.() -> Unit, bodyContent: BODY.() -> Unit) {
tagConsumer.html {
attributes["devsite"] = "true"
head {
headContent()
title {
+when (page) {
is JavaLayoutHtmlFormatOutputBuilder.Page.ClassIndex -> "Class Index"
is JavaLayoutHtmlFormatOutputBuilder.Page.ClassPage -> page.node.nameWithOuterClass()
is JavaLayoutHtmlFormatOutputBuilder.Page.PackageIndex -> "Package Index"
is JavaLayoutHtmlFormatOutputBuilder.Page.PackagePage -> page.node.nameWithOuterClass()
}
}
unsafe { +"{% setvar book_path %}${dacRoot}/${outlineRoot}_book.yaml{% endsetvar %}\n{% include \"_shared/_reference-head-tags.html\" %}\n" }
}
body {
bodyContent()
}
}
}
}
class DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl @javax.inject.Inject constructor(
val uriProvider: JavaLayoutHtmlUriProvider,
val languageService: LanguageService,
val templateService: JavaLayoutHtmlTemplateService,
val logger: DokkaLogger
) : JavaLayoutHtmlFormatOutputBuilderFactory {
override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder {
return createOutputBuilder(output, uriProvider.mainUri(node))
}
override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder {
return DevsiteLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri)
}
}
class DevsiteLayoutHtmlFormatOutputBuilder(
output: Appendable,
languageService: LanguageService,
uriProvider: JavaLayoutHtmlUriProvider,
templateService: JavaLayoutHtmlTemplateService,
logger: DokkaLogger,
uri: URI
) : JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri) {
override fun FlowContent.fullMemberDocs(node: DocumentationNode) {
fullMemberDocs(node, node)
}
override fun FlowContent.fullMemberDocs(node: DocumentationNode, uriNode: DocumentationNode) {
a {
attributes["name"] = uriNode.signatureForAnchor(logger).anchorEncoded()
}
div(classes = "api apilevel-${node.apiLevel.name}") {
attributes["data-version-added"] = node.apiLevel.name
h3(classes = "api-name") {
//id = node.signatureForAnchor(logger).urlEncoded()
+node.prettyName
}
apiAndDeprecatedVersions(node)
pre(classes = "api-signature no-pretty-print") { renderedSignature(node, LanguageService.RenderMode.FULL) }
deprecationWarningToMarkup(node, prefix = true)
nodeContent(node, uriNode)
node.constantValue()?.let { value ->
pre {
+"Value: "
code { +value }
}
}
for ((name, sections) in node.content.sections.groupBy { it.tag }) {
when (name) {
ContentTags.Return -> {
table(classes = "responsive") {
tbody {
tr {
th {
colSpan = "2"
+name
}
}
sections.forEach {
tr {
if (it.children.size > 0) {
td {
val firstChild = it.children.first()
if (firstChild is ContentBlock &&
firstChild.children.size == 3 &&
firstChild.children[0] is NodeRenderContent &&
firstChild.children[1] is ContentSymbol &&
firstChild.children[2] is ContentText) {
// it.children is expected to have two items
// First should have 3 children of its own:
// - NodeRenderContent is the return type
// - ContentSymbol - ":"
// - ContentText - " "
// We want to only use NodeRenderContent in a separate <td> and
// <code> to get proper formatting in DAC.
code {
metaMarkup(listOf(firstChild.children[0]))
}
} else {
metaMarkup(listOf(firstChild))
}
}
td {
if (it.children.size > 1) {
metaMarkup(it.children.subList(1, it.children.size))
}
}
}
}
}
}
}
}
ContentTags.Parameters -> {
table(classes = "responsive") {
tbody {
tr {
th {
colSpan = "2"
+name
}
}
sections.forEach { section ->
tr {
td {
val parameterInfoNode = section.children.find { it is ParameterInfoNode } as? ParameterInfoNode
// If there is no info found, just display the parameter
// name.
if (parameterInfoNode?.parameterContent == null) {
code {
section.subjectName?.let { +it }
}
} else {
// Add already marked up type information here
metaMarkup(
listOf(parameterInfoNode.parameterContent!!)
)
}
}
td {
metaMarkup(section.children)
}
}
}
}
}
}
ContentTags.SeeAlso -> {
div {
p {
b {
+name
}
}
ul(classes = "nolist") {
sections.filter {it.tag == "See Also"}.forEach {
it.children.forEach { child ->
if (child is ContentNodeLazyLink || child is ContentExternalLink) {
li {
code {
contentNodeToMarkup(child) // Wrap bare links in listItems.
} // bare links come from the java-to-kotlin parser.
}
}
else if (child is ContentUnorderedList) {
metaMarkup(child.children) // Already wrapped in listItems.
} // this is how we want things to look. No parser currently does this (yet).
else if (child is ContentParagraph) {
li{
code {
metaMarkup (child.children) // Replace paragraphs with listItems.
} // paragraph-wrapped links come from the kotlin parser
}
} // NOTE: currently the java-to-java parser does not add See Also links!
}
}
}
}
}
ContentTags.Exceptions -> {
table(classes = "responsive") {
tbody {
tr {
th {
colSpan = "2"
+name
}
}
sections.forEach {
tr {
td {
code {
it.subjectName?.let { +it }
}
}
td {
metaMarkup(it.children)
}
}
}
}
}
}
}
}
}
}
override fun summary(node: DocumentationNode) = node.firstSentenceOfSummary()
fun TBODY.xmlAttributeRow(attr: DocumentationNode) = tr {
td {
a(href = attr) {
code {
+attr.attributeRef!!.name
}
}
}
td {
+attr.attributeRef!!.firstSentence()
}
}
protected fun FlowContent.fullAttributeDocs(
attributes: List<DocumentationNode>,
header: String
) {
if (attributes.none()) return
h2 {
+header
}
attributes.forEach {
fullMemberDocs(it.attributeRef!!, it)
}
}
override fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) {
fullAttributeDocs(attributes, "XML attributes")
fullMemberDocs(enumValues, "Enum values")
fullMemberDocs(constants, "Constants")
constructors.forEach { (visibility, group) ->
fullMemberDocs(group, "${visibility.capitalize()} constructors")
}
functions.forEach { (visibility, group) ->
fullMemberDocs(group, "${visibility.capitalize()} methods")
}
fullMemberDocs(properties, "Properties")
fields.forEach { (visibility, group) ->
fullMemberDocs(group, "${visibility.capitalize()} fields")
}
if (!hasMeaningfulCompanion) {
fullMemberDocs(companionFunctions, "Companion functions")
fullMemberDocs(companionProperties, "Companion properties")
}
}
override fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) {
summaryNodeGroup(
nestedClasses,
header = "Nested classes",
summaryId = "nestedclasses",
tableClass = "responsive",
headerAsRow = true
) {
nestedClassSummaryRow(it)
}
summaryNodeGroup(
attributes,
header="XML attributes",
summaryId="lattrs",
tableClass = "responsive",
headerAsRow = true
) {
xmlAttributeRow(it)
}
expandableSummaryNodeGroupForInheritedMembers(
superClasses = inheritedAttributes.entries,
header="Inherited XML attributes",
tableId="inhattrs",
tableClass = "responsive",
row = { inheritedXmlAttributeRow(it)}
)
summaryNodeGroup(
constants,
header = "Constants",
summaryId = "constants",
tableClass = "responsive",
headerAsRow = true
) { propertyLikeSummaryRow(it) }
expandableSummaryNodeGroupForInheritedMembers(
superClasses = inheritedConstants.entries,
header = "Inherited constants",
tableId = "inhconstants",
tableClass = "responsive constants inhtable",
row = { inheritedMemberRow(it) }
)
constructors.forEach { (visibility, group) ->
summaryNodeGroup(
group,
header = "${visibility.capitalize()} constructors",
summaryId = "${visibility.take(3)}ctors",
tableClass = "responsive",
headerAsRow = true
) {
functionLikeSummaryRow(it)
}
}
summaryNodeGroup(
enumValues,
header = "Enum values",
summaryId = "enumvalues",
tableClass = "responsive",
headerAsRow = true
) {
propertyLikeSummaryRow(it, showSignature = false)
}
functions.forEach { (visibility, group) ->
summaryNodeGroup(
group,
header = "${visibility.capitalize()} methods",
summaryId = "${visibility.take(3)}methods",
tableClass = "responsive",
headerAsRow = true
) {
functionLikeSummaryRow(it)
}
}
summaryNodeGroup(
companionFunctions,
header = "Companion functions",
summaryId = "compmethods",
tableClass = "responsive",
headerAsRow = true
) {
functionLikeSummaryRow(it)
}
expandableSummaryNodeGroupForInheritedMembers(
superClasses = inheritedFunctionsByReceiver.entries,
header = "Inherited functions",
tableId = "inhmethods",
tableClass = "responsive",
row = { inheritedMemberRow(it) }
)
summaryNodeGroup(
extensionFunctions.entries,
header = "Extension functions",
summaryId = "extmethods",
tableClass = "responsive",
headerAsRow = true
) {
extensionRow(it) {
functionLikeSummaryRow(it)
}
}
summaryNodeGroup(
inheritedExtensionFunctions.entries,
header = "Inherited extension functions",
summaryId = "inhextmethods",
tableClass = "responsive",
headerAsRow = true
) {
extensionRow(it) {
functionLikeSummaryRow(it)
}
}
fields.forEach { (visibility, group) ->
summaryNodeGroup(
group,
header = "${visibility.capitalize()} fields",
summaryId = "${visibility.take(3)}fields",
tableClass = "responsive",
headerAsRow = true
) { propertyLikeSummaryRow(it) }
}
expandableSummaryNodeGroupForInheritedMembers(
superClasses = inheritedFieldsByReceiver.entries,
header = "Inherited fields",
tableId = "inhfields",
tableClass = "responsive properties inhtable",
row = { inheritedMemberRow(it) }
)
summaryNodeGroup(
properties,
header = "Properties",
summaryId = "properties",
tableClass = "responsive",
headerAsRow = true
) { propertyLikeSummaryRow(it) }
summaryNodeGroup(
companionProperties,
"Companion properties",
headerAsRow = true
) {
propertyLikeSummaryRow(it)
}
expandableSummaryNodeGroupForInheritedMembers(
superClasses = inheritedPropertiesByReceiver.entries,
header = "Inherited properties",
tableId = "inhfields",
tableClass = "responsive properties inhtable",
row = { inheritedMemberRow(it) }
)
summaryNodeGroup(
extensionProperties.entries,
"Extension properties",
headerAsRow = true
) {
extensionRow(it) {
propertyLikeSummaryRow(it)
}
}
summaryNodeGroup(
inheritedExtensionProperties.entries,
"Inherited extension properties",
headerAsRow = true
) {
extensionRow(it) {
propertyLikeSummaryRow(it)
}
}
}
fun <T> FlowContent.summaryNodeGroup(
nodes: Iterable<T>,
header: String,
headerAsRow: Boolean,
summaryId: String,
tableClass: String = "responsive",
row: TBODY.(T) -> Unit
) {
if (nodes.none()) return
if (!headerAsRow) {
h2 { +header }
}
table(classes = tableClass) {
id = summaryId
tbody {
if (headerAsRow) {
developerHeading(header)
}
nodes.forEach { node ->
row(node)
}
}
}
}
override fun FlowContent.contentBlockCode(content: ContentBlockCode) {
pre {
attributes["class"] = "prettyprint"
contentNodesToMarkup(content.children)
}
}
override fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) {
pre {
attributes["class"] = "prettyprint"
contentNodesToMarkup(content.importsBlock.children)
+"\n\n"
contentNodesToMarkup(content.children)
}
}
override fun generatePackage(page: Page.PackagePage) = templateService.composePage(
page,
htmlConsumer,
headContent = {
},
bodyContent = {
h1 { +page.node.name }
nodeContent(page.node)
summaryNodeGroup(page.interfaces.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Interfaces", headerAsRow = false) { classLikeRow(it) }
summaryNodeGroup(page.classes.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Classes", headerAsRow = false) { classLikeRow(it) }
summaryNodeGroup(page.exceptions.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Exceptions", headerAsRow = false) { classLikeRow(it) }
summaryNodeGroup(page.typeAliases.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
summaryNodeGroup(page.annotations.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Annotations", headerAsRow = false) { classLikeRow(it) }
summaryNodeGroup(page.enums.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Enums", headerAsRow = false) { classLikeRow(it) }
summaryNodeGroup(
page.constants.sortedBy { it.name },
"Top-level constants summary",
headerAsRow = false
) {
propertyLikeSummaryRow(it)
}
summaryNodeGroup(
page.functions.sortedBy { it.name },
"Top-level functions summary",
headerAsRow = false
) {
functionLikeSummaryRow(it)
}
summaryNodeGroup(
page.properties.sortedBy { it.name },
"Top-level properties summary",
headerAsRow = false
) {
propertyLikeSummaryRow(it)
}
summaryNodeGroupForExtensions("Extension functions summary", page.extensionFunctions.entries)
summaryNodeGroupForExtensions("Extension properties summary", page.extensionProperties.entries)
fullMemberDocs(page.constants.sortedBy { it.name }, "Top-level constants")
fullMemberDocs(page.functions.sortedBy { it.name }, "Top-level functions")
fullMemberDocs(page.properties.sortedBy { it.name }, "Top-level properties")
fullMemberDocs(page.extensionFunctions.values.flatten().sortedBy { it.name }, "Extension functions")
fullMemberDocs(page.extensionProperties.values.flatten().sortedBy { it.name }, "Extension properties")
}
)
private fun TBODY.inheritedXmlAttributeRow(inheritedMember: DocumentationNode) {
tr(classes = "api apilevel-${inheritedMember.attributeRef!!.apiLevel.name}") {
attributes["data-version-added"] = "${inheritedMember.apiLevel}"
td {
code {
a(href = inheritedMember) { +inheritedMember.attributeRef!!.name }
}
}
td {
attributes["width"] = "100%"
p {
nodeContent(inheritedMember.attributeRef!!, inheritedMember)
}
}
}
}
private fun TBODY.inheritedMemberRow(inheritedMember: DocumentationNode) {
tr(classes = "api apilevel-${inheritedMember.apiLevel.name}") {
attributes["data-version-added"] = "${inheritedMember.apiLevel}"
val type = inheritedMember.detailOrNull(NodeKind.Type)
td {
code {
type?.let {
renderedSignature(it, LanguageService.RenderMode.SUMMARY)
}
}
}
td {
attributes["width"] = "100%"
code {
a(href = inheritedMember) { +inheritedMember.name }
if (inheritedMember.kind == NodeKind.Function) {
shortFunctionParametersList(inheritedMember)
}
}
p {
nodeContent(inheritedMember)
}
}
}
}
private fun FlowContent.expandableSummaryNodeGroupForInheritedMembers(
tableId: String,
header: String,
tableClass: String,
superClasses: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>,
row: TBODY.(inheritedMember: DocumentationNode) -> Unit
) {
if (superClasses.none()) return
table(classes = tableClass) {
attributes["id"] = tableId
tbody {
developerHeading(header)
superClasses.forEach { (superClass, members) ->
tr(classes = "api apilevel-${superClass.apiLevel.name}") {
td {
attributes["colSpan"] = "2"
div(classes = "expandable jd-inherited-apis") {
span(classes = "expand-control exw-expanded") {
+"From class "
code {
a(href = superClass) { +superClass.name }
}
}
table(classes = "responsive exw-expanded-content") {
tbody {
members.forEach { inheritedMember ->
row(inheritedMember)
}
}
}
}
}
}
}
}
}
}
private fun FlowContent.summaryNodeGroupForExtensions(
header: String,
receivers: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>
) {
if (receivers.none()) return
h2 { +header }
div {
receivers.forEach {
table {
tr {
td {
attributes["colSpan"] = "2"
+"For "
a(href = it.key) { +it.key.name }
}
}
it.value.forEach { node ->
tr {
if (node.kind != NodeKind.Constructor) {
td {
modifiers(node)
renderedSignature(node.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
}
}
td {
div {
code {
val receiver = node.detailOrNull(NodeKind.Receiver)
if (receiver != null) {
renderedSignature(receiver.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
+"."
}
a(href = node) { +node.name }
shortFunctionParametersList(node)
}
}
nodeSummary(node)
}
}
}
}
}
}
}
override fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
page,
htmlConsumer,
headContent = {
},
bodyContent = {
h1 { +"Package Index" }
table {
tbody {
for (node in page.packages) {
tr {
td {
a(href = uriProvider.linkTo(node, uri)) { +node.name }
}
}
}
}
}
}
)
override fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
page,
htmlConsumer,
headContent = {
},
bodyContent = {
h1 { +"Class Index" }
p {
+"These are all the API classes. See all "
a(href="packages.html") {
+"API packages."
}
}
div(classes = "jd-letterlist") {
page.classesByFirstLetter.forEach { (letter) ->
+"\n "
a(href = "#letter_$letter") { +letter }
unsafe {
raw("&nbsp;&nbsp;")
}
}
+"\n "
}
page.classesByFirstLetter.forEach { (letter, classes) ->
h2 {
id = "letter_$letter"
+letter
}
table {
tbody {
for (node in classes) {
tr {
td {
a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
}
td {
if (!deprecatedIndexSummary(node)) {
nodeSummary(node)
}
}
}
}
}
}
}
}
)
override fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
table(classes = "jd-inheritance-table") {
var level = superclasses.size
superclasses.forEach {
tr {
var spaceColumns = max(superclasses.size - 1 - level, 0)
while (spaceColumns > 0) {
td(classes = "jd-inheritance-space") {
+" "
}
spaceColumns--
}
if (it != superclasses.first()) {
td(classes = "jd-inheritance-space") {
+"   ↳"
}
}
td(classes = "jd-inheritance-class-cell") {
attributes["colSpan"] = "$level"
qualifiedTypeReference(it)
}
}
level--
}
}
}
override fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
if (inheritors.isEmpty()) return
// The number of subclasses in collapsed view before truncating and adding a "and xx others".
// See https://developer.android.com/reference/android/view/View for an example.
val numToShow = 12
table(classes = "jd-sumtable jd-sumtable-subclasses") {
tbody {
tr {
td {
div(classes = "expandable") {
span(classes = "expand-control") {
if (direct)
+"Known Direct Subclasses"
else
+"Known Indirect Subclasses"
}
div(classes = "showalways") {
attributes["id"] = if (direct) "subclasses-direct" else "subclasses-indirect"
inheritors.take(numToShow).forEach { inheritor ->
a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
if (inheritor != inheritors.last()) +", "
}
if (inheritors.size > numToShow) {
+"and ${inheritors.size - numToShow} others."
}
}
div(classes = "exw-expanded-content") {
attributes["id"] = if (direct) "subclasses-direct-summary" else "subclasses-indirect-summary"
table(classes = "jd-sumtable-expando") {
inheritors.forEach { inheritor ->
tr(classes = "api api-level-${inheritor.apiLevel.name}") {
attributes["data-version-added"] = inheritor.apiLevel.name
td(classes = "jd-linkcol") {
a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
}
td(classes = "jd-descrcol") {
attributes["width"] = "100%"
nodeSummary(inheritor)
}
}
}
}
}
}
}
}
}
}
}
fun DocumentationNode.firstSentenceOfSummary(): ContentNode {
fun Sequence<ContentNode>.flatten(): Sequence<ContentNode> {
return flatMap {
when (it) {
is ContentParagraph -> it.children.asSequence().flatten()
else -> sequenceOf(it)
}
}
}
fun ContentNode.firstSentence(): ContentText? = when(this) {
is ContentText -> ContentText(text.firstSentence())
else -> null
}
val elements = sequenceOf(summary).flatten()
fun containsDot(it: ContentNode) = (it as? ContentText)?.text?.contains(".") == true
val paragraph = ContentParagraph()
(elements.takeWhile { !containsDot(it) } + elements.firstOrNull { containsDot(it) }?.firstSentence()).forEach {
if (it != null) {
paragraph.append(it)
}
}
if (paragraph.isEmpty()) {
return ContentEmpty
}
return paragraph
}
fun DocumentationNode.firstSentence(): String {
val sb = StringBuilder()
addContentNodeToStringBuilder(content, sb)
return sb.toString().firstSentence()
}
private fun addContentNodesToStringBuilder(content: List<ContentNode>, sb: StringBuilder): Unit =
content.forEach { addContentNodeToStringBuilder(it, sb) }
private fun addContentNodeToStringBuilder(content: ContentNode, sb: StringBuilder) {
when (content) {
is ContentText -> sb.appendWith(content.text)
is ContentSymbol -> sb.appendWith(content.text)
is ContentKeyword -> sb.appendWith(content.text)
is ContentIdentifier -> sb.appendWith(content.text)
is ContentEntity -> sb.appendWith(content.text)
is ContentHeading -> addContentNodesToStringBuilder(content.children, sb)
is ContentStrong -> addContentNodesToStringBuilder(content.children, sb)
is ContentStrikethrough -> addContentNodesToStringBuilder(content.children, sb)
is ContentEmphasis -> addContentNodesToStringBuilder(content.children, sb)
is ContentOrderedList -> addContentNodesToStringBuilder(content.children, sb)
is ContentUnorderedList -> addContentNodesToStringBuilder(content.children, sb)
is ContentListItem -> addContentNodesToStringBuilder(content.children, sb)
is ContentCode -> addContentNodesToStringBuilder(content.children, sb)
is ContentBlockSampleCode -> addContentNodesToStringBuilder(content.children, sb)
is ContentBlockCode -> addContentNodesToStringBuilder(content.children, sb)
is ContentParagraph -> addContentNodesToStringBuilder(content.children, sb)
is ContentNodeLink -> addContentNodesToStringBuilder(content.children, sb)
is ContentBookmark -> addContentNodesToStringBuilder(content.children, sb)
is ContentExternalLink -> addContentNodesToStringBuilder(content.children, sb)
is ContentLocalLink -> addContentNodesToStringBuilder(content.children, sb)
is ContentSection -> { }
is ContentBlock -> addContentNodesToStringBuilder(content.children, sb)
}
}
private fun StringBuilder.appendWith(text: String, delimiter: String = " ") {
if (this.length == 0) {
append(text)
} else {
append(delimiter)
append(text)
}
}
}
fun TBODY.developerHeading(header: String) {
tr {
th {
attributes["colSpan"] = "2"
+header
}
}
}
class DacFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin {
override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class
override val outlineFactoryClass = DacOutlineFormatter::class
override val languageServiceClass = KotlinLanguageService::class
override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class
override val sampleProcessingService = DevsiteSampleProcessingService::class
}
class DacAsJavaFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava {
override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class
override val outlineFactoryClass = DacOutlineFormatter::class
override val languageServiceClass = NewJavaLanguageService::class
override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class
}