<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(" ")
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