blob: b155ebca78dbae31101385a7726c74b09f4ed0eb [file] [log] [blame]
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +04001package org.jetbrains.dokka
2
Alexander Udalov5dbadfe2015-01-12 12:36:54 +03003import org.jetbrains.kotlin.descriptors.*
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +03004import org.jetbrains.dokka.DocumentationNode.*
Alexander Udalov5dbadfe2015-01-12 12:36:54 +03005import org.jetbrains.kotlin.types.*
6import org.jetbrains.kotlin.builtins.*
7import org.jetbrains.kotlin.name.*
8import org.jetbrains.kotlin.resolve.lazy.*
Dmitry Jemerov71580192015-01-12 17:27:41 +01009import org.jetbrains.kotlin.resolve.DescriptorUtils
10import org.jetbrains.kotlin.descriptors.annotations.Annotated
11import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
12import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
Dmitry Jemerov69dd2982014-12-30 18:47:03 +010013import com.intellij.openapi.util.text.StringUtil
Dmitry Jemerov71580192015-01-12 17:27:41 +010014import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040015
Ilya Ryzhenkov471039e2014-10-12 22:37:07 +040016public data class DocumentationOptions(val includeNonPublic: Boolean = false)
17
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +010018private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
19 val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
20 val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
21 return package1 != null && package2 != null && package1.fqName == package2.fqName
22}
23
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040024class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +040025 val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040026 val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
27 val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
28 val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
29 val packages = hashMapOf<FqName, DocumentationNode>()
30
31 fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040032 val docText = descriptor.getDocumentationElements().map { it.extractText() }.join("\n")
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +030033 val tree = parseMarkdown(docText)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040034 //println(tree.toTestString())
Ilya Ryzhenkovad14ea92014-10-13 18:22:02 +040035 val content = buildContent(tree, descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040036 return content
37 }
38
39 fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
40 links.put(node, descriptor)
41 }
42
43 fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
44 descriptorToNode.put(descriptor, node)
45 nodeToDescriptor.put(node, descriptor)
46 }
47
48 fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
49 val doc = parseDocumentation(descriptor)
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030050 val node = DocumentationNode(descriptor.getName().asString(), doc, kind).withModifiers(descriptor)
51 return node
52 }
53
54 private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor) : DocumentationNode{
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040055 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030056 appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040057 if (descriptor !is ConstructorDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030058 appendModality(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040059 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040060 }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030061 return this
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040062 }
63
64 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
65 addReferenceTo(child, kind)
66 when (kind) {
67 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
68 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
69 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
70 }
71 }
72
73 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
74 val modifier = descriptor.getModality().name().toLowerCase()
75 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
76 append(node, DocumentationReference.Kind.Detail)
77 }
78
79 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
80 val modifier = descriptor.getVisibility().toString()
81 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
82 append(node, DocumentationReference.Kind.Detail)
83 }
84
85 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
86 val superTypes = descriptor.getTypeConstructor().getSupertypes()
87 for (superType in superTypes) {
Dmitry Jemerov716483c2014-12-30 17:41:14 +010088 if (!ignoreSupertype(superType))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040089 appendType(superType, DocumentationNode.Kind.Supertype)
90 }
91 }
92
Dmitry Jemerov716483c2014-12-30 17:41:14 +010093 private fun ignoreSupertype(superType: JetType): Boolean {
94 val superClass = superType.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
95 if (superClass != null) {
96 val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
97 return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
98 }
99 return false
100 }
101
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400102 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
103 val prefix = when (projection.getProjectionKind()) {
104 Variance.IN_VARIANCE -> "in "
105 Variance.OUT_VARIANCE -> "out "
106 else -> ""
107 }
108 appendType(projection.getType(), kind, prefix)
109 }
110
111 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
112 if (jetType == null)
113 return
114 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
115 val name = when (classifierDescriptor) {
Ilya Ryzhenkov5d0af0b2014-12-04 15:52:12 +0300116 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isMarkedNullable()) "?" else ""
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400117 else -> "<anonymous>"
118 }
119 val node = DocumentationNode(name, Content.Empty, kind)
120 if (classifierDescriptor != null)
121 link(node, classifierDescriptor)
122
123 append(node, DocumentationReference.Kind.Detail)
124 for (typeArgument in jetType.getArguments())
125 node.appendProjection(typeArgument)
126 }
127
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100128 fun DocumentationNode.appendAnnotations(annotated: Annotated) {
129 annotated.getAnnotations().forEach {
Dmitry Jemerove17eaa52015-01-09 20:59:58 +0100130 val annotationNode = it.build()
131 if (annotationNode != null) {
132 append(annotationNode,
133 if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
134 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100135 }
136 }
137
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400138 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
139 // do not include generated code
140 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
141 return
142
143 if (options.includeNonPublic
144 || descriptor !is MemberDescriptor
145 || descriptor.getVisibility() in visibleToDocumentation) {
146 append(descriptor.build(), kind)
147 }
148 }
149
150 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
151 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
152 }
153
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100154 fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
155 externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
156 if (descriptor is CallableMemberDescriptor) {
157 val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
158 if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
159 val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
160 return externalClassNodes.getOrPut(fqName, {
161 val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
162 append(newNode, DocumentationReference.Kind.Member)
163 newNode
164 })
165 }
166 }
167 return this
168 }
169
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400170 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
171 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
172 for ((name, parts) in fragments.groupBy { it.fqName }) {
173 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400174 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400175 for ((packageName, declarations) in descriptors) {
Dmitry Jemerovc5fc45c2015-01-12 13:25:58 +0100176 println(" package $packageName: ${declarations.count()} declarations")
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400177 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100178 val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
179 declarations.forEach { descriptor ->
180 val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
181 parent.appendChild(descriptor, DocumentationReference.Kind.Member)
182 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400183 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400184 }
185 }
186
187 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
188 is ClassDescriptor -> build()
189 is ConstructorDescriptor -> build()
190 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400191 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400192 is PropertyAccessorDescriptor -> build()
193 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400194 is TypeParameterDescriptor -> build()
195 is ValueParameterDescriptor -> build()
196 is ReceiverParameterDescriptor -> build()
197 else -> throw IllegalStateException("Descriptor $this is not known")
198 }
199
200 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
201 fun ClassDescriptor.build(): DocumentationNode {
202 val kind = when (getKind()) {
203 ClassKind.OBJECT -> Kind.Object
204 ClassKind.CLASS_OBJECT -> Kind.Object
205 ClassKind.TRAIT -> Kind.Interface
206 ClassKind.ENUM_CLASS -> Kind.Enum
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100207 ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400208 ClassKind.ENUM_ENTRY -> Kind.EnumItem
209 else -> Kind.Class
210 }
211 val node = DocumentationNode(this, kind)
212 node.appendSupertypes(this)
Dmitry Jemerovd33f5b82015-01-12 17:49:34 +0100213 if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400214 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
215 node.appendChildren(getConstructors(), DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400216 }
217 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100218 val classObjectDescriptor = getClassObjectDescriptor()
219 if (classObjectDescriptor != null) {
220 node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
221 DocumentationReference.Kind.Member)
222 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100223 node.appendAnnotations(this)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400224 register(this, node)
225 return node
226 }
227
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400228 fun ConstructorDescriptor.build(): DocumentationNode {
229 val node = DocumentationNode(this, Kind.Constructor)
230 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
231 register(this, node)
232 return node
233 }
234
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100235 private fun DeclarationDescriptor.inClassObject() =
236 getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
237
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100238 fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
239 val extensionReceiver = getExtensionReceiverParameter()
240 if (extensionReceiver != null) {
241 val type = extensionReceiver.getType()
242 return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
243 }
244 return null
245 }
246
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400247 fun FunctionDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100248 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400249
250 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
251 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
252 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
253 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100254 node.appendAnnotations(this)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400255 register(this, node)
256 return node
257
258 }
259
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400260 fun PropertyAccessorDescriptor.build(): DocumentationNode {
261 val doc = parseDocumentation(this)
262 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300263 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400264
265 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
266 node.appendType(getReturnType())
267 register(this, node)
268 return node
269 }
270
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400271 fun PropertyDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100272 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectProperty else Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400273 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400274 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
275 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100276 node.appendAnnotations(this)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400277 getGetter()?.let {
278 if (!it.isDefault())
279 node.appendChild(it, DocumentationReference.Kind.Member)
280 }
281 getSetter()?.let {
282 if (!it.isDefault())
283 node.appendChild(it, DocumentationReference.Kind.Member)
284 }
285
286 register(this, node)
287 return node
288 }
289
290 fun ValueParameterDescriptor.build(): DocumentationNode {
291 val node = DocumentationNode(this, Kind.Parameter)
292 node.appendType(getType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100293 node.appendAnnotations(this)
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300294 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400295 return node
296 }
297
298 fun TypeParameterDescriptor.build(): DocumentationNode {
299 val doc = parseDocumentation(this)
300 val name = getName().asString()
301 val prefix = when (getVariance()) {
302 Variance.IN_VARIANCE -> "in "
303 Variance.OUT_VARIANCE -> "out "
304 else -> ""
305 }
306
307 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
308
309 val builtIns = KotlinBuiltIns.getInstance()
310 for (constraint in getUpperBounds()) {
311 if (constraint == builtIns.getDefaultBound())
312 continue
313 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
314 node.append(constraintNode, DocumentationReference.Kind.Detail)
315 }
316
317 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300318 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400319 continue
320 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
321 node.append(constraintNode, DocumentationReference.Kind.Detail)
322 }
323 return node
324 }
325
326 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400327 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400328 node.appendType(getType())
329 return node
330 }
331
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100332 fun AnnotationDescriptor.build(): DocumentationNode? {
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100333 val annotationClass = getType().getConstructor().getDeclarationDescriptor()
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100334 if (ErrorUtils.isError(annotationClass)) {
335 return null
336 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100337 val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100338 val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
339 arguments.forEach {
340 val valueNode = it.second.build()
341 if (valueNode != null) {
342 val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
343 paramNode.append(valueNode, DocumentationReference.Kind.Detail)
344 node.append(paramNode, DocumentationReference.Kind.Detail)
345 }
346 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100347 return node
348 }
349
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100350 fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
351 val value = getValue()
352 val valueString = when(value) {
353 is String ->
354 "\"" + StringUtil.escapeStringCharacters(value) + "\""
355 is EnumEntrySyntheticClassDescriptor ->
356 value.getContainingDeclaration().getName().asString() + "." + value.getName()
357 else -> value?.toString()
358 }
359 return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
360 }
361
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400362 /**
363 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
364 *
365 * $receiver: [DocumentationContext] for node/descriptor resolutions
366 * $node: [DocumentationNode] to visit
367 */
368 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400369 if (node.kind != Kind.PropertyAccessor) {
370 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
371 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
372 val descriptor = links[receiverType]
373 if (descriptor != null) {
374 val typeNode = descriptorToNode[descriptor]
375 // if typeNode is null, extension is to external type like in a library
376 // should we create dummy node here?
377 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
378 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400379 }
380 }
381 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
382 val descriptor = links[detail]
383 if (descriptor != null) {
384 val typeNode = descriptorToNode[descriptor]
385 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
386 }
387 }
388 node.details.forEach { detail ->
389 val descriptor = links[detail]
390 if (descriptor != null) {
391 val typeNode = descriptorToNode[descriptor]
392 if (typeNode != null) {
393 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
394 }
395 }
396 }
397
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400398 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400399
400 for (child in node.members) {
401 resolveReferences(child)
402 }
403 for (child in node.details) {
404 resolveReferences(child)
405 }
406 }
407
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300408 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400409 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300410 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400411 }
412
413 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
414 val snapshot = content.children.toList()
415 for (child in snapshot) {
416 if (child is ContentExternalLink) {
417 val referenceText = child.href
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300418 val symbol = resolveReference(getResolutionScope(node), referenceText)
419 if (symbol != null) {
420 val targetNode = descriptorToNode[symbol]
421 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400422
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300423 val index = content.children.indexOf(child)
424 content.children.remove(index)
425 contentLink.children.addAll(child.children)
426 content.children.add(index, contentLink)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400427 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300428
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400429 }
430 resolveContentLinks(node, child)
431 }
432 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300433
434 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
435 if (Name.isValidIdentifier(reference)) {
436 val scope = getResolutionScope(context)
437 val symbolName = Name.guess(reference)
438 return scope.getLocalVariable(symbolName) ?:
439 scope.getProperties(symbolName).firstOrNull() ?:
440 scope.getFunctions(symbolName).firstOrNull() ?:
441 scope.getClassifier(symbolName)
442
443 }
444
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200445 if ("." !in reference)
446 return null
447
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300448 val names = reference.split('.')
449 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
450 nextContext?.let { resolveReference(it, name) }
451 }
452
453 return result
454 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400455}