blob: be6d5d7c1ecdbf8d46653c6792271731999883d7 [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
Dmitry Jemerov6146fa82015-01-14 18:46:36 +010015import java.io.File
16import com.intellij.psi.PsiDocumentManager
17import com.intellij.psi.PsiNameIdentifierOwner
18import com.intellij.psi.PsiElement
Dmitry Jemerov807451f2015-01-14 20:30:54 +010019import org.jetbrains.kotlin.resolve.source.getPsi
Dmitry Jemerovf36d9b02015-01-14 19:38:58 +010020import org.jetbrains.kotlin.psi.JetParameter
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040021
Dmitry Jemerov6146fa82015-01-14 18:46:36 +010022public data class DocumentationOptions(val includeNonPublic: Boolean = false,
23 val sourceLinks: List<SourceLinkDefinition>)
Ilya Ryzhenkov471039e2014-10-12 22:37:07 +040024
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +010025private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
26 val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
27 val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
28 return package1 != null && package2 != null && package1.fqName == package2.fqName
29}
30
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040031class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +040032 val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040033 val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
34 val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
35 val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
36 val packages = hashMapOf<FqName, DocumentationNode>()
37
38 fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040039 val docText = descriptor.getDocumentationElements().map { it.extractText() }.join("\n")
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +030040 val tree = parseMarkdown(docText)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040041 //println(tree.toTestString())
Ilya Ryzhenkovad14ea92014-10-13 18:22:02 +040042 val content = buildContent(tree, descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040043 return content
44 }
45
46 fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
47 links.put(node, descriptor)
48 }
49
50 fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
51 descriptorToNode.put(descriptor, node)
52 nodeToDescriptor.put(node, descriptor)
53 }
54
55 fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
56 val doc = parseDocumentation(descriptor)
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030057 val node = DocumentationNode(descriptor.getName().asString(), doc, kind).withModifiers(descriptor)
58 return node
59 }
60
61 private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor) : DocumentationNode{
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040062 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030063 appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040064 if (descriptor !is ConstructorDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030065 appendModality(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040066 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040067 }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030068 return this
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040069 }
70
71 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
72 addReferenceTo(child, kind)
73 when (kind) {
74 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
75 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
76 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
77 }
78 }
79
80 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
Dmitry Jemerov6d234302015-01-13 17:15:52 +010081 var modality = descriptor.getModality()
82 if (modality == Modality.OPEN) {
83 val containingClass = descriptor.getContainingDeclaration() as? ClassDescriptor
84 if (containingClass?.getModality() == Modality.FINAL) {
85 modality = Modality.FINAL
86 }
87 }
88 val modifier = modality.name().toLowerCase()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040089 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
90 append(node, DocumentationReference.Kind.Detail)
91 }
92
93 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
94 val modifier = descriptor.getVisibility().toString()
95 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
96 append(node, DocumentationReference.Kind.Detail)
97 }
98
99 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
100 val superTypes = descriptor.getTypeConstructor().getSupertypes()
101 for (superType in superTypes) {
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100102 if (!ignoreSupertype(superType))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400103 appendType(superType, DocumentationNode.Kind.Supertype)
104 }
105 }
106
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100107 private fun ignoreSupertype(superType: JetType): Boolean {
108 val superClass = superType.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
109 if (superClass != null) {
110 val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
111 return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
112 }
113 return false
114 }
115
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400116 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
117 val prefix = when (projection.getProjectionKind()) {
118 Variance.IN_VARIANCE -> "in "
119 Variance.OUT_VARIANCE -> "out "
120 else -> ""
121 }
122 appendType(projection.getType(), kind, prefix)
123 }
124
125 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
126 if (jetType == null)
127 return
128 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
129 val name = when (classifierDescriptor) {
Ilya Ryzhenkov5d0af0b2014-12-04 15:52:12 +0300130 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isMarkedNullable()) "?" else ""
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400131 else -> "<anonymous>"
132 }
133 val node = DocumentationNode(name, Content.Empty, kind)
134 if (classifierDescriptor != null)
135 link(node, classifierDescriptor)
136
137 append(node, DocumentationReference.Kind.Detail)
138 for (typeArgument in jetType.getArguments())
139 node.appendProjection(typeArgument)
140 }
141
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100142 fun DocumentationNode.appendAnnotations(annotated: Annotated) {
143 annotated.getAnnotations().forEach {
Dmitry Jemerove17eaa52015-01-09 20:59:58 +0100144 val annotationNode = it.build()
145 if (annotationNode != null) {
146 append(annotationNode,
147 if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
148 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100149 }
150 }
151
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100152 fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
153 val psi = getTargetElement(sourceElement)
154 val path = psi?.getContainingFile()?.getVirtualFile()?.getPath()
155 if (path == null) {
156 return
157 }
158 val absPath = File(path).getAbsolutePath()
159 val linkDef = findSourceLinkDefinition(absPath)
160 if (linkDef != null) {
161 var url = linkDef.url + path.substring(linkDef.path.length())
162 if (linkDef.lineSuffix != null) {
163 val doc = PsiDocumentManager.getInstance(psi!!.getProject()).getDocument(psi.getContainingFile())
164 if (doc != null) {
165 // IJ uses 0-based line-numbers; external source browsers use 1-based
166 val line = doc.getLineNumber(psi.getTextRange().getStartOffset()) + 1
167 url += linkDef.lineSuffix + line.toString()
168 }
169 }
170 append(DocumentationNode(url, Content.Empty, DocumentationNode.Kind.SourceUrl),
171 DocumentationReference.Kind.Detail);
172 }
173 }
174
175 private fun getTargetElement(sourceElement: SourceElement): PsiElement? {
176 val psi = sourceElement.getPsi()
177 return if (psi is PsiNameIdentifierOwner) psi.getNameIdentifier() else psi
178 }
179
180 fun findSourceLinkDefinition(path: String) = options.sourceLinks.firstOrNull { path.startsWith(it.path) }
181
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400182 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
183 // do not include generated code
184 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
185 return
186
187 if (options.includeNonPublic
188 || descriptor !is MemberDescriptor
189 || descriptor.getVisibility() in visibleToDocumentation) {
190 append(descriptor.build(), kind)
191 }
192 }
193
194 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
195 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
196 }
197
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100198 fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
199 externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
200 if (descriptor is CallableMemberDescriptor) {
201 val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
202 if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
203 val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
204 return externalClassNodes.getOrPut(fqName, {
205 val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
206 append(newNode, DocumentationReference.Kind.Member)
207 newNode
208 })
209 }
210 }
211 return this
212 }
213
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400214 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
215 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
216 for ((name, parts) in fragments.groupBy { it.fqName }) {
217 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400218 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400219 for ((packageName, declarations) in descriptors) {
Dmitry Jemerovc5fc45c2015-01-12 13:25:58 +0100220 println(" package $packageName: ${declarations.count()} declarations")
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400221 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100222 val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
223 declarations.forEach { descriptor ->
224 val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
225 parent.appendChild(descriptor, DocumentationReference.Kind.Member)
226 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400227 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400228 }
229 }
230
231 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
232 is ClassDescriptor -> build()
233 is ConstructorDescriptor -> build()
234 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400235 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400236 is PropertyAccessorDescriptor -> build()
237 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400238 is TypeParameterDescriptor -> build()
239 is ValueParameterDescriptor -> build()
240 is ReceiverParameterDescriptor -> build()
241 else -> throw IllegalStateException("Descriptor $this is not known")
242 }
243
244 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
245 fun ClassDescriptor.build(): DocumentationNode {
246 val kind = when (getKind()) {
247 ClassKind.OBJECT -> Kind.Object
248 ClassKind.CLASS_OBJECT -> Kind.Object
249 ClassKind.TRAIT -> Kind.Interface
250 ClassKind.ENUM_CLASS -> Kind.Enum
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100251 ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400252 ClassKind.ENUM_ENTRY -> Kind.EnumItem
253 else -> Kind.Class
254 }
255 val node = DocumentationNode(this, kind)
256 node.appendSupertypes(this)
Dmitry Jemerovd33f5b82015-01-12 17:49:34 +0100257 if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400258 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
Dmitry Jemerov1ce53732015-01-13 16:19:42 +0100259 val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS)
260 getConstructors().filter { it.getValueParameters().size() > 0 }
261 else
262 getConstructors()
263 node.appendChildren(constructorsToDocument, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400264 }
265 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100266 val classObjectDescriptor = getClassObjectDescriptor()
267 if (classObjectDescriptor != null) {
268 node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
269 DocumentationReference.Kind.Member)
270 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100271 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100272 node.appendSourceLink(getSource())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400273 register(this, node)
274 return node
275 }
276
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400277 fun ConstructorDescriptor.build(): DocumentationNode {
278 val node = DocumentationNode(this, Kind.Constructor)
279 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
280 register(this, node)
281 return node
282 }
283
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100284 private fun DeclarationDescriptor.inClassObject() =
285 getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
286
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100287 fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
288 val extensionReceiver = getExtensionReceiverParameter()
289 if (extensionReceiver != null) {
290 val type = extensionReceiver.getType()
291 return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
292 }
293 return null
294 }
295
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400296 fun FunctionDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100297 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400298
299 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
300 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
301 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
302 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100303 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100304 node.appendSourceLink(getSource())
305
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400306 register(this, node)
307 return node
308
309 }
310
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400311 fun PropertyAccessorDescriptor.build(): DocumentationNode {
312 val doc = parseDocumentation(this)
313 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300314 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400315
316 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
317 node.appendType(getReturnType())
318 register(this, node)
319 return node
320 }
321
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400322 fun PropertyDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100323 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectProperty else Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400324 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400325 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
326 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100327 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100328 node.appendSourceLink(getSource())
Dmitry Jemerovfaad9012015-01-14 14:00:55 +0100329 if (isVar()) {
330 node.append(DocumentationNode("var", Content.Empty, DocumentationNode.Kind.Modifier),
331 DocumentationReference.Kind.Detail)
332 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400333 getGetter()?.let {
334 if (!it.isDefault())
335 node.appendChild(it, DocumentationReference.Kind.Member)
336 }
337 getSetter()?.let {
338 if (!it.isDefault())
339 node.appendChild(it, DocumentationReference.Kind.Member)
340 }
341
342 register(this, node)
343 return node
344 }
345
346 fun ValueParameterDescriptor.build(): DocumentationNode {
347 val node = DocumentationNode(this, Kind.Parameter)
Dmitry Jemerov2e277ba2015-01-13 16:38:41 +0100348 val varargType = getVarargElementType()
349 if (varargType != null) {
350 node.append(DocumentationNode("vararg", Content.Empty, Kind.Annotation), DocumentationReference.Kind.Annotation)
351 node.appendType(varargType)
352 } else {
353 node.appendType(getType())
354 }
Dmitry Jemerovf36d9b02015-01-14 19:38:58 +0100355 if (hasDefaultValue()) {
356 val psi = getSource().getPsi() as? JetParameter
357 if (psi != null) {
358 val defaultValueText = psi.getDefaultValue()?.getText()
359 if (defaultValueText != null) {
360 node.append(DocumentationNode(defaultValueText, Content.Empty, Kind.Value), DocumentationReference.Kind.Detail)
361 }
362 }
363 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100364 node.appendAnnotations(this)
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300365 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400366 return node
367 }
368
369 fun TypeParameterDescriptor.build(): DocumentationNode {
370 val doc = parseDocumentation(this)
371 val name = getName().asString()
372 val prefix = when (getVariance()) {
373 Variance.IN_VARIANCE -> "in "
374 Variance.OUT_VARIANCE -> "out "
375 else -> ""
376 }
377
378 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
379
380 val builtIns = KotlinBuiltIns.getInstance()
381 for (constraint in getUpperBounds()) {
382 if (constraint == builtIns.getDefaultBound())
383 continue
384 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
385 node.append(constraintNode, DocumentationReference.Kind.Detail)
386 }
387
388 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300389 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400390 continue
391 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
392 node.append(constraintNode, DocumentationReference.Kind.Detail)
393 }
394 return node
395 }
396
397 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400398 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400399 node.appendType(getType())
400 return node
401 }
402
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100403 fun AnnotationDescriptor.build(): DocumentationNode? {
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100404 val annotationClass = getType().getConstructor().getDeclarationDescriptor()
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100405 if (ErrorUtils.isError(annotationClass)) {
406 return null
407 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100408 val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100409 val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
410 arguments.forEach {
411 val valueNode = it.second.build()
412 if (valueNode != null) {
413 val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
414 paramNode.append(valueNode, DocumentationReference.Kind.Detail)
415 node.append(paramNode, DocumentationReference.Kind.Detail)
416 }
417 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100418 return node
419 }
420
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100421 fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
422 val value = getValue()
423 val valueString = when(value) {
424 is String ->
425 "\"" + StringUtil.escapeStringCharacters(value) + "\""
426 is EnumEntrySyntheticClassDescriptor ->
427 value.getContainingDeclaration().getName().asString() + "." + value.getName()
428 else -> value?.toString()
429 }
430 return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
431 }
432
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400433 /**
434 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
435 *
436 * $receiver: [DocumentationContext] for node/descriptor resolutions
437 * $node: [DocumentationNode] to visit
438 */
439 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400440 if (node.kind != Kind.PropertyAccessor) {
441 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
442 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
443 val descriptor = links[receiverType]
444 if (descriptor != null) {
445 val typeNode = descriptorToNode[descriptor]
446 // if typeNode is null, extension is to external type like in a library
447 // should we create dummy node here?
448 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
449 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400450 }
451 }
452 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
453 val descriptor = links[detail]
454 if (descriptor != null) {
455 val typeNode = descriptorToNode[descriptor]
456 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
457 }
458 }
459 node.details.forEach { detail ->
460 val descriptor = links[detail]
461 if (descriptor != null) {
462 val typeNode = descriptorToNode[descriptor]
463 if (typeNode != null) {
464 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
465 }
466 }
467 }
468
Dmitry Jemerov0dd5ea32015-01-14 13:30:43 +0100469 val descriptor = nodeToDescriptor[node]
470 if (descriptor is FunctionDescriptor) {
471 val overrides = descriptor.getOverriddenDescriptors();
472 overrides?.forEach {
473 addOverrideLink(node, it)
474 }
475 }
476
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400477 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400478
479 for (child in node.members) {
480 resolveReferences(child)
481 }
482 for (child in node.details) {
483 resolveReferences(child)
484 }
485 }
486
Dmitry Jemerov0dd5ea32015-01-14 13:30:43 +0100487 /**
488 * Add an override link from a function node to the node corresponding to the specified descriptor.
489 * Note that this descriptor may be contained in a class where the function is not actually overridden
490 * (just inherited from the parent), so we need to go further up the override chain to find a function
491 * which exists in the code and for which we do have a documentation node.
492 */
493 private fun addOverrideLink(node: DocumentationNode, overriddenDescriptor: FunctionDescriptor) {
494 val overriddenNode = descriptorToNode[overriddenDescriptor.getOriginal()]
495 if (overriddenNode != null) {
496 node.addReferenceTo(overriddenNode, DocumentationReference.Kind.Override)
497 } else {
498 overriddenDescriptor.getOverriddenDescriptors().forEach { addOverrideLink(node, it) }
499 }
500 }
501
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300502 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400503 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300504 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400505 }
506
507 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100508 val resolvedContentChildren = content.children.map { resolveContentLink(node, it) }
509 content.children.clear()
510 content.children.addAll(resolvedContentChildren)
511 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400512
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100513 private fun resolveContentLink(node: DocumentationNode, content: ContentNode): ContentNode {
514 if (content is ContentExternalLink) {
515 val referenceText = content.href
516 val symbol = resolveReference(getResolutionScope(node), referenceText)
517 // don't include unresolved links in generated doc
518 // assume that if an href doesn't contain '/', it's not an attempt to reference an external file
519 if (symbol != null || "/" !in referenceText) {
520 val targetNode = descriptorToNode[symbol]
521 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
522 contentLink.children.addAll(content.children.map { resolveContentLink(node, it) })
523 return contentLink
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400524 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400525 }
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100526 resolveContentLinks(node, content)
527 return content
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400528 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300529
530 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
531 if (Name.isValidIdentifier(reference)) {
532 val scope = getResolutionScope(context)
533 val symbolName = Name.guess(reference)
534 return scope.getLocalVariable(symbolName) ?:
535 scope.getProperties(symbolName).firstOrNull() ?:
536 scope.getFunctions(symbolName).firstOrNull() ?:
537 scope.getClassifier(symbolName)
538
539 }
540
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200541 if ("." !in reference)
542 return null
543
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300544 val names = reference.split('.')
545 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
546 nextContext?.let { resolveReference(it, name) }
547 }
548
549 return result
550 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400551}