1 package org.jetbrains.dokka
2
3 interface ContentNode {
4 val textLength: Int
5 }
6
7 object ContentEmpty : ContentNode {
8 override val textLength: Int get() = 0
9 }
10
11 open class ContentBlock() : ContentNode {
12 open val children = arrayListOf<ContentNode>()
13
appendnull14 fun append(node: ContentNode) {
15 children.add(node)
16 }
17
isEmptynull18 fun isEmpty() = children.isEmpty()
19
20 override fun equals(other: Any?): Boolean =
21 other is ContentBlock && javaClass == other.javaClass && children == other.children
22
23 override fun hashCode(): Int =
24 children.hashCode()
25
26 override val textLength: Int
27 get() = children.sumBy { it.textLength }
28 }
29
30 class NodeRenderContent(
31 val node: DocumentationNode,
32 val mode: LanguageService.RenderMode
33 ): ContentNode {
34 override val textLength: Int
35 get() = 0 //TODO: Clarify?
36 }
37
38 class LazyContentBlock(private val fillChildren: (ContentBlock) -> Unit) : ContentBlock() {
39 private var computed = false
40 override val children: ArrayList<ContentNode>
41 get() {
42 if (!computed) {
43 computed = true
44 fillChildren(this)
45 }
46 return super.children
47 }
48
equalsnull49 override fun equals(other: Any?): Boolean {
50 return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other)
51 }
52
hashCodenull53 override fun hashCode(): Int {
54 return super.hashCode() + 31 * fillChildren.hashCode()
55 }
56 }
57
58 enum class IdentifierKind {
59 TypeName,
60 ParameterName,
61 AnnotationName,
62 SummarizedTypeName,
63 Other
64 }
65
66 data class ContentText(val text: String) : ContentNode {
67 override val textLength: Int
68 get() = text.length
69 }
70
71 data class ContentKeyword(val text: String) : ContentNode {
72 override val textLength: Int
73 get() = text.length
74 }
75
76 data class ContentIdentifier(val text: String,
77 val kind: IdentifierKind = IdentifierKind.Other,
78 val signature: String? = null) : ContentNode {
79 override val textLength: Int
80 get() = text.length
81 }
82
83 data class ContentSymbol(val text: String) : ContentNode {
84 override val textLength: Int
85 get() = text.length
86 }
87
88 data class ContentEntity(val text: String) : ContentNode {
89 override val textLength: Int
90 get() = text.length
91 }
92
93 object ContentNonBreakingSpace: ContentNode {
94 override val textLength: Int
95 get() = 1
96 }
97
98 object ContentSoftLineBreak: ContentNode {
99 override val textLength: Int
100 get() = 0
101 }
102
103 object ContentIndentedSoftLineBreak: ContentNode {
104 override val textLength: Int
105 get() = 0
106 }
107 class ScriptBlock(val type: String?, val src: String) : ContentBlock()
108
109 class ContentParagraph(val label: String? = null) : ContentBlock()
110 class ContentEmphasis() : ContentBlock()
111 class ContentStrong() : ContentBlock()
112 class ContentStrikethrough() : ContentBlock()
113 class ContentCode() : ContentBlock()
114
115 class ContentDescriptionList() : ContentBlock()
116 class ContentDescriptionTerm() : ContentBlock()
117 class ContentDescriptionDefinition() : ContentBlock()
118
119 class ContentTable() : ContentBlock()
120 class ContentTableBody() : ContentBlock()
121 class ContentTableRow() : ContentBlock()
122 class ContentTableHeader(val colspan: String? = null, val rowspan: String? = null) : ContentBlock()
123 class ContentTableCell(val colspan: String? = null, val rowspan: String? = null) : ContentBlock()
124
125 class ContentSpecialReference() : ContentBlock()
126
127 open class ContentBlockCode(val language: String = "") : ContentBlock()
128 class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: ContentBlockCode = ContentBlockCode(language)) : ContentBlockCode(language)
129
130 abstract class ContentNodeLink() : ContentBlock() {
131 abstract val node: DocumentationNode?
132 }
133
134 object ContentHardLineBreak : ContentNode {
135 override val textLength: Int
136 get() = 0
137 }
138
139 class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLink() {
equalsnull140 override fun equals(other: Any?): Boolean =
141 super.equals(other) && other is ContentNodeDirectLink && node.name == other.node.name
142
143 override fun hashCode(): Int =
144 children.hashCode() * 31 + node.name.hashCode()
145 }
146
147 class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() {
148 override val node: DocumentationNode? get() = lazyNode()
149
150 override fun equals(other: Any?): Boolean =
151 super.equals(other) && other is ContentNodeLazyLink && linkText == other.linkText
152
153 override fun hashCode(): Int =
154 children.hashCode() * 31 + linkText.hashCode()
155 }
156
157 class ContentExternalLink(val href : String) : ContentBlock() {
equalsnull158 override fun equals(other: Any?): Boolean =
159 super.equals(other) && other is ContentExternalLink && href == other.href
160
161 override fun hashCode(): Int =
162 children.hashCode() * 31 + href.hashCode()
163 }
164
165 data class ContentBookmark(val name: String): ContentBlock()
166 data class ContentLocalLink(val href: String) : ContentBlock()
167
168 class ContentUnorderedList() : ContentBlock()
169 class ContentOrderedList() : ContentBlock()
170 class ContentListItem() : ContentBlock()
171
172 class ContentHeading(val level: Int) : ContentBlock()
173
174 class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() {
175 override fun equals(other: Any?): Boolean =
176 super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName
177
178 override fun hashCode(): Int =
179 children.hashCode() * 31 * 31 + tag.hashCode() * 31 + (subjectName?.hashCode() ?: 0)
180 }
181
182 object ContentTags {
183 val Description = "Description"
184 val SeeAlso = "See Also"
185 val Return = "Return"
186 val Exceptions = "Exceptions"
187 val Parameters = "Parameters"
188 }
189
contentnull190 fun content(body: ContentBlock.() -> Unit): ContentBlock {
191 val block = ContentBlock()
192 block.body()
193 return block
194 }
195
ContentBlocknull196 fun ContentBlock.text(value: String) = append(ContentText(value))
197 fun ContentBlock.keyword(value: String) = append(ContentKeyword(value))
198 fun ContentBlock.symbol(value: String) = append(ContentSymbol(value))
199
200 fun ContentBlock.identifier(value: String, kind: IdentifierKind = IdentifierKind.Other, signature: String? = null) {
201 append(ContentIdentifier(value, kind, signature))
202 }
203
nbspnull204 fun ContentBlock.nbsp() = append(ContentNonBreakingSpace)
205 fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak)
206 fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak)
207 fun ContentBlock.hardLineBreak() = append(ContentHardLineBreak)
208
209 fun ContentBlock.strong(body: ContentBlock.() -> Unit) {
210 val strong = ContentStrong()
211 strong.body()
212 append(strong)
213 }
214
codenull215 fun ContentBlock.code(body: ContentBlock.() -> Unit) {
216 val code = ContentCode()
217 code.body()
218 append(code)
219 }
220
ContentBlocknull221 fun ContentBlock.link(to: DocumentationNode, body: ContentBlock.() -> Unit) {
222 val block = if (to.kind == NodeKind.ExternalLink)
223 ContentExternalLink(to.name)
224 else
225 ContentNodeDirectLink(to)
226
227 block.body()
228 append(block)
229 }
230
231 open class Content(): ContentBlock() {
232 open val sections: List<ContentSection> get() = emptyList()
233 open val summary: ContentNode get() = ContentEmpty
234 open val description: ContentNode get() = ContentEmpty
235
findSectionByTagnull236 fun findSectionByTag(tag: String): ContentSection? =
237 sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) }
238
239 companion object {
240 val Empty = Content()
241
ofnull242 fun of(vararg child: ContentNode): Content {
243 val result = MutableContent()
244 child.forEach { result.append(it) }
245 return result
246 }
247 }
248 }
249
250 open class MutableContent() : Content() {
251 private val sectionList = arrayListOf<ContentSection>()
252 override val sections: List<ContentSection>
253 get() = sectionList
254
addSectionnull255 fun addSection(tag: String?, subjectName: String?): ContentSection {
256 val section = ContentSection(tag ?: "", subjectName)
257 sectionList.add(section)
258 return section
259 }
260
261 override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty
262
<lambda>null263 override val description: ContentNode by lazy {
264 val descriptionNodes = children.drop(1)
265 if (descriptionNodes.isEmpty()) {
266 ContentEmpty
267 } else {
268 val result = ContentSection(ContentTags.Description, null)
269 result.children.addAll(descriptionNodes)
270 result
271 }
272 }
273
equalsnull274 override fun equals(other: Any?): Boolean {
275 if (other !is Content)
276 return false
277 return sections == other.sections && children == other.children
278 }
279
hashCodenull280 override fun hashCode(): Int {
281 return sections.map { it.hashCode() }.sum()
282 }
283
toStringnull284 override fun toString(): String {
285 if (sections.isEmpty())
286 return "<empty>"
287 return (listOf(summary, description) + sections).joinToString()
288 }
289 }
290
javadocSectionDisplayNamenull291 fun javadocSectionDisplayName(sectionName: String?): String? =
292 when(sectionName) {
293 "param" -> "Parameters"
294 "throws", "exception" -> ContentTags.Exceptions
295 else -> sectionName?.capitalize()
296 }
297