blob: ea8ed36e828e315e1f1f6e6764981d35016b8e02 [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
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040025 public abstract fun formatSymbol(text: String): String
26 public abstract fun formatKeyword(text: String): String
27 public abstract fun formatIdentifier(text: String): String
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040028 public abstract fun formatLink(text: String, location: Location): String
29 public open fun formatLink(link: FormatLink): String = formatLink(formatText(link.text), link.location)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040030 public abstract fun formatBold(text: String): String
31 public abstract fun formatCode(code: String): String
32 public abstract fun formatBreadcrumbs(items: Iterable<FormatLink>): String
33
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +040034 open fun formatText(nodes: Iterable<ContentNode>): String {
35 return nodes.map { formatText(it) }.join("")
36 }
37
Ilya Ryzhenkove93b6292014-09-29 22:29:41 +040038 open fun formatText(node: ContentNode): String {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030039 return StringBuilder {
Ilya Ryzhenkove93b6292014-09-29 22:29:41 +040040 when (node) {
41 is ContentText -> append(node.text)
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040042 is ContentSymbol -> append(formatSymbol(node.text))
43 is ContentKeyword -> append(formatKeyword(node.text))
44 is ContentIdentifier -> append(formatIdentifier(node.text))
Ilya Ryzhenkove93b6292014-09-29 22:29:41 +040045 is ContentEmphasis -> append(formatBold(formatText(node.children)))
46 else -> append(formatText(node.children))
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030047 }
48 }.toString()
49 }
50
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040051 open public fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension)
52
53 open public fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink {
54 return FormatLink(to.name, locationService.relativeLocation(from, to, extension))
55 }
56
57 open public fun appendDescription(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030058 val described = nodes.filter { !it.doc.isEmpty }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040059 if (described.any()) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040060 val single = described.size == 1
61 appendHeader(to, "Description", 3)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040062 for (node in described) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040063 if (!single) {
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040064 appendBlockCode(to, formatText(languageService.render(node)))
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040065 }
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040066 appendLine(to, formatText(node.doc.description))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040067 appendLine(to)
Ilya Ryzhenkovf7bab782014-09-25 22:20:58 +040068 for ((label, section) in node.doc.sections) {
69 if (label.startsWith("$"))
70 continue
71 appendLine(to, formatBold(formatText(label)))
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +040072 appendLine(to, formatText(section))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040073 appendLine(to)
74 }
75 }
76 }
77 }
78
79 open public fun appendSummary(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
80 val breakdownBySummary = nodes.groupByTo(LinkedHashMap()) { node ->
81 node.doc.summary
82 }
83
84 for ((summary, items) in breakdownBySummary) {
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040085 items.forEach {
86 appendBlockCode(to, formatText(languageService.render(it)))
87 }
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030088 appendLine(to, formatText(summary))
89 appendLine(to)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040090 }
91 }
92
93 open public fun appendLocation(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +040094 val breakdownByName = nodes.groupBy { node -> node.name }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040095 for ((name, items) in breakdownByName) {
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040096 appendHeader(to, formatText(name))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040097 appendSummary(to, items)
98 appendDescription(to, items)
99 }
100 }
101
102 override fun appendNodes(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +0400103 val breakdownByLocation = nodes.groupBy { node ->
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400104 formatBreadcrumbs(node.path.map { link(node, it) })
105 }
106
107 for ((breadcrumbs, items) in breakdownByLocation) {
108 appendLine(to, breadcrumbs)
109 appendLine(to)
110 appendLocation(to, items)
111 }
112
113 for (node in nodes) {
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400114 appendSection("Members", node.members, node, to)
115 appendSection("Extensions", node.extensions, node, to)
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +0400116 appendSection("Inheritors", node.inheritors, node, to)
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400117 appendSection("Links", node.links, node, to)
118 }
119 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400120
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400121 private fun StructuredFormatService.appendSection(caption: String, nodes: List<DocumentationNode>, node: DocumentationNode, to: StringBuilder) {
122 if (nodes.any()) {
123 appendHeader(to, caption, 3)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400124
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400125 val children = nodes.sortBy { it.name }
126 val membersMap = children.groupBy { link(node, it) }
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400127
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400128 appendTable(to) {
129 appendTableBody(to) {
130 for ((location, members) in membersMap) {
131 appendTableRow(to) {
132 appendTableCell(to) {
133 appendText(to, formatLink(location))
134 }
135 appendTableCell(to) {
136 val breakdownBySummary = members.groupBy { it.doc.summary }
137 for ((summary, items) in breakdownBySummary) {
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +0400138 for (signature in items) {
139 appendBlockCode(to, formatText(languageService.render(signature)))
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400140 }
141
142 if (!summary.isEmpty()) {
143 appendText(to, formatText(summary))
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400144 }
145 }
146 }
Ilya Ryzhenkove8447fd2014-07-15 16:37:50 +0400147 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400148 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400149 }
150 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400151 }
152 }
153
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +0400154 abstract public fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode)
155 abstract public fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>)
156
157 override public fun appendOutline(to: StringBuilder, nodes: Iterable<DocumentationNode>) {
158 for (node in nodes) {
159 appendOutlineHeader(to, node)
160 if (node.members.any()) {
161 appendOutlineChildren(to, node.members)
162 }
163 }
164 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400165}