blob: df11b835494047715d5b1e2d79d86dc8b7a6c480 [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 Ryzhenkov499d0822014-07-15 16:18:53 +04008 val languageService: LanguageService) : FormatService {
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +04009
10 abstract public fun appendBlockCode(to: StringBuilder, line: String)
11 abstract public fun appendBlockCode(to: StringBuilder, lines: Iterable<String>)
12 abstract public fun appendHeader(to: StringBuilder, text: String, level: Int = 1)
13 abstract public fun appendText(to: StringBuilder, text: String)
14 abstract public fun appendLine(to: StringBuilder, text: String)
15 public abstract fun appendLine(to: StringBuilder)
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +040016
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040017 public abstract fun appendTable(to: StringBuilder, body: () -> Unit)
18 public abstract fun appendTableHeader(to: StringBuilder, body: () -> Unit)
19 public abstract fun appendTableBody(to: StringBuilder, body: () -> Unit)
20 public abstract fun appendTableRow(to: StringBuilder, body: () -> Unit)
21 public abstract fun appendTableCell(to: StringBuilder, body: () -> Unit)
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +040022
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040023 public abstract fun formatText(text: String): String
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040024 public abstract fun formatSymbol(text: String): String
25 public abstract fun formatKeyword(text: String): String
26 public abstract fun formatIdentifier(text: String): String
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040027 public abstract fun formatLink(text: String, location: Location): String
Ilya Ryzhenkov71cd87e2014-10-03 22:51:44 +040028 public abstract fun formatLink(text: String, href: String): String
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +040029 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 Ryzhenkovd6fd0452014-10-03 20:20:02 +040034 open fun formatText(location: Location, nodes: Iterable<ContentNode>): String {
35 return nodes.map { formatText(location, it) }.join("")
Ilya Ryzhenkov778e2b32014-09-29 20:54:59 +040036 }
37
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040038 open fun formatText(location: Location, content: ContentNode): String {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030039 return StringBuilder {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040040 when (content) {
41 is ContentText -> append(content.text)
42 is ContentSymbol -> append(formatSymbol(content.text))
43 is ContentKeyword -> append(formatKeyword(content.text))
44 is ContentIdentifier -> append(formatIdentifier(content.text))
45 is ContentEmphasis -> append(formatBold(formatText(location, content.children)))
46 is ContentNodeLink -> {
47 val linkTo = locationService.relativeLocation(location, content.node, extension)
48 val linkText = formatText(location, content.children)
49 append(formatLink(linkText, linkTo))
50 }
Ilya Ryzhenkov71cd87e2014-10-03 22:51:44 +040051 is ContentExternalLink -> {
52 val linkText = formatText(location, content.children)
53 append(formatLink(linkText, content.href))
54 }
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040055 else -> append(formatText(location, content.children))
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030056 }
57 }.toString()
58 }
59
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040060 open public fun link(from: DocumentationNode, to: DocumentationNode): FormatLink = link(from, to, extension)
61
62 open public fun link(from: DocumentationNode, to: DocumentationNode, extension: String): FormatLink {
63 return FormatLink(to.name, locationService.relativeLocation(from, to, extension))
64 }
65
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040066 fun appendDescription(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030067 val described = nodes.filter { !it.doc.isEmpty }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040068 if (described.any()) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040069 val single = described.size == 1
70 appendHeader(to, "Description", 3)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040071 for (node in described) {
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040072 if (!single) {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040073 appendBlockCode(to, formatText(location, languageService.render(node)))
Ilya Ryzhenkovfb41c692014-07-15 18:23:15 +040074 }
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040075 appendLine(to, formatText(location,node.doc.description))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040076 appendLine(to)
Ilya Ryzhenkovf7bab782014-09-25 22:20:58 +040077 for ((label, section) in node.doc.sections) {
78 if (label.startsWith("$"))
79 continue
80 appendLine(to, formatBold(formatText(label)))
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040081 appendLine(to, formatText(location, section))
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040082 appendLine(to)
83 }
84 }
85 }
86 }
87
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040088 fun appendSummary(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040089 val breakdownBySummary = nodes.groupByTo(LinkedHashMap()) { node ->
90 node.doc.summary
91 }
92
93 for ((summary, items) in breakdownBySummary) {
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040094 items.forEach {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040095 appendBlockCode(to, formatText(location, languageService.render(it)))
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +040096 }
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +040097 appendLine(to, formatText(location, summary))
Ilya Ryzhenkov455d74a2014-09-19 22:25:27 +030098 appendLine(to)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +040099 }
100 }
101
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400102 fun appendLocation(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkovbd494a82014-08-21 19:53:36 +0400103 val breakdownByName = nodes.groupBy { node -> node.name }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400104 for ((name, items) in breakdownByName) {
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400105 appendHeader(to, formatText(name))
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400106 appendSummary(location, to, items)
107 appendDescription(location, to, items)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400108 }
109 }
110
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400111 private fun StructuredFormatService.appendSection(location : Location, caption: String, nodes: List<DocumentationNode>, node: DocumentationNode, to: StringBuilder) {
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400112 if (nodes.any()) {
113 appendHeader(to, caption, 3)
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400114
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400115 val children = nodes.sortBy { it.name }
116 val membersMap = children.groupBy { link(node, it) }
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400117
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400118 appendTable(to) {
119 appendTableBody(to) {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400120 for ((memberLocation, members) in membersMap) {
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400121 appendTableRow(to) {
122 appendTableCell(to) {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400123 appendText(to, formatLink(memberLocation))
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400124 }
125 appendTableCell(to) {
126 val breakdownBySummary = members.groupBy { it.doc.summary }
127 for ((summary, items) in breakdownBySummary) {
Ilya Ryzhenkov7c6da4b2014-10-03 19:09:31 +0400128 for (signature in items) {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400129 appendBlockCode(to, formatText(location, languageService.render(signature)))
Ilya Ryzhenkova52e1d52014-10-03 15:57:16 +0400130 }
131
132 if (!summary.isEmpty()) {
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400133 appendText(to, formatText(location, summary))
Ilya Ryzhenkovaa59acb2014-07-15 20:05:55 +0400134 }
135 }
136 }
Ilya Ryzhenkove8447fd2014-07-15 16:37:50 +0400137 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400138 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400139 }
140 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400141 }
142 }
143
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400144 override fun appendNodes(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
145 val breakdownByLocation = nodes.groupBy { node ->
146 formatBreadcrumbs(node.path.map { link(node, it) })
147 }
148
149 for ((breadcrumbs, items) in breakdownByLocation) {
150 appendLine(to, breadcrumbs)
151 appendLine(to)
152 appendLocation(location, to, items)
153 }
154
155 for (node in nodes) {
156 appendSection(location, "Members", node.members, node, to)
157 appendSection(location, "Extensions", node.extensions, node, to)
158 appendSection(location, "Inheritors", node.inheritors, node, to)
159 appendSection(location, "Links", node.links, node, to)
160
161 }
162 }
163
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +0400164 abstract public fun appendOutlineHeader(to: StringBuilder, node: DocumentationNode)
165 abstract public fun appendOutlineChildren(to: StringBuilder, nodes: Iterable<DocumentationNode>)
166
Ilya Ryzhenkovd6fd0452014-10-03 20:20:02 +0400167 public override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) {
Ilya Ryzhenkov499d0822014-07-15 16:18:53 +0400168 for (node in nodes) {
169 appendOutlineHeader(to, node)
170 if (node.members.any()) {
171 appendOutlineChildren(to, node.members)
172 }
173 }
174 }
Ilya Ryzhenkov62cb5092014-07-15 15:54:05 +0400175}