blob: 99c81760a4f2ab8d861a2ad932d532d79e33291e [file] [log] [blame]
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +04001package org.jetbrains.dokka
2
3import org.jetbrains.jet.lang.descriptors.*
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +03004import org.jetbrains.dokka.DocumentationNode.*
5import org.jetbrains.jet.lang.types.*
6import org.jetbrains.jet.lang.types.lang.*
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +03007import org.jetbrains.jet.lang.resolve.name.*
8import org.jetbrains.jet.lang.resolve.lazy.*
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +01009import org.jetbrains.jet.lang.descriptors.annotations.Annotated
10import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor
Dmitry Jemerov716483c2014-12-30 17:41:14 +010011import org.jetbrains.jet.lang.resolve.DescriptorUtils
Dmitry Jemerov69dd2982014-12-30 18:47:03 +010012import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant
13import com.intellij.openapi.util.text.StringUtil
14import org.jetbrains.jet.lang.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 {
130 append(it.build(), DocumentationReference.Kind.Annotation)
131 }
132 }
133
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400134 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
135 // do not include generated code
136 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
137 return
138
139 if (options.includeNonPublic
140 || descriptor !is MemberDescriptor
141 || descriptor.getVisibility() in visibleToDocumentation) {
142 append(descriptor.build(), kind)
143 }
144 }
145
146 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
147 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
148 }
149
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100150 fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
151 externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
152 if (descriptor is CallableMemberDescriptor) {
153 val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
154 if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
155 val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
156 return externalClassNodes.getOrPut(fqName, {
157 val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
158 append(newNode, DocumentationReference.Kind.Member)
159 newNode
160 })
161 }
162 }
163 return this
164 }
165
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400166 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
167 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
168 for ((name, parts) in fragments.groupBy { it.fqName }) {
169 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400170 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400171 for ((packageName, declarations) in descriptors) {
172 println(" package $packageName: ${declarations.count()} nodes")
173 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100174 val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
175 declarations.forEach { descriptor ->
176 val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
177 parent.appendChild(descriptor, DocumentationReference.Kind.Member)
178 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400179 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400180 }
181 }
182
183 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
184 is ClassDescriptor -> build()
185 is ConstructorDescriptor -> build()
186 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400187 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400188 is PropertyAccessorDescriptor -> build()
189 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400190 is TypeParameterDescriptor -> build()
191 is ValueParameterDescriptor -> build()
192 is ReceiverParameterDescriptor -> build()
193 else -> throw IllegalStateException("Descriptor $this is not known")
194 }
195
196 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
197 fun ClassDescriptor.build(): DocumentationNode {
198 val kind = when (getKind()) {
199 ClassKind.OBJECT -> Kind.Object
200 ClassKind.CLASS_OBJECT -> Kind.Object
201 ClassKind.TRAIT -> Kind.Interface
202 ClassKind.ENUM_CLASS -> Kind.Enum
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100203 ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400204 ClassKind.ENUM_ENTRY -> Kind.EnumItem
205 else -> Kind.Class
206 }
207 val node = DocumentationNode(this, kind)
208 node.appendSupertypes(this)
209 if (getKind() != ClassKind.OBJECT) {
210 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
211 node.appendChildren(getConstructors(), DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400212 }
213 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100214 val classObjectDescriptor = getClassObjectDescriptor()
215 if (classObjectDescriptor != null) {
216 node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
217 DocumentationReference.Kind.Member)
218 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100219 node.appendAnnotations(this)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400220 register(this, node)
221 return node
222 }
223
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400224 fun ConstructorDescriptor.build(): DocumentationNode {
225 val node = DocumentationNode(this, Kind.Constructor)
226 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
227 register(this, node)
228 return node
229 }
230
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100231 private fun DeclarationDescriptor.inClassObject() =
232 getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
233
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100234 fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
235 val extensionReceiver = getExtensionReceiverParameter()
236 if (extensionReceiver != null) {
237 val type = extensionReceiver.getType()
238 return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
239 }
240 return null
241 }
242
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400243 fun FunctionDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100244 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400245
246 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
247 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
248 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
249 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100250 node.appendAnnotations(this)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400251 register(this, node)
252 return node
253
254 }
255
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400256 fun PropertyAccessorDescriptor.build(): DocumentationNode {
257 val doc = parseDocumentation(this)
258 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300259 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400260
261 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
262 node.appendType(getReturnType())
263 register(this, node)
264 return node
265 }
266
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400267 fun PropertyDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100268 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectProperty else Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400269 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400270 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
271 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100272 node.appendAnnotations(this)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400273 getGetter()?.let {
274 if (!it.isDefault())
275 node.appendChild(it, DocumentationReference.Kind.Member)
276 }
277 getSetter()?.let {
278 if (!it.isDefault())
279 node.appendChild(it, DocumentationReference.Kind.Member)
280 }
281
282 register(this, node)
283 return node
284 }
285
286 fun ValueParameterDescriptor.build(): DocumentationNode {
287 val node = DocumentationNode(this, Kind.Parameter)
288 node.appendType(getType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100289 node.appendAnnotations(this)
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300290 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400291 return node
292 }
293
294 fun TypeParameterDescriptor.build(): DocumentationNode {
295 val doc = parseDocumentation(this)
296 val name = getName().asString()
297 val prefix = when (getVariance()) {
298 Variance.IN_VARIANCE -> "in "
299 Variance.OUT_VARIANCE -> "out "
300 else -> ""
301 }
302
303 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
304
305 val builtIns = KotlinBuiltIns.getInstance()
306 for (constraint in getUpperBounds()) {
307 if (constraint == builtIns.getDefaultBound())
308 continue
309 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
310 node.append(constraintNode, DocumentationReference.Kind.Detail)
311 }
312
313 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300314 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400315 continue
316 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
317 node.append(constraintNode, DocumentationReference.Kind.Detail)
318 }
319 return node
320 }
321
322 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400323 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400324 node.appendType(getType())
325 return node
326 }
327
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100328 fun AnnotationDescriptor.build(): DocumentationNode {
329 val annotationClass = getType().getConstructor().getDeclarationDescriptor()
330 val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100331 val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
332 arguments.forEach {
333 val valueNode = it.second.build()
334 if (valueNode != null) {
335 val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
336 paramNode.append(valueNode, DocumentationReference.Kind.Detail)
337 node.append(paramNode, DocumentationReference.Kind.Detail)
338 }
339 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100340 return node
341 }
342
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100343 fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
344 val value = getValue()
345 val valueString = when(value) {
346 is String ->
347 "\"" + StringUtil.escapeStringCharacters(value) + "\""
348 is EnumEntrySyntheticClassDescriptor ->
349 value.getContainingDeclaration().getName().asString() + "." + value.getName()
350 else -> value?.toString()
351 }
352 return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
353 }
354
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400355 /**
356 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
357 *
358 * $receiver: [DocumentationContext] for node/descriptor resolutions
359 * $node: [DocumentationNode] to visit
360 */
361 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400362 if (node.kind != Kind.PropertyAccessor) {
363 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
364 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
365 val descriptor = links[receiverType]
366 if (descriptor != null) {
367 val typeNode = descriptorToNode[descriptor]
368 // if typeNode is null, extension is to external type like in a library
369 // should we create dummy node here?
370 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
371 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400372 }
373 }
374 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
375 val descriptor = links[detail]
376 if (descriptor != null) {
377 val typeNode = descriptorToNode[descriptor]
378 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
379 }
380 }
381 node.details.forEach { detail ->
382 val descriptor = links[detail]
383 if (descriptor != null) {
384 val typeNode = descriptorToNode[descriptor]
385 if (typeNode != null) {
386 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
387 }
388 }
389 }
390
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400391 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400392
393 for (child in node.members) {
394 resolveReferences(child)
395 }
396 for (child in node.details) {
397 resolveReferences(child)
398 }
399 }
400
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300401 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400402 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300403 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400404 }
405
406 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
407 val snapshot = content.children.toList()
408 for (child in snapshot) {
409 if (child is ContentExternalLink) {
410 val referenceText = child.href
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300411 val symbol = resolveReference(getResolutionScope(node), referenceText)
412 if (symbol != null) {
413 val targetNode = descriptorToNode[symbol]
414 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400415
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300416 val index = content.children.indexOf(child)
417 content.children.remove(index)
418 contentLink.children.addAll(child.children)
419 content.children.add(index, contentLink)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400420 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300421
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400422 }
423 resolveContentLinks(node, child)
424 }
425 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300426
427 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
428 if (Name.isValidIdentifier(reference)) {
429 val scope = getResolutionScope(context)
430 val symbolName = Name.guess(reference)
431 return scope.getLocalVariable(symbolName) ?:
432 scope.getProperties(symbolName).firstOrNull() ?:
433 scope.getFunctions(symbolName).firstOrNull() ?:
434 scope.getClassifier(symbolName)
435
436 }
437
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200438 if ("." !in reference)
439 return null
440
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300441 val names = reference.split('.')
442 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
443 nextContext?.let { resolveReference(it, name) }
444 }
445
446 return result
447 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400448}