• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package org.jetbrains.dokka
2 
3 import org.jetbrains.dokka.LanguageService.RenderMode
4 import java.util.*
5 
6 data class FormatLink(val text: String, val href: String)
7 
8 abstract class StructuredOutputBuilder(val to: StringBuilder,
9                                        val location: Location,
10                                        val generator: NodeLocationAwareGenerator,
11                                        val languageService: LanguageService,
12                                        val extension: String,
13                                        val impliedPlatforms: List<String>) : FormattedOutputBuilder {
14 
15     protected fun DocumentationNode.location() = generator.location(this)
16 
17     protected fun wrap(prefix: String, suffix: String, body: () -> Unit) {
18         to.append(prefix)
19         body()
20         to.append(suffix)
21     }
22 
23     protected fun wrapIfNotEmpty(prefix: String, suffix: String, body: () -> Unit, checkEndsWith: Boolean = false) {
24         val startLength = to.length
25         to.append(prefix)
26         body()
27         if (checkEndsWith && to.endsWith(suffix)) {
28             to.setLength(to.length - suffix.length)
29         } else if (to.length > startLength + prefix.length) {
30             to.append(suffix)
31         } else {
32             to.setLength(startLength)
33         }
34     }
35 
36     protected fun wrapInTag(tag: String,
37                             body: () -> Unit,
38                             newlineBeforeOpen: Boolean = false,
39                             newlineAfterOpen: Boolean = false,
40                             newlineAfterClose: Boolean = false) {
41         if (newlineBeforeOpen && !to.endsWith('\n')) to.appendln()
42         to.append("<$tag>")
43         if (newlineAfterOpen) to.appendln()
44         body()
45         to.append("</$tag>")
46         if (newlineAfterClose) to.appendln()
47     }
48 
49     protected abstract fun ensureParagraph()
50 
51     open fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) = appendBlockCode(language, body)
52     abstract fun appendBlockCode(language: String, body: () -> Unit)
53     abstract fun appendHeader(level: Int = 1, body: () -> Unit)
54     abstract fun appendParagraph(body: () -> Unit)
55 
56     open fun appendSoftParagraph(body: () -> Unit) {
57         ensureParagraph()
58         body()
59     }
60 
61     abstract fun appendLine()
62     abstract fun appendAnchor(anchor: String)
63 
64     abstract fun appendTable(vararg columns: String, body: () -> Unit)
65     abstract fun appendTableBody(body: () -> Unit)
66     abstract fun appendTableRow(body: () -> Unit)
67     abstract fun appendTableCell(body: () -> Unit)
68 
69     abstract fun appendText(text: String)
70 
71     open fun appendSinceKotlin(version: String) {
72         appendParagraph {
73             appendText("Available since Kotlin: ")
74             appendCode { appendText(version) }
75         }
76     }
77 
78     open fun appendSectionWithTag(section: ContentSection) {
79         appendParagraph {
80             appendStrong { appendText(section.tag) }
81             appendLine()
82             appendContent(section)
83         }
84     }
85 
86     open fun appendSymbol(text: String) {
87         appendText(text)
88     }
89 
90     open fun appendKeyword(text: String) {
91         appendText(text)
92     }
93 
94     open fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) {
95         appendText(text)
96     }
97 
98     fun appendEntity(text: String) {
99         to.append(text)
100     }
101 
102     abstract fun appendLink(href: String, body: () -> Unit)
103 
104     open fun appendLink(link: FormatLink) {
105         appendLink(link.href) { appendText(link.text) }
106     }
107 
108     abstract fun appendStrong(body: () -> Unit)
109     abstract fun appendStrikethrough(body: () -> Unit)
110     abstract fun appendEmphasis(body: () -> Unit)
111     abstract fun appendCode(body: () -> Unit)
112     abstract fun appendUnorderedList(body: () -> Unit)
113     abstract fun appendOrderedList(body: () -> Unit)
114     abstract fun appendListItem(body: () -> Unit)
115 
116     abstract fun appendBreadcrumbSeparator()
117     abstract fun appendNonBreakingSpace()
118     open fun appendSoftLineBreak() {
119     }
120 
121     open fun appendIndentedSoftLineBreak() {
122     }
123 
124     fun appendContent(content: List<ContentNode>) {
125         for (contentNode in content) {
126             appendContent(contentNode)
127         }
128     }
129 
130     open fun appendContent(content: ContentNode) {
131         when (content) {
132             is ContentText -> appendText(content.text)
133             is ContentSymbol -> appendSymbol(content.text)
134             is ContentKeyword -> appendKeyword(content.text)
135             is ContentIdentifier -> appendIdentifier(content.text, content.kind, content.signature)
136             is ContentNonBreakingSpace -> appendNonBreakingSpace()
137             is ContentSoftLineBreak -> appendSoftLineBreak()
138             is ContentIndentedSoftLineBreak -> appendIndentedSoftLineBreak()
139             is ContentEntity -> appendEntity(content.text)
140             is ContentStrong -> appendStrong { appendContent(content.children) }
141             is ContentStrikethrough -> appendStrikethrough { appendContent(content.children) }
142             is ContentCode -> appendCode { appendContent(content.children) }
143             is ContentEmphasis -> appendEmphasis { appendContent(content.children) }
144             is ContentUnorderedList -> appendUnorderedList { appendContent(content.children) }
145             is ContentOrderedList -> appendOrderedList { appendContent(content.children) }
146             is ContentListItem -> appendListItem {
147                 val child = content.children.singleOrNull()
148                 if (child is ContentParagraph) {
149                     appendContent(child.children)
150                 } else {
151                     appendContent(content.children)
152                 }
153             }
154 
155             is ContentNodeLink -> {
156                 val node = content.node
157                 val linkTo = if (node != null) locationHref(location, node) else "#"
158                 appendLinkIfNotThisPage(linkTo, content)
159             }
160             is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content)
161 
162             is ContentParagraph -> {
163                 if (!content.isEmpty()) {
164                     appendParagraph { appendContent(content.children) }
165                 }
166             }
167 
168             is ContentSpecialReference -> wrapInTag(tag = "aside class=\"note\"", body = {
169                 if (!content.isEmpty()) {
170                     appendContent(content.children)
171                 }
172             })
173 
174             is ContentBlockSampleCode, is ContentBlockCode -> {
175                 content as ContentBlockCode
176                 fun ContentBlockCode.appendBlockCodeContent() {
177                     children
178                             .dropWhile { it is ContentText && it.text.isBlank() }
179                             .forEach { appendContent(it) }
180                 }
181                 when (content) {
182                     is ContentBlockSampleCode ->
183                         appendSampleBlockCode(content.language, content.importsBlock::appendBlockCodeContent, { content.appendBlockCodeContent() })
184                     is ContentBlockCode ->
185                         appendBlockCode(content.language, { content.appendBlockCodeContent() })
186                 }
187             }
188             is ContentHeading -> appendHeader(content.level) { appendContent(content.children) }
189             is ContentBlock -> appendContent(content.children)
190         }
191     }
192 
193     private fun appendLinkIfNotThisPage(href: String, content: ContentBlock) {
194         if (href == ".") {
195             appendContent(content.children)
196         } else {
197             appendLink(href) { appendContent(content.children) }
198         }
199     }
200 
201     open fun link(from: DocumentationNode,
202                   to: DocumentationNode,
203                   name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink = link(from, to, extension, name)
204 
205     open fun link(from: DocumentationNode,
206                   to: DocumentationNode,
207                   extension: String,
208                   name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink {
209         if (to.owner?.kind == NodeKind.GroupNode)
210             return link(from, to.owner!!, extension, name)
211 
212         if (from.owner?.kind == NodeKind.GroupNode)
213             return link(from.owner!!, to, extension, name)
214 
215         return FormatLink(name(to), from.location().relativePathTo(to.location()))
216     }
217 
218     fun locationHref(from: Location, to: DocumentationNode): String {
219         val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to
220         if (topLevelPage != null) {
221             val signature = to.detailOrNull(NodeKind.Signature)
222             return from.relativePathTo(topLevelPage.location(), signature?.name ?: to.name)
223         }
224         return from.relativePathTo(to.location())
225     }
226 
227     private fun DocumentationNode.isModuleOrPackage(): Boolean =
228             kind == NodeKind.Module || kind == NodeKind.Package
229 
230     protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) {
231         block()
232     }
233 
234     protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) {
235         block()
236     }
237 
238     protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) {
239         appendTableRow(block)
240     }
241 
242     protected open fun appendPlatforms(platforms: Set<String>) {
243         if (platforms.isNotEmpty()) {
244             appendLine()
245             appendText(platforms.joinToString(prefix = "(", postfix = ")"))
246         }
247     }
248 
249     protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) {
250         for ((index, item) in path.withIndex()) {
251             if (index > 0) {
252                 appendBreadcrumbSeparator()
253             }
254             appendLink(item)
255         }
256     }
257 
258     fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> =
259             sections.filter { it.subjectName != null }.groupBy { it.tag }
260 
261     private fun ContentNode.appendSignature() {
262         if (this is ContentBlock && this.isEmpty()) {
263             return
264         }
265 
266         val signatureAsCode = ContentCode()
267         signatureAsCode.append(this)
268         appendContent(signatureAsCode)
269     }
270 
271     open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) {
272         open fun build() {
273             val breakdownByLocation = nodes.groupBy { node ->
274                 node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct()
275             }
276 
277             for ((path, nodes) in breakdownByLocation) {
278                 if (!noHeader && path.isNotEmpty()) {
279                     appendBreadcrumbs(path)
280                     appendLine()
281                     appendLine()
282                 }
283                 appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass })
284             }
285         }
286 
287         private fun appendLocation(nodes: Iterable<DocumentationNode>) {
288             val singleNode = nodes.singleOrNull()
289             if (singleNode != null && singleNode.isModuleOrPackage()) {
290                 if (singleNode.kind == NodeKind.Package) {
291                     val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name
292                     appendHeader(2) { appendText("Package $packageName") }
293                 }
294                 singleNode.appendPlatforms()
295                 appendContent(singleNode.content)
296             } else {
297                 val breakdownByName = nodes.groupBy { node -> node.name }
298                 for ((name, items) in breakdownByName) {
299                     if (!noHeader)
300                         appendHeader { appendText(name) }
301                     appendDocumentation(items, singleNode != null)
302                 }
303             }
304         }
305 
306         private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) {
307             val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> node.content }
308 
309             if (breakdownBySummary.size == 1) {
310                 formatOverloadGroup(breakdownBySummary.values.single(), isSingleNode)
311             } else {
312                 for ((_, items) in breakdownBySummary) {
313 
314                     appendAsOverloadGroup(to, platformsOfItems(items)) {
315                         formatOverloadGroup(items)
316                     }
317 
318                 }
319             }
320         }
321 
322         private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) {
323             for ((index, item) in items.withIndex()) {
324                 if (index > 0) appendLine()
325                 val rendered = languageService.render(item)
326                 item.detailOrNull(NodeKind.Signature)?.let {
327                     if (item.kind !in NodeKind.classLike || !isSingleNode)
328                         appendAnchor(it.name)
329                 }
330                 appendAsSignature(rendered) {
331                     appendCode { appendContent(rendered) }
332                     item.appendSourceLink()
333                 }
334                 item.appendOverrides()
335                 item.appendDeprecation()
336                 item.appendPlatforms()
337             }
338             // All items have exactly the same documentation, so we can use any item to render it
339             val item = items.first()
340             item.details(NodeKind.OverloadGroupNote).forEach {
341                 appendContent(it.content)
342             }
343 
344             appendContent(item.content.summary)
345             item.appendDescription()
346         }
347 
348         private fun DocumentationNode.appendSourceLink() {
349             val sourceUrl = details(NodeKind.SourceUrl).firstOrNull()
350             if (sourceUrl != null) {
351                 to.append(" ")
352                 appendLink(sourceUrl.name) { to.append("(source)") }
353             }
354         }
355 
356         private fun DocumentationNode.appendOverrides() {
357             overrides.forEach {
358                 appendParagraph {
359                     to.append("Overrides ")
360                     val location = location().relativePathTo(it.location())
361 
362                     appendLink(FormatLink(it.owner!!.name + "." + it.name, location))
363                 }
364             }
365         }
366 
367         private fun DocumentationNode.appendDeprecation() {
368             if (deprecation != null) {
369                 val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull()
370                 val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull()
371                 appendLine()
372                 if (deprecationValue != null) {
373                     appendStrong { to.append("Deprecated:") }
374                     appendText(" " + deprecationValue.name.removeSurrounding("\""))
375                     appendLine()
376                     appendLine()
377                 } else if (deprecation?.content != Content.Empty) {
378                     appendStrong { to.append("Deprecated:") }
379                     to.append(" ")
380                     appendContent(deprecation!!.content)
381                 } else {
382                     appendStrong { to.append("Deprecated") }
383                     appendLine()
384                     appendLine()
385                 }
386             }
387         }
388 
389         private fun DocumentationNode.appendPlatforms() {
390             val platforms = if (isModuleOrPackage())
391                 platformsToShow.toSet() + platformsOfItems(members)
392             else
393                 platformsToShow
394 
395             if (platforms.isEmpty()) return
396 
397             appendParagraph {
398                 appendStrong { to.append("Platform and version requirements:") }
399                 to.append(" " + platforms.joinToString())
400             }
401         }
402 
403         protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> {
404             val platforms = items.asSequence().map {
405                 when (it.kind) {
406                     NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members)
407                     else -> it.platformsToShow.toSet()
408                 }
409             }
410 
411             fun String.isKotlinVersion() = this.startsWith("Kotlin")
412 
413             // Calculating common platforms for items
414             return platforms.reduce { result, platformsOfItem ->
415                 val otherKotlinVersion = result.find { it.isKotlinVersion() }
416                 val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() }
417 
418                 // When no Kotlin version specified, it means that version is 1.0
419                 if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) {
420                     val allKotlinVersions = (kotlinVersions + otherKotlinVersion).distinct()
421 
422                     val minVersion = allKotlinVersions.min()!!
423                     val resultVersion = when {
424                         allKotlinVersions.size == 1 -> allKotlinVersions.single()
425                         minVersion.endsWith("+") -> minVersion
426                         else -> minVersion + "+"
427                     }
428 
429                     result.intersect(otherPlatforms) + resultVersion
430                 } else {
431                     result.intersect(platformsOfItem)
432                 }
433             }
434         }
435 
436         val DocumentationNode.platformsToShow: List<String>
437             get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it }
438 
439         private fun DocumentationNode.appendDescription() {
440             if (content.description != ContentEmpty) {
441                 appendContent(content.description)
442             }
443             content.getSectionsWithSubjects().forEach {
444                 appendSectionWithSubject(it.key, it.value)
445             }
446 
447             for (section in content.sections.filter { it.subjectName == null }) {
448                 appendSectionWithTag(section)
449             }
450         }
451 
452         fun appendSectionWithSubject(title: String, subjectSections: List<ContentSection>) {
453             appendHeader(3) { appendText(title) }
454             subjectSections.forEach {
455                 val subjectName = it.subjectName
456                 if (subjectName != null) {
457                     appendSoftParagraph {
458                         appendAnchor(subjectName)
459                         appendCode { to.append(subjectName) }
460                         to.append(" - ")
461                         appendContent(it)
462                     }
463                 }
464             }
465         }
466     }
467 
468     inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) {
469 
470         override fun build() {
471             val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }
472 
473             appendBreadcrumbs(breakdownByLocation)
474             appendLine()
475             appendLine()
476             appendHeader { appendText(node.name) }
477 
478             fun DocumentationNode.priority(): Int = when (kind) {
479                 NodeKind.TypeAlias -> 1
480                 NodeKind.Class -> 2
481                 else -> 3
482             }
483 
484             for (member in node.members.sortedBy(DocumentationNode::priority)) {
485 
486                 appendAsOverloadGroup(to, platformsOfItems(listOf(member))) {
487                     formatSubNodeOfGroup(member)
488                 }
489 
490             }
491         }
492 
493         fun formatSubNodeOfGroup(member: DocumentationNode) {
494             SingleNodePageBuilder(member, true).build()
495         }
496     }
497 
498     inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false)
499         : PageBuilder(listOf(node), noHeader) {
500 
501         override fun build() {
502             super.build()
503 
504             if (node.kind == NodeKind.ExternalClass) {
505                 appendSection("Extensions for ${node.name}", node.members)
506                 return
507             }
508 
509             fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> {
510                 return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) }
511             }
512 
513             fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> {
514                 return membersOrGroupMembers { it.kind == kind }
515             }
516 
517             appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true)
518             appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception })
519             appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass))
520             appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception))
521             appendSection("Type Aliases", node.membersOrGroupMembers(NodeKind.TypeAlias))
522             appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass))
523             appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true)
524             appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true)
525             appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true)
526             appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property))
527             appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true)
528             appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function))
529             appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true)
530             appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property))
531             appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true)
532             appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function))
533             appendSection("Other members", node.members.filter {
534                 it.kind !in setOf(
535                         NodeKind.Class,
536                         NodeKind.Interface,
537                         NodeKind.Enum,
538                         NodeKind.Object,
539                         NodeKind.AnnotationClass,
540                         NodeKind.Exception,
541                         NodeKind.TypeAlias,
542                         NodeKind.Constructor,
543                         NodeKind.Property,
544                         NodeKind.Package,
545                         NodeKind.Function,
546                         NodeKind.CompanionObjectProperty,
547                         NodeKind.CompanionObjectFunction,
548                         NodeKind.ExternalClass,
549                         NodeKind.EnumItem,
550                         NodeKind.AllTypes,
551                         NodeKind.GroupNode
552                 )
553             })
554 
555             val allExtensions = node.extensions
556             appendSection("Extension Properties", allExtensions.filter { it.kind == NodeKind.Property })
557             appendSection("Extension Functions", allExtensions.filter { it.kind == NodeKind.Function })
558             appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty })
559             appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction })
560             appendSection("Inheritors",
561                     node.inheritors.filter { it.kind != NodeKind.EnumItem })
562 
563             if (node.kind == NodeKind.Module) {
564                 appendHeader(3) { to.append("Index") }
565                 node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes ->
566                     appendLink(link(node, allTypes, { "All Types" }))
567                 }
568             }
569         }
570 
571         private fun appendSection(caption: String, members: List<DocumentationNode>,
572                                   sortMembers: Boolean = true,
573                                   omitSamePlatforms: Boolean = false,
574                                   platformsBasedOnMembers: Boolean = false) {
575             if (members.isEmpty()) return
576 
577             appendHeader(3) { appendText(caption) }
578 
579             val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members
580             val membersMap = children.groupBy { link(node, it) }
581 
582 
583 
584             appendTable("Name", "Summary") {
585                 appendTableBody {
586                     for ((memberLocation, members) in membersMap) {
587                         val elementPlatforms = platformsOfItems(members, omitSamePlatforms)
588                         val platforms = if (platformsBasedOnMembers)
589                             members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms
590                         else
591                             elementPlatforms
592                         appendIndexRow(platforms) {
593                             appendTableCell {
594                                 appendParagraph {
595                                     appendLink(memberLocation)
596                                     if (members.singleOrNull()?.kind != NodeKind.ExternalClass) {
597                                         appendPlatforms(platforms)
598                                     }
599                                 }
600                             }
601                             appendTableCell {
602                                 val breakdownBySummary = members.groupBy { it.summary }
603                                 for ((summary, items) in breakdownBySummary) {
604                                     appendSummarySignatures(items)
605                                     appendContent(summary)
606                                 }
607                             }
608                         }
609                     }
610                 }
611             }
612         }
613 
614         private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> {
615             val platforms = platformsOfItems(items)
616             if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) {
617                 return platforms
618             }
619             return emptySet()
620         }
621 
622         private fun appendSummarySignatures(items: List<DocumentationNode>) {
623             val summarySignature = languageService.summarizeSignatures(items)
624             if (summarySignature != null) {
625                 appendAsSignature(summarySignature) {
626                     summarySignature.appendSignature()
627                 }
628                 return
629             }
630             val renderedSignatures = items.map { languageService.render(it, RenderMode.SUMMARY) }
631             renderedSignatures.subList(0, renderedSignatures.size - 1).forEach {
632                 appendAsSignature(it) {
633                     it.appendSignature()
634                 }
635                 appendLine()
636             }
637             appendAsSignature(renderedSignatures.last()) {
638                 renderedSignatures.last().appendSignature()
639             }
640         }
641     }
642 
643     inner class AllTypesNodeBuilder(val node: DocumentationNode)
644         : PageBuilder(listOf(node)) {
645 
646         override fun build() {
647             appendContent(node.owner!!.summary)
648             appendHeader(3) { to.append("All Types") }
649 
650             appendTable("Name", "Summary") {
651                 appendTableBody {
652                     for (type in node.members) {
653                         appendTableRow {
654                             appendTableCell {
655                                 appendLink(link(node, type) {
656                                     if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName()
657                                 })
658                                 if (type.kind == NodeKind.ExternalClass) {
659                                     val packageName = type.owner?.name
660                                     if (packageName != null) {
661                                         appendText(" (extensions in package $packageName)")
662                                     }
663                                 }
664                             }
665                             appendTableCell {
666                                 appendContent(type.summary)
667                             }
668                         }
669                     }
670                 }
671             }
672         }
673     }
674 
675     override fun appendNodes(nodes: Iterable<DocumentationNode>) {
676         val singleNode = nodes.singleOrNull()
677         when (singleNode?.kind) {
678             NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build()
679             NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build()
680             null -> PageBuilder(nodes).build()
681             else -> SingleNodePageBuilder(singleNode).build()
682         }
683     }
684 }
685 
686 abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator,
687                                        val languageService: LanguageService,
688                                        override val extension: String,
689                                        override final val linkExtension: String = extension) : FormatService {
690 
691 }
692