blob: 053615bcb4d239b35a40d3577a6355f54db6c6f1 [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
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010021import org.jetbrains.kotlin.kdoc.findKDoc
22import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
23import com.intellij.psi.util.PsiTreeUtil
24import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040025
Dmitry Jemerov6146fa82015-01-14 18:46:36 +010026public data class DocumentationOptions(val includeNonPublic: Boolean = false,
27 val sourceLinks: List<SourceLinkDefinition>)
Ilya Ryzhenkov471039e2014-10-12 22:37:07 +040028
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +010029private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
30 val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
31 val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
32 return package1 != null && package2 != null && package1.fqName == package2.fqName
33}
34
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040035class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +040036 val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040037 val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
38 val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
39 val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
40 val packages = hashMapOf<FqName, DocumentationNode>()
41
42 fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010043 val kdoc = findKDoc(descriptor)
44 val docText = kdoc?.getContent() ?: ""
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +030045 val tree = parseMarkdown(docText)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040046 //println(tree.toTestString())
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010047 val content = buildContent(tree)
48 if (kdoc is KDocSection) {
49 val tags = PsiTreeUtil.getChildrenOfType(kdoc, javaClass<KDocTag>())
50 tags?.forEach {
51 if (it.getName() == "code") {
52 content.append(functionBody(descriptor, it.getContent()))
53 } else {
54 val section = content.addSection(displayName(it.getName()), it.getSubjectName())
55 val sectionContent = it.getContent()
56 val markdownNode = parseMarkdown(sectionContent)
57 buildInlineContentTo(markdownNode, section)
58 }
59 }
60 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040061 return content
62 }
63
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010064 fun displayName(sectionName: String?): String? =
65 when(sectionName) {
66 "param" -> "Parameters"
67 "throws", "exception" -> "Exceptions"
68 else -> sectionName
69 }
70
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040071 fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
72 links.put(node, descriptor)
73 }
74
75 fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
76 descriptorToNode.put(descriptor, node)
77 nodeToDescriptor.put(node, descriptor)
78 }
79
80 fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
81 val doc = parseDocumentation(descriptor)
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030082 val node = DocumentationNode(descriptor.getName().asString(), doc, kind).withModifiers(descriptor)
83 return node
84 }
85
86 private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor) : DocumentationNode{
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040087 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030088 appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040089 if (descriptor !is ConstructorDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030090 appendModality(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040091 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040092 }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030093 return this
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040094 }
95
96 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
97 addReferenceTo(child, kind)
98 when (kind) {
99 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
100 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
101 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
102 }
103 }
104
105 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
Dmitry Jemerov6d234302015-01-13 17:15:52 +0100106 var modality = descriptor.getModality()
107 if (modality == Modality.OPEN) {
108 val containingClass = descriptor.getContainingDeclaration() as? ClassDescriptor
109 if (containingClass?.getModality() == Modality.FINAL) {
110 modality = Modality.FINAL
111 }
112 }
113 val modifier = modality.name().toLowerCase()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400114 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
115 append(node, DocumentationReference.Kind.Detail)
116 }
117
118 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
119 val modifier = descriptor.getVisibility().toString()
120 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
121 append(node, DocumentationReference.Kind.Detail)
122 }
123
124 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
125 val superTypes = descriptor.getTypeConstructor().getSupertypes()
126 for (superType in superTypes) {
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100127 if (!ignoreSupertype(superType))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400128 appendType(superType, DocumentationNode.Kind.Supertype)
129 }
130 }
131
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100132 private fun ignoreSupertype(superType: JetType): Boolean {
133 val superClass = superType.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
134 if (superClass != null) {
135 val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
136 return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
137 }
138 return false
139 }
140
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400141 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
142 val prefix = when (projection.getProjectionKind()) {
143 Variance.IN_VARIANCE -> "in "
144 Variance.OUT_VARIANCE -> "out "
145 else -> ""
146 }
147 appendType(projection.getType(), kind, prefix)
148 }
149
150 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
151 if (jetType == null)
152 return
153 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
154 val name = when (classifierDescriptor) {
Ilya Ryzhenkov5d0af0b2014-12-04 15:52:12 +0300155 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isMarkedNullable()) "?" else ""
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400156 else -> "<anonymous>"
157 }
158 val node = DocumentationNode(name, Content.Empty, kind)
159 if (classifierDescriptor != null)
160 link(node, classifierDescriptor)
161
162 append(node, DocumentationReference.Kind.Detail)
163 for (typeArgument in jetType.getArguments())
164 node.appendProjection(typeArgument)
165 }
166
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100167 fun DocumentationNode.appendAnnotations(annotated: Annotated) {
168 annotated.getAnnotations().forEach {
Dmitry Jemerove17eaa52015-01-09 20:59:58 +0100169 val annotationNode = it.build()
170 if (annotationNode != null) {
171 append(annotationNode,
172 if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
173 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100174 }
175 }
176
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100177 fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
178 val psi = getTargetElement(sourceElement)
179 val path = psi?.getContainingFile()?.getVirtualFile()?.getPath()
180 if (path == null) {
181 return
182 }
183 val absPath = File(path).getAbsolutePath()
184 val linkDef = findSourceLinkDefinition(absPath)
185 if (linkDef != null) {
186 var url = linkDef.url + path.substring(linkDef.path.length())
187 if (linkDef.lineSuffix != null) {
188 val doc = PsiDocumentManager.getInstance(psi!!.getProject()).getDocument(psi.getContainingFile())
189 if (doc != null) {
190 // IJ uses 0-based line-numbers; external source browsers use 1-based
191 val line = doc.getLineNumber(psi.getTextRange().getStartOffset()) + 1
192 url += linkDef.lineSuffix + line.toString()
193 }
194 }
195 append(DocumentationNode(url, Content.Empty, DocumentationNode.Kind.SourceUrl),
196 DocumentationReference.Kind.Detail);
197 }
198 }
199
200 private fun getTargetElement(sourceElement: SourceElement): PsiElement? {
201 val psi = sourceElement.getPsi()
202 return if (psi is PsiNameIdentifierOwner) psi.getNameIdentifier() else psi
203 }
204
205 fun findSourceLinkDefinition(path: String) = options.sourceLinks.firstOrNull { path.startsWith(it.path) }
206
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400207 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
208 // do not include generated code
209 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
210 return
211
212 if (options.includeNonPublic
213 || descriptor !is MemberDescriptor
214 || descriptor.getVisibility() in visibleToDocumentation) {
215 append(descriptor.build(), kind)
216 }
217 }
218
219 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
220 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
221 }
222
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100223 fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
224 externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
225 if (descriptor is CallableMemberDescriptor) {
226 val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
227 if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
228 val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
229 return externalClassNodes.getOrPut(fqName, {
230 val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
231 append(newNode, DocumentationReference.Kind.Member)
232 newNode
233 })
234 }
235 }
236 return this
237 }
238
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400239 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
240 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
241 for ((name, parts) in fragments.groupBy { it.fqName }) {
242 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400243 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400244 for ((packageName, declarations) in descriptors) {
Dmitry Jemerovc5fc45c2015-01-12 13:25:58 +0100245 println(" package $packageName: ${declarations.count()} declarations")
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400246 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100247 val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
248 declarations.forEach { descriptor ->
249 val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
250 parent.appendChild(descriptor, DocumentationReference.Kind.Member)
251 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400252 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400253 }
254 }
255
256 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
257 is ClassDescriptor -> build()
258 is ConstructorDescriptor -> build()
259 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400260 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400261 is PropertyAccessorDescriptor -> build()
262 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400263 is TypeParameterDescriptor -> build()
264 is ValueParameterDescriptor -> build()
265 is ReceiverParameterDescriptor -> build()
266 else -> throw IllegalStateException("Descriptor $this is not known")
267 }
268
269 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
270 fun ClassDescriptor.build(): DocumentationNode {
271 val kind = when (getKind()) {
272 ClassKind.OBJECT -> Kind.Object
273 ClassKind.CLASS_OBJECT -> Kind.Object
274 ClassKind.TRAIT -> Kind.Interface
275 ClassKind.ENUM_CLASS -> Kind.Enum
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100276 ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400277 ClassKind.ENUM_ENTRY -> Kind.EnumItem
278 else -> Kind.Class
279 }
280 val node = DocumentationNode(this, kind)
281 node.appendSupertypes(this)
Dmitry Jemerovd33f5b82015-01-12 17:49:34 +0100282 if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400283 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
Dmitry Jemerov1ce53732015-01-13 16:19:42 +0100284 val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS)
285 getConstructors().filter { it.getValueParameters().size() > 0 }
286 else
287 getConstructors()
288 node.appendChildren(constructorsToDocument, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400289 }
290 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100291 val classObjectDescriptor = getClassObjectDescriptor()
292 if (classObjectDescriptor != null) {
293 node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
294 DocumentationReference.Kind.Member)
295 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100296 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100297 node.appendSourceLink(getSource())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400298 register(this, node)
299 return node
300 }
301
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400302 fun ConstructorDescriptor.build(): DocumentationNode {
303 val node = DocumentationNode(this, Kind.Constructor)
304 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
305 register(this, node)
306 return node
307 }
308
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100309 private fun DeclarationDescriptor.inClassObject() =
310 getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
311
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100312 fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
313 val extensionReceiver = getExtensionReceiverParameter()
314 if (extensionReceiver != null) {
315 val type = extensionReceiver.getType()
316 return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
317 }
318 return null
319 }
320
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400321 fun FunctionDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100322 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400323
324 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
325 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
326 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
327 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100328 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100329 node.appendSourceLink(getSource())
330
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400331 register(this, node)
332 return node
333
334 }
335
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400336 fun PropertyAccessorDescriptor.build(): DocumentationNode {
337 val doc = parseDocumentation(this)
338 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300339 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400340
341 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
342 node.appendType(getReturnType())
343 register(this, node)
344 return node
345 }
346
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400347 fun PropertyDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100348 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectProperty else Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400349 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400350 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
351 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100352 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100353 node.appendSourceLink(getSource())
Dmitry Jemerovfaad9012015-01-14 14:00:55 +0100354 if (isVar()) {
355 node.append(DocumentationNode("var", Content.Empty, DocumentationNode.Kind.Modifier),
356 DocumentationReference.Kind.Detail)
357 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400358 getGetter()?.let {
359 if (!it.isDefault())
360 node.appendChild(it, DocumentationReference.Kind.Member)
361 }
362 getSetter()?.let {
363 if (!it.isDefault())
364 node.appendChild(it, DocumentationReference.Kind.Member)
365 }
366
367 register(this, node)
368 return node
369 }
370
371 fun ValueParameterDescriptor.build(): DocumentationNode {
372 val node = DocumentationNode(this, Kind.Parameter)
Dmitry Jemerov2e277ba2015-01-13 16:38:41 +0100373 val varargType = getVarargElementType()
374 if (varargType != null) {
375 node.append(DocumentationNode("vararg", Content.Empty, Kind.Annotation), DocumentationReference.Kind.Annotation)
376 node.appendType(varargType)
377 } else {
378 node.appendType(getType())
379 }
Dmitry Jemerovf36d9b02015-01-14 19:38:58 +0100380 if (hasDefaultValue()) {
381 val psi = getSource().getPsi() as? JetParameter
382 if (psi != null) {
383 val defaultValueText = psi.getDefaultValue()?.getText()
384 if (defaultValueText != null) {
385 node.append(DocumentationNode(defaultValueText, Content.Empty, Kind.Value), DocumentationReference.Kind.Detail)
386 }
387 }
388 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100389 node.appendAnnotations(this)
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300390 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400391 return node
392 }
393
394 fun TypeParameterDescriptor.build(): DocumentationNode {
395 val doc = parseDocumentation(this)
396 val name = getName().asString()
397 val prefix = when (getVariance()) {
398 Variance.IN_VARIANCE -> "in "
399 Variance.OUT_VARIANCE -> "out "
400 else -> ""
401 }
402
403 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
404
405 val builtIns = KotlinBuiltIns.getInstance()
406 for (constraint in getUpperBounds()) {
407 if (constraint == builtIns.getDefaultBound())
408 continue
409 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
410 node.append(constraintNode, DocumentationReference.Kind.Detail)
411 }
412
413 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300414 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400415 continue
416 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
417 node.append(constraintNode, DocumentationReference.Kind.Detail)
418 }
419 return node
420 }
421
422 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400423 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400424 node.appendType(getType())
425 return node
426 }
427
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100428 fun AnnotationDescriptor.build(): DocumentationNode? {
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100429 val annotationClass = getType().getConstructor().getDeclarationDescriptor()
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100430 if (ErrorUtils.isError(annotationClass)) {
431 return null
432 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100433 val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100434 val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
435 arguments.forEach {
436 val valueNode = it.second.build()
437 if (valueNode != null) {
438 val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
439 paramNode.append(valueNode, DocumentationReference.Kind.Detail)
440 node.append(paramNode, DocumentationReference.Kind.Detail)
441 }
442 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100443 return node
444 }
445
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100446 fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
447 val value = getValue()
448 val valueString = when(value) {
449 is String ->
450 "\"" + StringUtil.escapeStringCharacters(value) + "\""
451 is EnumEntrySyntheticClassDescriptor ->
452 value.getContainingDeclaration().getName().asString() + "." + value.getName()
453 else -> value?.toString()
454 }
455 return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
456 }
457
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400458 /**
459 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
460 *
461 * $receiver: [DocumentationContext] for node/descriptor resolutions
462 * $node: [DocumentationNode] to visit
463 */
464 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400465 if (node.kind != Kind.PropertyAccessor) {
466 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
467 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
468 val descriptor = links[receiverType]
469 if (descriptor != null) {
470 val typeNode = descriptorToNode[descriptor]
471 // if typeNode is null, extension is to external type like in a library
472 // should we create dummy node here?
473 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
474 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400475 }
476 }
477 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
478 val descriptor = links[detail]
479 if (descriptor != null) {
480 val typeNode = descriptorToNode[descriptor]
481 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
482 }
483 }
484 node.details.forEach { detail ->
485 val descriptor = links[detail]
486 if (descriptor != null) {
487 val typeNode = descriptorToNode[descriptor]
488 if (typeNode != null) {
489 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
490 }
491 }
492 }
493
Dmitry Jemerov0dd5ea32015-01-14 13:30:43 +0100494 val descriptor = nodeToDescriptor[node]
495 if (descriptor is FunctionDescriptor) {
496 val overrides = descriptor.getOverriddenDescriptors();
497 overrides?.forEach {
498 addOverrideLink(node, it)
499 }
500 }
501
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400502 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400503
504 for (child in node.members) {
505 resolveReferences(child)
506 }
507 for (child in node.details) {
508 resolveReferences(child)
509 }
510 }
511
Dmitry Jemerov0dd5ea32015-01-14 13:30:43 +0100512 /**
513 * Add an override link from a function node to the node corresponding to the specified descriptor.
514 * Note that this descriptor may be contained in a class where the function is not actually overridden
515 * (just inherited from the parent), so we need to go further up the override chain to find a function
516 * which exists in the code and for which we do have a documentation node.
517 */
518 private fun addOverrideLink(node: DocumentationNode, overriddenDescriptor: FunctionDescriptor) {
519 val overriddenNode = descriptorToNode[overriddenDescriptor.getOriginal()]
520 if (overriddenNode != null) {
521 node.addReferenceTo(overriddenNode, DocumentationReference.Kind.Override)
522 } else {
523 overriddenDescriptor.getOverriddenDescriptors().forEach { addOverrideLink(node, it) }
524 }
525 }
526
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300527 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400528 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300529 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400530 }
531
532 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100533 val resolvedContentChildren = content.children.map { resolveContentLink(node, it) }
534 content.children.clear()
535 content.children.addAll(resolvedContentChildren)
536 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400537
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100538 private fun resolveContentLink(node: DocumentationNode, content: ContentNode): ContentNode {
539 if (content is ContentExternalLink) {
540 val referenceText = content.href
541 val symbol = resolveReference(getResolutionScope(node), referenceText)
542 // don't include unresolved links in generated doc
543 // assume that if an href doesn't contain '/', it's not an attempt to reference an external file
544 if (symbol != null || "/" !in referenceText) {
545 val targetNode = descriptorToNode[symbol]
546 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
547 contentLink.children.addAll(content.children.map { resolveContentLink(node, it) })
548 return contentLink
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400549 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400550 }
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100551 resolveContentLinks(node, content)
552 return content
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400553 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300554
555 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
556 if (Name.isValidIdentifier(reference)) {
557 val scope = getResolutionScope(context)
558 val symbolName = Name.guess(reference)
559 return scope.getLocalVariable(symbolName) ?:
560 scope.getProperties(symbolName).firstOrNull() ?:
561 scope.getFunctions(symbolName).firstOrNull() ?:
562 scope.getClassifier(symbolName)
563
564 }
565
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200566 if ("." !in reference)
567 return null
568
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300569 val names = reference.split('.')
570 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
571 nextContext?.let { resolveReference(it, name) }
572 }
573
574 return result
575 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400576}