xref: /aosp_15_r20/external/dokka/core/src/main/kotlin/Formats/DacHtmlFormat.kt (revision 1b2d298c530bf0473cc943e8414a5ff577a994bc)

<lambda>null1 package org.jetbrains.dokka.Formats
2 
3 import com.google.inject.Inject
4 import com.google.inject.name.Named
5 import kotlinx.html.*
6 import org.jetbrains.dokka.*
7 import org.jetbrains.dokka.Samples.DevsiteSampleProcessingService
8 import org.jetbrains.dokka.Kotlin.ParameterInfoNode
9 import org.jetbrains.dokka.Utilities.firstSentence
10 import java.lang.Math.max
11 import java.net.URI
12 import kotlin.reflect.KClass
13 
14 /**
15  * On Devsite, certain headers and footers are needed for generating Devsite metadata.
16  */
17 class DevsiteHtmlTemplateService @Inject constructor(
18     @Named("outlineRoot") val outlineRoot: String,
19     @Named("dacRoot") val dacRoot: String
20 ) : JavaLayoutHtmlTemplateService {
21     override fun composePage(page: JavaLayoutHtmlFormatOutputBuilder.Page, tagConsumer: TagConsumer<Appendable>, headContent: HEAD.() -> Unit, bodyContent: BODY.() -> Unit) {
22         tagConsumer.html {
23             attributes["devsite"] = "true"
24             head {
25                 headContent()
26                 title {
27                     +when (page) {
28                         is JavaLayoutHtmlFormatOutputBuilder.Page.ClassIndex -> "Class Index"
29                         is JavaLayoutHtmlFormatOutputBuilder.Page.ClassPage -> page.node.nameWithOuterClass()
30                         is JavaLayoutHtmlFormatOutputBuilder.Page.PackageIndex -> "Package Index"
31                         is JavaLayoutHtmlFormatOutputBuilder.Page.PackagePage -> page.node.nameWithOuterClass()
32                     }
33                 }
34                 unsafe { +"{% setvar book_path %}${dacRoot}/${outlineRoot}_book.yaml{% endsetvar %}\n{% include \"_shared/_reference-head-tags.html\" %}\n" }
35             }
36             body {
37                 bodyContent()
38             }
39         }
40     }
41 }
42 
43 class DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl @javax.inject.Inject constructor(
44         val uriProvider: JavaLayoutHtmlUriProvider,
45         val languageService: LanguageService,
46         val templateService: JavaLayoutHtmlTemplateService,
47         val logger: DokkaLogger
48 ) : JavaLayoutHtmlFormatOutputBuilderFactory {
createOutputBuildernull49     override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder {
50         return createOutputBuilder(output, uriProvider.mainUri(node))
51     }
52 
createOutputBuildernull53     override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder {
54         return DevsiteLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri)
55     }
56 }
57 
58 class DevsiteLayoutHtmlFormatOutputBuilder(
59         output: Appendable,
60         languageService: LanguageService,
61         uriProvider: JavaLayoutHtmlUriProvider,
62         templateService: JavaLayoutHtmlTemplateService,
63         logger: DokkaLogger,
64         uri: URI
65 ) : JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri) {
fullMemberDocsnull66     override fun FlowContent.fullMemberDocs(node: DocumentationNode) {
67         fullMemberDocs(node, node)
68     }
69 
fullMemberDocsnull70     override fun FlowContent.fullMemberDocs(node: DocumentationNode, uriNode: DocumentationNode) {
71         a {
72             attributes["name"] = uriNode.signatureForAnchor(logger).anchorEncoded()
73         }
74         div(classes = "api apilevel-${node.apiLevel.name}") {
75             attributes["data-version-added"] = node.apiLevel.name
76             h3(classes = "api-name") {
77                 //id = node.signatureForAnchor(logger).urlEncoded()
78                 +node.prettyName
79             }
80             apiAndDeprecatedVersions(node)
81             pre(classes = "api-signature no-pretty-print") { renderedSignature(node, LanguageService.RenderMode.FULL) }
82             deprecationWarningToMarkup(node, prefix = true)
83             nodeContent(node, uriNode)
84             node.constantValue()?.let { value ->
85                 pre {
86                     +"Value: "
87                     code { +value }
88                 }
89             }
90             for ((name, sections) in node.content.sections.groupBy { it.tag }) {
91                 when (name) {
92                     ContentTags.Return -> {
93                         table(classes = "responsive") {
94                             tbody {
95                                 tr {
96                                     th {
97                                         colSpan = "2"
98                                         +name
99                                     }
100                                 }
101                                 sections.forEach {
102                                     tr {
103                                         if (it.children.size > 0) {
104                                             td {
105                                                 val firstChild = it.children.first()
106                                                 if (firstChild is ContentBlock &&
107                                                     firstChild.children.size == 3 &&
108                                                     firstChild.children[0] is NodeRenderContent &&
109                                                     firstChild.children[1] is ContentSymbol &&
110                                                     firstChild.children[2] is ContentText) {
111                                                     // it.children is expected to have two items
112                                                     // First should have 3 children of its own:
113                                                     // - NodeRenderContent is the return type
114                                                     // - ContentSymbol - ":"
115                                                     // - ContentText - " "
116                                                     // We want to only use NodeRenderContent in a separate <td> and
117                                                     // <code> to get proper formatting in DAC.
118                                                     code {
119                                                         metaMarkup(listOf(firstChild.children[0]))
120                                                     }
121                                                 } else {
122                                                     metaMarkup(listOf(firstChild))
123                                                 }
124                                             }
125                                             td {
126                                                 if (it.children.size > 1) {
127                                                     metaMarkup(it.children.subList(1, it.children.size))
128                                                 }
129                                             }
130                                         }
131                                     }
132                                 }
133                             }
134                         }
135                     }
136                     ContentTags.Parameters -> {
137                         table(classes = "responsive") {
138                             tbody {
139                                 tr {
140                                     th {
141                                         colSpan = "2"
142                                         +name
143                                     }
144                                 }
145                                 sections.forEach { section ->
146                                     tr {
147                                         td {
148                                             val parameterInfoNode = section.children.find { it is ParameterInfoNode } as? ParameterInfoNode
149                                             // If there is no info found, just display the parameter
150                                             // name.
151                                             if (parameterInfoNode?.parameterContent == null) {
152                                                 code {
153                                                     section.subjectName?.let { +it }
154                                                 }
155                                             } else {
156                                                 // Add already marked up type information here
157                                                 metaMarkup(
158                                                     listOf(parameterInfoNode.parameterContent!!)
159                                                 )
160                                             }
161                                         }
162                                         td {
163                                             metaMarkup(section.children)
164                                         }
165                                     }
166                                 }
167                             }
168                         }
169                     }
170                     ContentTags.SeeAlso -> {
171                         div {
172                             p {
173                                 b {
174                                     +name
175                                 }
176                             }
177                             ul(classes = "nolist") {
178                                 sections.filter {it.tag == "See Also"}.forEach {
179                                     it.children.forEach { child ->
180                                         if (child is ContentNodeLazyLink || child is ContentExternalLink) {
181                                             li {
182                                                 code {
183                                                     contentNodeToMarkup(child) // Wrap bare links in listItems.
184                                                 } // bare links come from the java-to-kotlin parser.
185                                             }
186                                         }
187                                         else if (child is ContentUnorderedList) {
188                                             metaMarkup(child.children) // Already wrapped in listItems.
189                                         } // this is how we want things to look. No parser currently does this (yet).
190                                         else if (child is ContentParagraph) {
191                                             li{
192                                                 code {
193                                                     metaMarkup (child.children) // Replace paragraphs with listItems.
194                                                 } // paragraph-wrapped links come from the kotlin parser
195                                             }
196                                         } // NOTE: currently the java-to-java parser does not add See Also links!
197                                     }
198                                 }
199                             }
200                         }
201                     }
202                     ContentTags.Exceptions -> {
203                         table(classes = "responsive") {
204                             tbody {
205                                 tr {
206                                     th {
207                                         colSpan = "2"
208                                         +name
209                                     }
210                                 }
211                                 sections.forEach {
212                                     tr {
213                                         td {
214                                             code {
215                                                 it.subjectName?.let { +it }
216                                             }
217                                         }
218                                         td {
219                                             metaMarkup(it.children)
220                                         }
221                                     }
222                                 }
223                             }
224                         }
225                     }
226                 }
227             }
228         }
229     }
230 
summarynull231     override fun summary(node: DocumentationNode) = node.firstSentenceOfSummary()
232 
233     fun TBODY.xmlAttributeRow(attr: DocumentationNode) = tr {
234         td {
235             a(href = attr) {
236                 code {
237                     +attr.attributeRef!!.name
238                 }
239             }
240         }
241         td {
242             +attr.attributeRef!!.firstSentence()
243         }
244     }
245 
FlowContentnull246     protected fun FlowContent.fullAttributeDocs(
247         attributes: List<DocumentationNode>,
248         header: String
249     ) {
250         if (attributes.none()) return
251         h2 {
252             +header
253         }
254         attributes.forEach {
255             fullMemberDocs(it.attributeRef!!, it)
256         }
257     }
258 
<lambda>null259     override fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) {
260         fullAttributeDocs(attributes, "XML attributes")
261         fullMemberDocs(enumValues, "Enum values")
262         fullMemberDocs(constants, "Constants")
263 
264         constructors.forEach { (visibility, group) ->
265             fullMemberDocs(group, "${visibility.capitalize()} constructors")
266         }
267 
268         functions.forEach { (visibility, group) ->
269             fullMemberDocs(group, "${visibility.capitalize()} methods")
270         }
271 
272         fullMemberDocs(properties, "Properties")
273 
274         fields.forEach { (visibility, group) ->
275             fullMemberDocs(group, "${visibility.capitalize()} fields")
276         }
277         if (!hasMeaningfulCompanion) {
278             fullMemberDocs(companionFunctions, "Companion functions")
279             fullMemberDocs(companionProperties, "Companion properties")
280         }
281     }
282 
<lambda>null283     override fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) {
284         this@classLikeSummaries.summaryNodeGroup(
285                 nestedClasses,
286                 header = "Nested classes",
287                 summaryId = "nestedclasses",
288                 tableClass = "responsive",
289                 headerAsRow = true
290         ) {
291             nestedClassSummaryRow(it)
292         }
293 
294         this@classLikeSummaries.summaryNodeGroup(
295             attributes,
296             header="XML attributes",
297             summaryId="lattrs",
298             tableClass = "responsive",
299             headerAsRow = true
300         ) {
301             xmlAttributeRow(it)
302         }
303 
304         this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
305                 superClasses = inheritedAttributes.entries,
306                 header="Inherited XML attributes",
307                 tableId="inhattrs",
308                 tableClass = "responsive",
309                 row = { inheritedXmlAttributeRow(it)}
310         )
311 
312         this@classLikeSummaries.summaryNodeGroup(
313                 constants,
314                 header = "Constants",
315                 summaryId = "constants",
316                 tableClass = "responsive",
317                 headerAsRow = true
318         ) { propertyLikeSummaryRow(it) }
319 
320         this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
321                 superClasses = inheritedConstants.entries,
322                 header = "Inherited constants",
323                 tableId = "inhconstants",
324                 tableClass = "responsive constants inhtable",
325                 row = { inheritedMemberRow(it) }
326         )
327 
328         constructors.forEach { (visibility, group) ->
329             this@classLikeSummaries.summaryNodeGroup(
330                     group,
331                     header = "${visibility.capitalize()} constructors",
332                     summaryId = "${visibility.take(3)}ctors",
333                     tableClass = "responsive",
334                     headerAsRow = true
335             ) {
336                 functionLikeSummaryRow(it)
337             }
338         }
339 
340         this@classLikeSummaries.summaryNodeGroup(
341             enumValues,
342             header = "Enum values",
343             summaryId = "enumvalues",
344             tableClass = "responsive",
345             headerAsRow = true
346         ) {
347             propertyLikeSummaryRow(it, showSignature = false)
348         }
349 
350         functions.forEach { (visibility, group) ->
351             this@classLikeSummaries.summaryNodeGroup(
352                     group,
353                     header = "${visibility.capitalize()} methods",
354                     summaryId = "${visibility.take(3)}methods",
355                     tableClass = "responsive",
356                     headerAsRow = true
357             ) {
358                 functionLikeSummaryRow(it)
359             }
360         }
361 
362         this@classLikeSummaries.summaryNodeGroup(
363                 companionFunctions,
364                 header = "Companion functions",
365                 summaryId = "compmethods",
366                 tableClass = "responsive",
367                 headerAsRow = true
368         ) {
369             functionLikeSummaryRow(it)
370         }
371 
372         this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
373                 superClasses = inheritedFunctionsByReceiver.entries,
374                 header = "Inherited functions",
375                 tableId = "inhmethods",
376                 tableClass = "responsive",
377                 row = { inheritedMemberRow(it) }
378         )
379 
380         this@classLikeSummaries.summaryNodeGroup(
381                 extensionFunctions.entries,
382                 header = "Extension functions",
383                 summaryId = "extmethods",
384                 tableClass = "responsive",
385                 headerAsRow = true
386         ) {
387             extensionRow(it) {
388                 functionLikeSummaryRow(it)
389             }
390         }
391         this@classLikeSummaries.summaryNodeGroup(
392                 inheritedExtensionFunctions.entries,
393                 header = "Inherited extension functions",
394                 summaryId = "inhextmethods",
395                 tableClass = "responsive",
396                 headerAsRow = true
397         ) {
398             extensionRow(it) {
399                 functionLikeSummaryRow(it)
400             }
401         }
402 
403         fields.forEach { (visibility, group) ->
404             this@classLikeSummaries.summaryNodeGroup(
405                 group,
406                 header = "${visibility.capitalize()} fields",
407                 summaryId = "${visibility.take(3)}fields",
408                 tableClass = "responsive",
409                 headerAsRow = true
410             ) { propertyLikeSummaryRow(it) }
411         }
412 
413         this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
414             superClasses = inheritedFieldsByReceiver.entries,
415             header = "Inherited fields",
416             tableId = "inhfields",
417             tableClass = "responsive properties inhtable",
418             row = { inheritedMemberRow(it) }
419         )
420 
421         this@classLikeSummaries.summaryNodeGroup(
422                 properties,
423                 header = "Properties",
424                 summaryId = "properties",
425                 tableClass = "responsive",
426                 headerAsRow = true
427         ) { propertyLikeSummaryRow(it) }
428 
429 
430         this@classLikeSummaries.summaryNodeGroup(
431                 companionProperties,
432                 "Companion properties",
433                 headerAsRow = true
434         ) {
435             propertyLikeSummaryRow(it)
436         }
437 
438         this@classLikeSummaries.expandableSummaryNodeGroupForInheritedMembers(
439                 superClasses = inheritedPropertiesByReceiver.entries,
440                 header = "Inherited properties",
441                 tableId = "inhfields",
442                 tableClass = "responsive properties inhtable",
443                 row = { inheritedMemberRow(it) }
444         )
445 
446         this@classLikeSummaries.summaryNodeGroup(
447                 extensionProperties.entries,
448                 "Extension properties",
449                 headerAsRow = true
450         ) {
451             extensionRow(it) {
452                 propertyLikeSummaryRow(it)
453             }
454         }
455 
456         this@classLikeSummaries.summaryNodeGroup(
457                 inheritedExtensionProperties.entries,
458                 "Inherited extension properties",
459                 headerAsRow = true
460         ) {
461             extensionRow(it) {
462                 propertyLikeSummaryRow(it)
463             }
464         }
465     }
466 
summaryNodeGroupnull467     fun <T> FlowContent.summaryNodeGroup(
468             nodes: Iterable<T>,
469             header: String,
470             headerAsRow: Boolean,
471             summaryId: String,
472             tableClass: String = "responsive",
473             row: TBODY.(T) -> Unit
474     ) {
475         if (nodes.none()) return
476         if (!headerAsRow) {
477             h2 { +header }
478         }
479         table(classes = tableClass) {
480             id = summaryId
481             tbody {
482                 if (headerAsRow) {
483                     developerHeading(header)
484                 }
485                 nodes.forEach { node ->
486                     row(node)
487                 }
488             }
489         }
490     }
491 
contentBlockCodenull492     override fun FlowContent.contentBlockCode(content: ContentBlockCode) {
493         pre {
494             attributes["class"] = "prettyprint"
495             contentNodesToMarkup(content.children)
496         }
497     }
498 
contentBlockSampleCodenull499     override fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) {
500         pre {
501             attributes["class"] = "prettyprint"
502             contentNodesToMarkup(content.importsBlock.children)
503             +"\n\n"
504             contentNodesToMarkup(content.children)
505         }
506     }
507 
generatePackagenull508     override fun generatePackage(page: Page.PackagePage) = templateService.composePage(
509             page,
510             htmlConsumer,
511             headContent = {
512 
513             },
<lambda>null514             bodyContent = {
515                 h1 { +page.node.name }
516                 nodeContent(page.node)
517                 this@composePage.summaryNodeGroup(page.interfaces.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Interfaces", headerAsRow = false) { classLikeRow(it) }
518                 this@composePage.summaryNodeGroup(page.classes.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Classes", headerAsRow = false) { classLikeRow(it) }
519                 this@composePage.summaryNodeGroup(page.exceptions.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Exceptions", headerAsRow = false) { classLikeRow(it) }
520                 this@composePage.summaryNodeGroup(page.typeAliases.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
521                 this@composePage.summaryNodeGroup(page.annotations.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Annotations", headerAsRow = false) { classLikeRow(it) }
522                 this@composePage.summaryNodeGroup(page.enums.sortedBy { it.nameWithOuterClass().toLowerCase() }, "Enums", headerAsRow = false) { classLikeRow(it) }
523 
524                 this@composePage.summaryNodeGroup(
525                         page.constants.sortedBy { it.name },
526                         "Top-level constants summary",
527                         headerAsRow = false
528                 ) {
529                     propertyLikeSummaryRow(it)
530                 }
531 
532                 this@composePage.summaryNodeGroup(
533                         page.functions.sortedBy { it.name },
534                         "Top-level functions summary",
535                         headerAsRow = false
536                 ) {
537                     functionLikeSummaryRow(it)
538                 }
539 
540                 this@composePage.summaryNodeGroup(
541                         page.properties.sortedBy { it.name },
542                         "Top-level properties summary",
543                         headerAsRow = false
544                 ) {
545                     propertyLikeSummaryRow(it)
546                 }
547 
548                 summaryNodeGroupForExtensions("Extension functions summary", page.extensionFunctions.entries)
549                 summaryNodeGroupForExtensions("Extension properties summary", page.extensionProperties.entries)
550 
551                 fullMemberDocs(page.constants.sortedBy { it.name }, "Top-level constants")
552                 fullMemberDocs(page.functions.sortedBy { it.name }, "Top-level functions")
553                 fullMemberDocs(page.properties.sortedBy { it.name }, "Top-level properties")
554                 fullMemberDocs(page.extensionFunctions.values.flatten().sortedBy { it.name }, "Extension functions")
555                 fullMemberDocs(page.extensionProperties.values.flatten().sortedBy { it.name }, "Extension properties")
556             }
557     )
558 
TBODYnull559     private fun TBODY.inheritedXmlAttributeRow(inheritedMember: DocumentationNode) {
560         tr(classes = "api apilevel-${inheritedMember.attributeRef!!.apiLevel.name}") {
561             attributes["data-version-added"] = "${inheritedMember.apiLevel}"
562             td {
563                 code {
564                     a(href = inheritedMember) { +inheritedMember.attributeRef!!.name }
565                 }
566             }
567             td {
568                 attributes["width"] = "100%"
569                 p {
570                     nodeContent(inheritedMember.attributeRef!!, inheritedMember)
571                 }
572             }
573         }
574     }
575 
inheritedMemberRownull576     private fun TBODY.inheritedMemberRow(inheritedMember: DocumentationNode) {
577         tr(classes = "api apilevel-${inheritedMember.apiLevel.name}") {
578             attributes["data-version-added"] = "${inheritedMember.apiLevel}"
579             val type = inheritedMember.detailOrNull(NodeKind.Type)
580             td {
581                 code {
582                     type?.let {
583                         renderedSignature(it, LanguageService.RenderMode.SUMMARY)
584                     }
585                 }
586             }
587             td {
588                 attributes["width"] = "100%"
589                 code {
590                     a(href = inheritedMember) { +inheritedMember.name }
591                     if (inheritedMember.kind == NodeKind.Function) {
592                         shortFunctionParametersList(inheritedMember)
593                     }
594                 }
595                 p {
596                     nodeContent(inheritedMember)
597                 }
598             }
599         }
600     }
601 
expandableSummaryNodeGroupForInheritedMembersnull602     private fun FlowContent.expandableSummaryNodeGroupForInheritedMembers(
603             tableId: String,
604             header: String,
605             tableClass: String,
606             superClasses: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>,
607             row: TBODY.(inheritedMember: DocumentationNode) -> Unit
608     ) {
609         if (superClasses.none()) return
610         table(classes = tableClass) {
611             attributes["id"] = tableId
612             tbody {
613                 developerHeading(header)
614                 superClasses.forEach { (superClass, members) ->
615                     tr(classes = "api apilevel-${superClass.apiLevel.name}") {
616                         td {
617                             attributes["colSpan"] = "2"
618                             div(classes = "expandable jd-inherited-apis") {
619                                 span(classes = "expand-control exw-expanded") {
620                                     +"From class "
621                                     code {
622                                         a(href = superClass) { +superClass.name }
623                                     }
624                                 }
625                                 table(classes = "responsive exw-expanded-content") {
626                                     tbody {
627                                         members.forEach { inheritedMember ->
628                                             row(inheritedMember)
629                                         }
630                                     }
631                                 }
632                             }
633                         }
634                     }
635                 }
636             }
637         }
638     }
639 
FlowContentnull640     private fun FlowContent.summaryNodeGroupForExtensions(
641             header: String,
642             receivers: Set<Map.Entry<DocumentationNode, List<DocumentationNode>>>
643     ) {
644         if (receivers.none()) return
645         h2 { +header }
646         div {
647             receivers.forEach {
648                 table {
649                     tr {
650                         td {
651                             attributes["colSpan"] = "2"
652                             +"For "
653                             a(href = it.key) { +it.key.name }
654                         }
655                     }
656                     it.value.forEach { node ->
657                         tr {
658                             if (node.kind != NodeKind.Constructor) {
659                                 td {
660                                     modifiers(node)
661                                     renderedSignature(node.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
662                                 }
663                             }
664                             td {
665                                 div {
666                                     code {
667                                         val receiver = node.detailOrNull(NodeKind.Receiver)
668                                         if (receiver != null) {
669                                             renderedSignature(receiver.detail(NodeKind.Type), LanguageService.RenderMode.SUMMARY)
670                                             +"."
671                                         }
672                                         a(href = node) { +node.name }
673                                         shortFunctionParametersList(node)
674                                     }
675                                 }
676 
677                                 nodeSummary(node)
678                             }
679                         }
680                     }
681                 }
682             }
683         }
684     }
685 
686 
generatePackageIndexnull687     override fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
688             page,
689             htmlConsumer,
690             headContent = {
691 
692             },
<lambda>null693             bodyContent = {
694                 h1 { +"Package Index" }
695                 table {
696                     tbody {
697                         for (node in page.packages) {
698                             tr {
699                                 td {
700                                     a(href = uriProvider.linkTo(node, uri)) { +node.name }
701                                 }
702                             }
703                         }
704                     }
705                 }
706             }
707     )
708 
generateClassIndexnull709     override fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
710             page,
711             htmlConsumer,
712             headContent = {
713 
714             },
<lambda>null715             bodyContent = {
716                 h1 { +"Class Index" }
717 
718                 p {
719                     +"These are all the API classes. See all "
720                     a(href="packages.html") {
721                         +"API packages."
722                     }
723                 }
724 
725                 div(classes = "jd-letterlist") {
726                     page.classesByFirstLetter.forEach { (letter) ->
727                         +"\n        "
728                         a(href = "#letter_$letter") { +letter }
729                         unsafe {
730                             raw("&nbsp;&nbsp;")
731                         }
732                     }
733                     +"\n    "
734                 }
735 
736                 page.classesByFirstLetter.forEach { (letter, classes) ->
737                     h2 {
738                         id = "letter_$letter"
739                         +letter
740                     }
741                     table {
742                         tbody {
743                             for (node in classes) {
744                                 tr {
745                                     td {
746                                         a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
747                                     }
748                                     td {
749                                         if (!deprecatedIndexSummary(node)) {
750                                             nodeSummary(node)
751                                         }
752                                     }
753                                 }
754                             }
755                         }
756                     }
757                 }
758             }
759     )
760 
classHierarchynull761     override fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
762         table(classes = "jd-inheritance-table") {
763             var level = superclasses.size
764             superclasses.forEach {
765                 tr {
766                     var spaceColumns = max(superclasses.size - 1 - level, 0)
767                     while (spaceColumns > 0) {
768                         td(classes = "jd-inheritance-space") {
769                             +" "
770                         }
771                         spaceColumns--
772                     }
773                     if (it != superclasses.first()) {
774                         td(classes = "jd-inheritance-space") {
775                             +"   ↳"
776                         }
777                     }
778                     td(classes = "jd-inheritance-class-cell") {
779                         attributes["colSpan"] = "$level"
780                         qualifiedTypeReference(it)
781                     }
782                 }
783                 level--
784             }
785         }
786     }
787 
subclassesnull788     override fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
789         if (inheritors.isEmpty()) return
790 
791         // The number of subclasses in collapsed view before truncating and adding a "and xx others".
792         // See https://developer.android.com/reference/android/view/View for an example.
793         val numToShow = 12
794 
795         table(classes = "jd-sumtable jd-sumtable-subclasses") {
796             tbody {
797                 tr {
798                     td {
799                         div(classes = "expandable") {
800                             span(classes = "expand-control") {
801                                 if (direct)
802                                     +"Known Direct Subclasses"
803                                 else
804                                     +"Known Indirect Subclasses"
805                             }
806                             div(classes = "showalways") {
807                                 attributes["id"] = if (direct) "subclasses-direct" else "subclasses-indirect"
808 
809                                 inheritors.take(numToShow).forEach { inheritor ->
810                                     a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
811                                     if (inheritor != inheritors.last()) +", "
812                                 }
813 
814                                 if (inheritors.size > numToShow) {
815                                     +"and ${inheritors.size - numToShow} others."
816                                 }
817                             }
818                             div(classes = "exw-expanded-content") {
819                                 attributes["id"] = if (direct) "subclasses-direct-summary" else "subclasses-indirect-summary"
820                                 table(classes = "jd-sumtable-expando") {
821                                     inheritors.forEach { inheritor ->
822                                         tr(classes = "api api-level-${inheritor.apiLevel.name}") {
823                                             attributes["data-version-added"] = inheritor.apiLevel.name
824                                             td(classes = "jd-linkcol") {
825                                                 a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
826                                             }
827                                             td(classes = "jd-descrcol") {
828                                                 attributes["width"] = "100%"
829                                                 nodeSummary(inheritor)
830                                             }
831                                         }
832                                     }
833                                 }
834                             }
835                         }
836                     }
837                 }
838             }
839         }
840     }
841 
842 
firstSentenceOfSummarynull843     fun DocumentationNode.firstSentenceOfSummary(): ContentNode {
844 
845         fun Sequence<ContentNode>.flatten(): Sequence<ContentNode> {
846             return flatMap {
847                 when (it) {
848                     is ContentParagraph -> it.children.asSequence().flatten()
849                     else -> sequenceOf(it)
850                 }
851             }
852         }
853 
854         fun ContentNode.firstSentence(): ContentText? = when(this) {
855             is ContentText -> ContentText(text.firstSentence())
856             else -> null
857         }
858 
859         val elements = sequenceOf(summary).flatten()
860         fun containsDot(it: ContentNode) = (it as? ContentText)?.text?.contains(".") == true
861 
862         val paragraph = ContentParagraph()
863         (elements.takeWhile { !containsDot(it) } + elements.firstOrNull { containsDot(it) }?.firstSentence()).forEach {
864             if (it != null) {
865                 paragraph.append(it)
866             }
867         }
868         if (paragraph.isEmpty()) {
869             return ContentEmpty
870         }
871 
872         return paragraph
873     }
874 
firstSentencenull875     fun DocumentationNode.firstSentence(): String {
876         val sb = StringBuilder()
877         addContentNodeToStringBuilder(content, sb)
878         return sb.toString().firstSentence()
879     }
880 
addContentNodesToStringBuildernull881     private fun addContentNodesToStringBuilder(content: List<ContentNode>, sb: StringBuilder): Unit =
882         content.forEach { addContentNodeToStringBuilder(it, sb) }
883 
addContentNodeToStringBuildernull884     private fun addContentNodeToStringBuilder(content: ContentNode, sb: StringBuilder) {
885         when (content) {
886             is ContentText -> sb.appendWith(content.text)
887             is ContentSymbol -> sb.appendWith(content.text)
888             is ContentKeyword -> sb.appendWith(content.text)
889             is ContentIdentifier -> sb.appendWith(content.text)
890             is ContentEntity -> sb.appendWith(content.text)
891 
892             is ContentHeading -> addContentNodesToStringBuilder(content.children, sb)
893             is ContentStrong -> addContentNodesToStringBuilder(content.children, sb)
894             is ContentStrikethrough -> addContentNodesToStringBuilder(content.children, sb)
895             is ContentEmphasis -> addContentNodesToStringBuilder(content.children, sb)
896             is ContentOrderedList -> addContentNodesToStringBuilder(content.children, sb)
897             is ContentUnorderedList -> addContentNodesToStringBuilder(content.children, sb)
898             is ContentListItem -> addContentNodesToStringBuilder(content.children, sb)
899             is ContentCode -> addContentNodesToStringBuilder(content.children, sb)
900             is ContentBlockSampleCode -> addContentNodesToStringBuilder(content.children, sb)
901             is ContentBlockCode -> addContentNodesToStringBuilder(content.children, sb)
902             is ContentParagraph -> addContentNodesToStringBuilder(content.children, sb)
903             is ContentNodeLink -> addContentNodesToStringBuilder(content.children, sb)
904             is ContentBookmark -> addContentNodesToStringBuilder(content.children, sb)
905             is ContentExternalLink -> addContentNodesToStringBuilder(content.children, sb)
906             is ContentLocalLink -> addContentNodesToStringBuilder(content.children, sb)
907             is ContentSection -> { }
908             is ContentBlock -> addContentNodesToStringBuilder(content.children, sb)
909         }
910     }
911 
StringBuildernull912     private fun StringBuilder.appendWith(text: String, delimiter: String = " ") {
913         if (this.length == 0) {
914             append(text)
915         } else {
916             append(delimiter)
917             append(text)
918         }
919     }
920 }
921 
developerHeadingnull922 fun TBODY.developerHeading(header: String) {
923     tr {
924         th {
925             attributes["colSpan"] = "2"
926             +header
927         }
928     }
929 }
930 
<lambda>null931 class DacFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin {
932     override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class
933 
934     override val outlineFactoryClass = DacOutlineFormatter::class
935     override val languageServiceClass = KotlinLanguageService::class
936     override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
937     override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class
938     override val sampleProcessingService = DevsiteSampleProcessingService::class
939 }
940 
941 
<lambda>null942 class DacAsJavaFormatDescriptor : JavaLayoutHtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava {
943     override val templateServiceClass: KClass<out JavaLayoutHtmlTemplateService> = DevsiteHtmlTemplateService::class
944 
945     override val outlineFactoryClass = DacOutlineFormatter::class
946     override val languageServiceClass = NewJavaLanguageService::class
947     override val packageListServiceClass: KClass<out PackageListService> = JavaLayoutHtmlPackageListService::class
948     override val outputBuilderFactoryClass: KClass<out JavaLayoutHtmlFormatOutputBuilderFactory> = DevsiteLayoutHtmlFormatOutputBuilderFactoryImpl::class
949 }
950