<lambda>null1 package org.jetbrains.dokka
2
3 import org.jetbrains.dokka.classNodeNameWithOuterClass
4 import org.jetbrains.dokka.LanguageService.RenderMode
5
6 /**
7 * Implements [LanguageService] and provides rendering of symbols in Kotlin language
8 */
9 class KotlinLanguageService : CommonLanguageService() {
10 override fun showModifierInSummary(node: DocumentationNode): Boolean {
11 return node.name !in fullOnlyModifiers
12 }
13
14 private val fullOnlyModifiers =
15 setOf("public", "protected", "private", "inline", "noinline", "crossinline", "reified")
16
17 override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode {
18 return content {
19 when (node.kind) {
20 NodeKind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node)
21 in NodeKind.classLike -> renderClass(node, renderMode)
22
23 NodeKind.EnumItem -> renderClass(node, renderMode)
24 NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name)
25
26 NodeKind.Parameter -> renderParameter(node, renderMode)
27 NodeKind.TypeParameter -> renderTypeParameter(node, renderMode)
28 NodeKind.Type,
29 NodeKind.UpperBound -> renderType(node, renderMode)
30
31 NodeKind.Modifier -> renderModifier(this, node, renderMode)
32 NodeKind.Constructor,
33 NodeKind.Function,
34 NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode)
35 NodeKind.Property,
36 NodeKind.CompanionObjectProperty -> renderProperty(node, renderMode)
37 else -> identifier(node.name)
38 }
39 }
40 }
41
42
43 override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? {
44 if (nodes.size < 2) return null
45 val receiverKind = nodes.getReceiverKind() ?: return null
46 val functionWithTypeParameter = nodes.firstOrNull { it.details(NodeKind.TypeParameter).any() } ?: return null
47 return content {
48 val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first()
49 if (functionWithTypeParameter.kind == NodeKind.Function) {
50 renderFunction(
51 functionWithTypeParameter,
52 RenderMode.SUMMARY,
53 SummarizingMapper(receiverKind, typeParameter.name)
54 )
55 } else {
56 renderProperty(
57 functionWithTypeParameter,
58 RenderMode.SUMMARY,
59 SummarizingMapper(receiverKind, typeParameter.name)
60 )
61 }
62 }
63 }
64
65 private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? {
66 val qNames = map { it.getReceiverQName() }.filterNotNull()
67 if (qNames.size != size)
68 return null
69
70 return ReceiverKind.values().firstOrNull { kind -> qNames.all { it in kind.classes } }
71 }
72
73 private fun DocumentationNode.getReceiverQName(): String? {
74 if (kind != NodeKind.Function && kind != NodeKind.Property) return null
75 val receiver = details(NodeKind.Receiver).singleOrNull() ?: return null
76 return receiver.detail(NodeKind.Type).qualifiedNameFromType()
77 }
78
79 companion object {
80 private val arrayClasses = setOf(
81 "kotlin.Array",
82 "kotlin.BooleanArray",
83 "kotlin.ByteArray",
84 "kotlin.CharArray",
85 "kotlin.ShortArray",
86 "kotlin.IntArray",
87 "kotlin.LongArray",
88 "kotlin.FloatArray",
89 "kotlin.DoubleArray"
90 )
91
92 private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses
93
94 private val iterableClasses = setOf(
95 "kotlin.Collection",
96 "kotlin.Sequence",
97 "kotlin.Iterable",
98 "kotlin.Map",
99 "kotlin.String",
100 "kotlin.CharSequence"
101 ) + arrayOrListClasses
102 }
103
104 private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) {
105 ARRAY("any_array", arrayClasses),
106 ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses),
107 ITERABLE("any_iterable", iterableClasses),
108 }
109
110 interface SignatureMapper {
111 fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
112 }
113
114 private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper {
115 override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
116 to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
117 to.text("<$typeParameterName>")
118 }
119 }
120
121 private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) {
122 node.references(RefKind.HiddenAnnotation).map { it.to }
123 .find { it.name == "ParameterName" }?.let {
124 val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value)
125 identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName)
126 symbol(":")
127 nbsp()
128 }
129 }
130
131 private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) {
132 var typeArguments = node.details(NodeKind.Type)
133
134 if (node.name.startsWith("Suspend")) {
135 keyword("suspend ")
136 }
137
138 // lambda
139 val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" }
140 if (isExtension) {
141 renderType(typeArguments.first(), renderMode)
142 symbol(".")
143 typeArguments = typeArguments.drop(1)
144 }
145 symbol("(")
146 renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) {
147 renderFunctionalTypeParameterName(it, renderMode)
148 renderType(it, renderMode)
149 }
150 symbol(")")
151 nbsp()
152 symbol("->")
153 nbsp()
154 renderType(typeArguments.last(), renderMode)
155
156 }
157
158 private fun DocumentationNode.isFunctionalType(): Boolean {
159 val typeArguments = details(NodeKind.Type)
160 val functionalTypeName = "Function${typeArguments.count() - 1}"
161 val suspendFunctionalTypeName = "Suspend$functionalTypeName"
162 return name == functionalTypeName || name == suspendFunctionalTypeName
163 }
164
165 private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) {
166 if (node.name == "dynamic") {
167 keyword("dynamic")
168 return
169 }
170 if (node.isFunctionalType()) {
171 renderFunctionalType(node, renderMode)
172 return
173 }
174 if (renderMode == RenderMode.FULL) {
175 renderAnnotationsForNode(node)
176 }
177 renderModifiersForNode(node, renderMode, true)
178 renderLinked(this, node) {
179 identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName)
180 }
181 val typeArguments = node.details(NodeKind.Type)
182 if (typeArguments.isNotEmpty()) {
183 symbol("<")
184 renderList(typeArguments, noWrap = true) {
185 renderType(it, renderMode)
186 }
187 symbol(">")
188 }
189 val nullabilityModifier = node.details(NodeKind.NullabilityModifier).singleOrNull()
190 if (nullabilityModifier != null) {
191 symbol(nullabilityModifier.name)
192 }
193 }
194
195 override fun renderModifier(
196 block: ContentBlock,
197 node: DocumentationNode,
198 renderMode: RenderMode,
199 nowrap: Boolean
200 ) {
201 when (node.name) {
202 "final", "public", "var" -> {
203 }
204 else -> {
205 if (node.name !in fullOnlyModifiers || renderMode == RenderMode.FULL) {
206 super.renderModifier(block, node, renderMode, nowrap)
207 }
208 }
209 }
210 }
211
212 private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) {
213 renderModifiersForNode(node, renderMode, true)
214
215 identifier(node.name)
216
217 val constraints = node.details(NodeKind.UpperBound)
218 if (constraints.size == 1) {
219 nbsp()
220 symbol(":")
221 nbsp()
222 renderList(constraints, noWrap = true) {
223 renderType(it, renderMode)
224 }
225 }
226 }
227
228 private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
229 if (renderMode == RenderMode.FULL) {
230 renderAnnotationsForNode(node)
231 }
232 renderModifiersForNode(node, renderMode)
233 identifier(node.name, IdentifierKind.ParameterName, node.detailOrNull(NodeKind.Signature)?.name)
234 symbol(":")
235 nbsp()
236 val parameterType = node.detail(NodeKind.Type)
237 renderType(parameterType, renderMode)
238 val valueNode = node.details(NodeKind.Value).firstOrNull()
239 if (valueNode != null) {
240 nbsp()
241 symbol("=")
242 nbsp()
243 text(valueNode.name)
244 }
245 }
246
247 private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) {
248 val typeParameters = node.details(NodeKind.TypeParameter)
249 if (typeParameters.any()) {
250 symbol("<")
251 renderList(typeParameters) {
252 renderTypeParameter(it, renderMode)
253 }
254 symbol(">")
255 }
256 }
257
258 private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) {
259 val parametersWithMultipleConstraints =
260 node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 }
261 val parametersWithConstraints = parametersWithMultipleConstraints
262 .flatMap { parameter ->
263 parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint }
264 }
265 if (parametersWithMultipleConstraints.isNotEmpty()) {
266 keyword(" where ")
267 renderList(parametersWithConstraints) {
268 identifier(it.first.name)
269 nbsp()
270 symbol(":")
271 nbsp()
272 renderType(it.second, renderMode)
273 }
274 }
275 }
276
277 private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
278 val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
279 if (supertypes.any()) {
280 nbsp()
281 symbol(":")
282 nbsp()
283 renderList(supertypes) {
284 indentedSoftLineBreak()
285 renderType(it, renderMode)
286 }
287 }
288 }
289
290 private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) {
291 node.annotations.forEach {
292 renderAnnotation(it)
293 }
294 }
295
296 private fun ContentBlock.renderAnnotation(node: DocumentationNode) {
297 identifier("@" + node.name, IdentifierKind.AnnotationName)
298 val parameters = node.details(NodeKind.Parameter)
299 if (!parameters.isEmpty()) {
300 symbol("(")
301 renderList(parameters) {
302 text(it.detail(NodeKind.Value).name)
303 }
304 symbol(")")
305 }
306 text(" ")
307 }
308
309 private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) {
310 if (renderMode == RenderMode.FULL) {
311 renderAnnotationsForNode(node)
312 }
313 renderModifiersForNode(node, renderMode)
314 when (node.kind) {
315 NodeKind.Class,
316 NodeKind.AnnotationClass,
317 NodeKind.Exception,
318 NodeKind.Enum -> keyword("class ")
319 NodeKind.Interface -> keyword("interface ")
320 NodeKind.EnumItem -> keyword("enum val ")
321 NodeKind.Object -> keyword("object ")
322 NodeKind.TypeAlias -> keyword("typealias ")
323 else -> throw IllegalArgumentException("Node $node is not a class-like object")
324 }
325
326 identifierOrDeprecated(node)
327 renderTypeParametersForNode(node, renderMode)
328 renderSupertypesForNode(node, renderMode)
329 renderExtraTypeParameterConstraints(node, renderMode)
330
331 if (node.kind == NodeKind.TypeAlias) {
332 nbsp()
333 symbol("=")
334 nbsp()
335 renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode)
336 }
337 }
338
339 private fun ContentBlock.renderFunction(
340 node: DocumentationNode,
341 renderMode: RenderMode,
342 signatureMapper: SignatureMapper? = null
343 ) {
344 if (renderMode == RenderMode.FULL) {
345 renderAnnotationsForNode(node)
346 }
347 renderModifiersForNode(node, renderMode)
348 when (node.kind) {
349 NodeKind.Constructor -> identifier(node.owner!!.name)
350 NodeKind.Function,
351 NodeKind.CompanionObjectFunction -> keyword("fun ")
352 else -> throw IllegalArgumentException("Node $node is not a function-like object")
353 }
354 renderTypeParametersForNode(node, renderMode)
355 if (node.details(NodeKind.TypeParameter).any()) {
356 text(" ")
357 }
358
359 renderReceiver(node, renderMode, signatureMapper)
360
361 if (node.kind != NodeKind.Constructor)
362 identifierOrDeprecated(node)
363
364 symbol("(")
365 val parameters = node.details(NodeKind.Parameter)
366 renderHardWrappingList(parameters) {
367 renderParameter(it, renderMode)
368 }
369 if (needReturnType(node)) {
370 if (parameters.size > 1) {
371 hardLineBreak()
372 }
373 symbol(")")
374 symbol(": ")
375 renderType(node.detail(NodeKind.Type), renderMode)
376 } else {
377 symbol(")")
378 }
379 renderExtraTypeParameterConstraints(node, renderMode)
380 }
381
382 private fun ContentBlock.renderReceiver(
383 node: DocumentationNode,
384 renderMode: RenderMode,
385 signatureMapper: SignatureMapper?
386 ) {
387 val receiver = node.details(NodeKind.Receiver).singleOrNull()
388 if (receiver != null) {
389 if (signatureMapper != null) {
390 signatureMapper.renderReceiver(receiver, this)
391 } else {
392 val type = receiver.detail(NodeKind.Type)
393
394 if (type.isFunctionalType()) {
395 symbol("(")
396 renderFunctionalType(type, renderMode)
397 symbol(")")
398 } else {
399 renderType(type, renderMode)
400 }
401 }
402 symbol(".")
403 }
404 }
405
406 private fun needReturnType(node: DocumentationNode) = when (node.kind) {
407 NodeKind.Constructor -> false
408 else -> !node.isUnitReturnType()
409 }
410
411 fun DocumentationNode.isUnitReturnType(): Boolean =
412 detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit"
413
414 private fun ContentBlock.renderProperty(
415 node: DocumentationNode,
416 renderMode: RenderMode,
417 signatureMapper: SignatureMapper? = null
418 ) {
419 if (renderMode == RenderMode.FULL) {
420 renderAnnotationsForNode(node)
421 }
422 renderModifiersForNode(node, renderMode)
423 when (node.kind) {
424 NodeKind.Property,
425 NodeKind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ")
426 else -> throw IllegalArgumentException("Node $node is not a property")
427 }
428 renderTypeParametersForNode(node, renderMode)
429 if (node.details(NodeKind.TypeParameter).any()) {
430 text(" ")
431 }
432
433 renderReceiver(node, renderMode, signatureMapper)
434
435 identifierOrDeprecated(node)
436 symbol(": ")
437 renderType(node.detail(NodeKind.Type), renderMode)
438 renderExtraTypeParameterConstraints(node, renderMode)
439 }
440
441 fun DocumentationNode.getPropertyKeyword() =
442 if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val"
443
444 fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) {
445 if (node.deprecation != null) {
446 val strike = ContentStrikethrough()
447 strike.identifier(node.name)
448 append(strike)
449 } else {
450 identifier(node.name)
451 }
452 }
453 }
454
qualifiedNameFromTypenull455 fun DocumentationNode.qualifiedNameFromType(): String {
456 return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name
457 ?: (links.firstOrNull { it.kind != NodeKind.ExternalLink } ?: hiddenLinks.firstOrNull())?.qualifiedName()
458 ?: name
459 }
460
461
462 val DocumentationNode.typeDeclarationClass
<lambda>null463 get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType)
464