blob: b1e614f6f4a76c047043f1c8d5f8c550238737a5 [file] [log] [blame]
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +04001package org.jetbrains.dokka
2
3import java.util.LinkedHashMap
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +04004
5public data class FormatLink(val text: String, val location: Location)
6
7public abstract class StructuredFormatService(val locationService: LocationService,
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +03008 val resolutionService: ResolutionService,
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +04009 val languageService: LanguageService) : FormatService {
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040010
11 abstract public fun appendBlockCode(to: StringBuilder, line: String)
12 abstract public fun appendBlockCode(to: StringBuilder, lines: Iterable<String>)
13 abstract public fun appendHeader(to: StringBuilder, text: String, level: Int = 1)
14 abstract public fun appendText(to: StringBuilder, text: String)
15 abstract public fun appendLine(to: StringBuilder, text: String)
16 public abstract fun appendLine(to: StringBuilder)
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +040017
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040018 public abstract fun appendTable(to: StringBuilder, body: () -> Unit)
19 public abstract fun appendTableHeader(to: StringBuilder, body: () -> Unit)
20 public abstract fun appendTableBody(to: StringBuilder, body: () -> Unit)
21 public abstract fun appendTableRow(to: StringBuilder, body: () -> Unit)
22 public abstract fun appendTableCell(to: StringBuilder, body: () -> Unit)
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +040023
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040024 public abstract fun formatText(text: String): String
25 public abstract fun formatLink(text: String, location: Location): String
26 public open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.location)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040027 public abstract fun formatBold(text: String): String
28 public abstract fun formatCode(code: String): String
29 public abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String
30
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +040031 open fun formatText(nodes: Iterable<ContentNode>): String {
32 return nodes.map { formatText(it) }.join("")
33 }
34
35 open fun formatText(text: ContentNode): String {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030036 return StringBuilder {
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +040037 for (node in text.children) {
38 when (node) {
39 is ContentText -> append(node.text)
40 is ContentEmphasis -> append(formatBold(formatText(node.children)))
41 else -> append(formatText(node.children))
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030042 }
43 }
44 }.toString()
45 }
46
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040047 open public fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension)
48
49 open public fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink {
50 return FormatLink(to.name, locationService.relativeLocation(from, to, extension))
51 }
52
53 open public fun appendDescription(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030054 val described = nodes.filter { !it.doc.isEmpty }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040055 if (described.any()) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040056 val single = described.size == 1
57 appendHeader(to, "Description", 3)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040058 for (node in described) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040059 if (!single) {
60 appendBlockCode(to, languageService.render(node))
61 }
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040062 appendLine(to, formatText(node.doc.description))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040063 appendLine(to)
Ilya Ryzhenkovf7bab782014-09-25 22:20:58 +040064 for ((label, section) in node.doc.sections) {
65 if (label.startsWith("$"))
66 continue
67 appendLine(to, formatBold(formatText(label)))
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +040068 appendLine(to, formatText(section))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040069 appendLine(to)
70 }
71 }
72 }
73 }
74
75 open public fun appendSummary(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
76 val breakdownBySummary = nodes.groupByTo(LinkedHashMap()) { node ->
77 node.doc.summary
78 }
79
80 for ((summary, items) in breakdownBySummary) {
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +040081 appendBlockCode(to, items.map { languageService.render(it) })
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030082 appendLine(to, formatText(summary))
83 appendLine(to)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040084 }
85 }
86
87 open public fun appendLocation(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +040088 val breakdownByName = nodes.groupBy { node -> node.name }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040089 for ((name, items) in breakdownByName) {
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040090 appendHeader(to, formatText(name))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040091 appendSummary(to, items)
92 appendDescription(to, items)
93 }
94 }
95
96 override fun appendNodes(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +040097 val breakdownByLocation = nodes.groupBy { node ->
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040098 formatBreadcrumbs(node.path.map { link(node, it) })
99 }
100
101 for ((breadcrumbs, items) in breakdownByLocation) {
102 appendLine(to, breadcrumbs)
103 appendLine(to)
104 appendLocation(to, items)
105 }
106
107 for (node in nodes) {
108 if (node.members.any()) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +0400109 appendHeader(to, "Members", 3)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400110
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400111 val children = node.members.sortBy { it.name }
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +0400112 val membersMap = children.groupBy { link(node, it) }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400113
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400114 appendTable(to) {
115 appendTableBody(to) {
116 for ((location, members) in membersMap) {
117 appendTableRow(to) {
118 appendTableCell(to) {
119 appendText(to, formatLink(location))
120 }
121 appendTableCell(to) {
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +0400122 val breakdownBySummary = members.groupBy { it.doc.summary }
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400123 for ((summary, items) in breakdownBySummary) {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +0300124 val signatures = items.map { formatCode("${languageService.render(it)}") }
125 for (signature in signatures) {
126 appendText(to, signature)
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400127 }
128
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +0300129 if (!summary.isEmpty()) {
130 appendText(to, formatText(summary))
131 }
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400132 }
133 }
134 }
Ilya Ryzhenkove8447fd2014-07-15 16:37:50 +0400135 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400136 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400137 }
138 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400139 }
140 }
141
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +0400142 abstract public fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode)
143 abstract public fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>)
144
145 override public fun appendOutline(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
146 for (node in nodes) {
147 appendOutlineHeader(to, node)
148 if (node.members.any()) {
149 appendOutlineChildren(to, node.members)
150 }
151 }
152 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400153}