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