blob: 1b37dc51db27d0ecd3e64a190fde9bdfdcb2e690 [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
Dmitry Jemerov4ed5b2e2015-02-05 16:53:40 +010025import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040026
Dmitry Jemerov6146fa82015-01-14 18:46:36 +010027public data class DocumentationOptions(val includeNonPublic: Boolean = false,
28 val sourceLinks: List<SourceLinkDefinition>)
Ilya Ryzhenkov471039e2014-10-12 22:37:07 +040029
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +010030private fun isSamePackage(descriptor1: DeclarationDescriptor, descriptor2: DeclarationDescriptor): Boolean {
31 val package1 = DescriptorUtils.getParentOfType(descriptor1, javaClass<PackageFragmentDescriptor>())
32 val package2 = DescriptorUtils.getParentOfType(descriptor2, javaClass<PackageFragmentDescriptor>())
33 return package1 != null && package2 != null && package1.fqName == package2.fqName
34}
35
Dmitry Jemerov0783f6f2015-01-15 14:59:50 +010036class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions, val logger: DokkaLogger) {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +040037 val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040038 val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
39 val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
40 val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
41 val packages = hashMapOf<FqName, DocumentationNode>()
42
43 fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010044 val kdoc = findKDoc(descriptor)
Dmitry Jemerov0fac1d92015-01-30 19:01:40 +010045 if (kdoc == null) {
46 return Content.Empty
47 }
48 val tree = parseMarkdown(kdoc.getContent())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040049 //println(tree.toTestString())
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010050 val content = buildContent(tree)
51 if (kdoc is KDocSection) {
Dmitry Jemerov0fac1d92015-01-30 19:01:40 +010052 val tags = kdoc.getTags()
53 tags.forEach {
Dmitry Jemerov0e76eed2015-02-06 16:10:04 +010054 if (it.getName() == "sample") {
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010055 content.append(functionBody(descriptor, it.getContent()))
56 } else {
57 val section = content.addSection(displayName(it.getName()), it.getSubjectName())
58 val sectionContent = it.getContent()
59 val markdownNode = parseMarkdown(sectionContent)
60 buildInlineContentTo(markdownNode, section)
61 }
62 }
63 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040064 return content
65 }
66
Dmitry Jemerov0fac1d92015-01-30 19:01:40 +010067 fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, javaClass<KDocTag>()) ?: array()
68
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010069 fun displayName(sectionName: String?): String? =
70 when(sectionName) {
71 "param" -> "Parameters"
72 "throws", "exception" -> "Exceptions"
Dmitry Jemerov0fac1d92015-01-30 19:01:40 +010073 else -> sectionName?.capitalize()
Dmitry Jemerovbfd9ffd2015-01-30 17:59:15 +010074 }
75
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040076 fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
77 links.put(node, descriptor)
78 }
79
80 fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
81 descriptorToNode.put(descriptor, node)
82 nodeToDescriptor.put(node, descriptor)
83 }
84
85 fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
86 val doc = parseDocumentation(descriptor)
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030087 val node = DocumentationNode(descriptor.getName().asString(), doc, kind).withModifiers(descriptor)
88 return node
89 }
90
91 private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor) : DocumentationNode{
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040092 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030093 appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040094 if (descriptor !is ConstructorDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030095 appendModality(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040096 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040097 }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030098 return this
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040099 }
100
101 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
102 addReferenceTo(child, kind)
103 when (kind) {
104 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
105 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
106 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
107 }
108 }
109
110 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
Dmitry Jemerov6d234302015-01-13 17:15:52 +0100111 var modality = descriptor.getModality()
112 if (modality == Modality.OPEN) {
113 val containingClass = descriptor.getContainingDeclaration() as? ClassDescriptor
114 if (containingClass?.getModality() == Modality.FINAL) {
115 modality = Modality.FINAL
116 }
117 }
118 val modifier = modality.name().toLowerCase()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400119 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
120 append(node, DocumentationReference.Kind.Detail)
121 }
122
123 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
124 val modifier = descriptor.getVisibility().toString()
125 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
126 append(node, DocumentationReference.Kind.Detail)
127 }
128
129 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
130 val superTypes = descriptor.getTypeConstructor().getSupertypes()
131 for (superType in superTypes) {
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100132 if (!ignoreSupertype(superType))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400133 appendType(superType, DocumentationNode.Kind.Supertype)
134 }
135 }
136
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100137 private fun ignoreSupertype(superType: JetType): Boolean {
138 val superClass = superType.getConstructor()?.getDeclarationDescriptor() as? ClassDescriptor
139 if (superClass != null) {
140 val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
141 return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
142 }
143 return false
144 }
145
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400146 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
147 val prefix = when (projection.getProjectionKind()) {
148 Variance.IN_VARIANCE -> "in "
149 Variance.OUT_VARIANCE -> "out "
150 else -> ""
151 }
152 appendType(projection.getType(), kind, prefix)
153 }
154
155 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
156 if (jetType == null)
157 return
158 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
159 val name = when (classifierDescriptor) {
Ilya Ryzhenkov5d0af0b2014-12-04 15:52:12 +0300160 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isMarkedNullable()) "?" else ""
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400161 else -> "<anonymous>"
162 }
163 val node = DocumentationNode(name, Content.Empty, kind)
164 if (classifierDescriptor != null)
165 link(node, classifierDescriptor)
166
167 append(node, DocumentationReference.Kind.Detail)
168 for (typeArgument in jetType.getArguments())
169 node.appendProjection(typeArgument)
170 }
171
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100172 fun DocumentationNode.appendAnnotations(annotated: Annotated) {
173 annotated.getAnnotations().forEach {
Dmitry Jemerove17eaa52015-01-09 20:59:58 +0100174 val annotationNode = it.build()
175 if (annotationNode != null) {
176 append(annotationNode,
177 if (annotationNode.name == "deprecated") DocumentationReference.Kind.Deprecation else DocumentationReference.Kind.Annotation)
178 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100179 }
180 }
181
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100182 fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) {
183 val psi = getTargetElement(sourceElement)
184 val path = psi?.getContainingFile()?.getVirtualFile()?.getPath()
185 if (path == null) {
186 return
187 }
188 val absPath = File(path).getAbsolutePath()
189 val linkDef = findSourceLinkDefinition(absPath)
190 if (linkDef != null) {
191 var url = linkDef.url + path.substring(linkDef.path.length())
192 if (linkDef.lineSuffix != null) {
193 val doc = PsiDocumentManager.getInstance(psi!!.getProject()).getDocument(psi.getContainingFile())
194 if (doc != null) {
195 // IJ uses 0-based line-numbers; external source browsers use 1-based
196 val line = doc.getLineNumber(psi.getTextRange().getStartOffset()) + 1
197 url += linkDef.lineSuffix + line.toString()
198 }
199 }
200 append(DocumentationNode(url, Content.Empty, DocumentationNode.Kind.SourceUrl),
201 DocumentationReference.Kind.Detail);
202 }
203 }
204
205 private fun getTargetElement(sourceElement: SourceElement): PsiElement? {
206 val psi = sourceElement.getPsi()
207 return if (psi is PsiNameIdentifierOwner) psi.getNameIdentifier() else psi
208 }
209
210 fun findSourceLinkDefinition(path: String) = options.sourceLinks.firstOrNull { path.startsWith(it.path) }
211
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400212 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
213 // do not include generated code
214 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
215 return
216
217 if (options.includeNonPublic
218 || descriptor !is MemberDescriptor
219 || descriptor.getVisibility() in visibleToDocumentation) {
220 append(descriptor.build(), kind)
221 }
222 }
223
224 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
225 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
226 }
227
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100228 fun DocumentationNode.getParentForPackageMember(descriptor: DeclarationDescriptor,
229 externalClassNodes: MutableMap<FqName, DocumentationNode>): DocumentationNode {
230 if (descriptor is CallableMemberDescriptor) {
231 val extensionClassDescriptor = descriptor.getExtensionClassDescriptor()
232 if (extensionClassDescriptor != null && !isSamePackage(descriptor, extensionClassDescriptor)) {
233 val fqName = DescriptorUtils.getFqNameFromTopLevelClass(extensionClassDescriptor)
234 return externalClassNodes.getOrPut(fqName, {
235 val newNode = DocumentationNode(fqName.asString(), Content.Empty, Kind.ExternalClass)
236 append(newNode, DocumentationReference.Kind.Member)
237 newNode
238 })
239 }
240 }
241 return this
242 }
243
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400244 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
245 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
246 for ((name, parts) in fragments.groupBy { it.fqName }) {
247 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400248 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400249 for ((packageName, declarations) in descriptors) {
Dmitry Jemerov0783f6f2015-01-15 14:59:50 +0100250 logger.info(" package $packageName: ${declarations.count()} declarations")
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400251 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100252 val externalClassNodes = hashMapOf<FqName, DocumentationNode>()
253 declarations.forEach { descriptor ->
254 val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes)
255 parent.appendChild(descriptor, DocumentationReference.Kind.Member)
256 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400257 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400258 }
259 }
260
261 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
262 is ClassDescriptor -> build()
263 is ConstructorDescriptor -> build()
264 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400265 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400266 is PropertyAccessorDescriptor -> build()
267 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400268 is TypeParameterDescriptor -> build()
269 is ValueParameterDescriptor -> build()
270 is ReceiverParameterDescriptor -> build()
271 else -> throw IllegalStateException("Descriptor $this is not known")
272 }
273
274 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
275 fun ClassDescriptor.build(): DocumentationNode {
276 val kind = when (getKind()) {
277 ClassKind.OBJECT -> Kind.Object
278 ClassKind.CLASS_OBJECT -> Kind.Object
279 ClassKind.TRAIT -> Kind.Interface
280 ClassKind.ENUM_CLASS -> Kind.Enum
Dmitry Jemerov716483c2014-12-30 17:41:14 +0100281 ClassKind.ANNOTATION_CLASS -> Kind.AnnotationClass
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400282 ClassKind.ENUM_ENTRY -> Kind.EnumItem
283 else -> Kind.Class
284 }
285 val node = DocumentationNode(this, kind)
286 node.appendSupertypes(this)
Dmitry Jemerovd33f5b82015-01-12 17:49:34 +0100287 if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400288 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
Dmitry Jemerov1ce53732015-01-13 16:19:42 +0100289 val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS)
290 getConstructors().filter { it.getValueParameters().size() > 0 }
291 else
292 getConstructors()
293 node.appendChildren(constructorsToDocument, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400294 }
295 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100296 val classObjectDescriptor = getClassObjectDescriptor()
297 if (classObjectDescriptor != null) {
298 node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
299 DocumentationReference.Kind.Member)
300 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100301 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100302 node.appendSourceLink(getSource())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400303 register(this, node)
304 return node
305 }
306
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400307 fun ConstructorDescriptor.build(): DocumentationNode {
308 val node = DocumentationNode(this, Kind.Constructor)
309 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
310 register(this, node)
311 return node
312 }
313
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100314 private fun DeclarationDescriptor.inClassObject() =
315 getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
316
Dmitry Jemerov4b0dcee2015-01-09 19:48:44 +0100317 fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? {
318 val extensionReceiver = getExtensionReceiverParameter()
319 if (extensionReceiver != null) {
320 val type = extensionReceiver.getType()
321 return type.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
322 }
323 return null
324 }
325
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400326 fun FunctionDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100327 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400328
329 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
330 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
331 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
332 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100333 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100334 node.appendSourceLink(getSource())
335
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400336 register(this, node)
337 return node
338
339 }
340
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400341 fun PropertyAccessorDescriptor.build(): DocumentationNode {
342 val doc = parseDocumentation(this)
343 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300344 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400345
346 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
347 node.appendType(getReturnType())
348 register(this, node)
349 return node
350 }
351
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400352 fun PropertyDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100353 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectProperty else Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400354 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400355 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
356 node.appendType(getReturnType())
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100357 node.appendAnnotations(this)
Dmitry Jemerov6146fa82015-01-14 18:46:36 +0100358 node.appendSourceLink(getSource())
Dmitry Jemerovfaad9012015-01-14 14:00:55 +0100359 if (isVar()) {
360 node.append(DocumentationNode("var", Content.Empty, DocumentationNode.Kind.Modifier),
361 DocumentationReference.Kind.Detail)
362 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400363 getGetter()?.let {
364 if (!it.isDefault())
365 node.appendChild(it, DocumentationReference.Kind.Member)
366 }
367 getSetter()?.let {
368 if (!it.isDefault())
369 node.appendChild(it, DocumentationReference.Kind.Member)
370 }
371
372 register(this, node)
373 return node
374 }
375
376 fun ValueParameterDescriptor.build(): DocumentationNode {
377 val node = DocumentationNode(this, Kind.Parameter)
Dmitry Jemerov2e277ba2015-01-13 16:38:41 +0100378 val varargType = getVarargElementType()
379 if (varargType != null) {
380 node.append(DocumentationNode("vararg", Content.Empty, Kind.Annotation), DocumentationReference.Kind.Annotation)
381 node.appendType(varargType)
382 } else {
383 node.appendType(getType())
384 }
Dmitry Jemerovf36d9b02015-01-14 19:38:58 +0100385 if (hasDefaultValue()) {
386 val psi = getSource().getPsi() as? JetParameter
387 if (psi != null) {
388 val defaultValueText = psi.getDefaultValue()?.getText()
389 if (defaultValueText != null) {
390 node.append(DocumentationNode(defaultValueText, Content.Empty, Kind.Value), DocumentationReference.Kind.Detail)
391 }
392 }
393 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100394 node.appendAnnotations(this)
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300395 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400396 return node
397 }
398
399 fun TypeParameterDescriptor.build(): DocumentationNode {
400 val doc = parseDocumentation(this)
401 val name = getName().asString()
402 val prefix = when (getVariance()) {
403 Variance.IN_VARIANCE -> "in "
404 Variance.OUT_VARIANCE -> "out "
405 else -> ""
406 }
407
408 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
409
410 val builtIns = KotlinBuiltIns.getInstance()
411 for (constraint in getUpperBounds()) {
412 if (constraint == builtIns.getDefaultBound())
413 continue
414 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
415 node.append(constraintNode, DocumentationReference.Kind.Detail)
416 }
417
418 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300419 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400420 continue
421 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
422 node.append(constraintNode, DocumentationReference.Kind.Detail)
423 }
424 return node
425 }
426
427 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400428 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400429 node.appendType(getType())
430 return node
431 }
432
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100433 fun AnnotationDescriptor.build(): DocumentationNode? {
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100434 val annotationClass = getType().getConstructor().getDeclarationDescriptor()
Dmitry Jemerov1a479432015-01-09 19:57:54 +0100435 if (ErrorUtils.isError(annotationClass)) {
436 return null
437 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100438 val node = DocumentationNode(annotationClass.getName().asString(), Content.Empty, DocumentationNode.Kind.Annotation)
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100439 val arguments = getAllValueArguments().toList().sortBy { it.first.getIndex() }
440 arguments.forEach {
441 val valueNode = it.second.build()
442 if (valueNode != null) {
443 val paramNode = DocumentationNode(it.first.getName().asString(), Content.Empty, DocumentationNode.Kind.Parameter)
444 paramNode.append(valueNode, DocumentationReference.Kind.Detail)
445 node.append(paramNode, DocumentationReference.Kind.Detail)
446 }
447 }
Dmitry Jemerovef51f7e2014-12-30 16:34:08 +0100448 return node
449 }
450
Dmitry Jemerov69dd2982014-12-30 18:47:03 +0100451 fun CompileTimeConstant<out Any?>.build(): DocumentationNode? {
452 val value = getValue()
453 val valueString = when(value) {
454 is String ->
455 "\"" + StringUtil.escapeStringCharacters(value) + "\""
456 is EnumEntrySyntheticClassDescriptor ->
457 value.getContainingDeclaration().getName().asString() + "." + value.getName()
458 else -> value?.toString()
459 }
460 return if (valueString != null) DocumentationNode(valueString, Content.Empty, DocumentationNode.Kind.Value) else null
461 }
462
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400463 /**
464 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
465 *
466 * $receiver: [DocumentationContext] for node/descriptor resolutions
467 * $node: [DocumentationNode] to visit
468 */
469 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400470 if (node.kind != Kind.PropertyAccessor) {
471 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
472 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
473 val descriptor = links[receiverType]
474 if (descriptor != null) {
475 val typeNode = descriptorToNode[descriptor]
476 // if typeNode is null, extension is to external type like in a library
477 // should we create dummy node here?
478 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
479 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400480 }
481 }
482 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
483 val descriptor = links[detail]
484 if (descriptor != null) {
485 val typeNode = descriptorToNode[descriptor]
486 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
487 }
488 }
489 node.details.forEach { detail ->
490 val descriptor = links[detail]
491 if (descriptor != null) {
492 val typeNode = descriptorToNode[descriptor]
493 if (typeNode != null) {
494 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
495 }
496 }
497 }
498
Dmitry Jemerov0dd5ea32015-01-14 13:30:43 +0100499 val descriptor = nodeToDescriptor[node]
500 if (descriptor is FunctionDescriptor) {
501 val overrides = descriptor.getOverriddenDescriptors();
502 overrides?.forEach {
503 addOverrideLink(node, it)
504 }
505 }
506
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400507 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400508
509 for (child in node.members) {
510 resolveReferences(child)
511 }
512 for (child in node.details) {
513 resolveReferences(child)
514 }
515 }
516
Dmitry Jemerov0dd5ea32015-01-14 13:30:43 +0100517 /**
518 * Add an override link from a function node to the node corresponding to the specified descriptor.
519 * Note that this descriptor may be contained in a class where the function is not actually overridden
520 * (just inherited from the parent), so we need to go further up the override chain to find a function
521 * which exists in the code and for which we do have a documentation node.
522 */
523 private fun addOverrideLink(node: DocumentationNode, overriddenDescriptor: FunctionDescriptor) {
524 val overriddenNode = descriptorToNode[overriddenDescriptor.getOriginal()]
525 if (overriddenNode != null) {
526 node.addReferenceTo(overriddenNode, DocumentationReference.Kind.Override)
527 } else {
528 overriddenDescriptor.getOverriddenDescriptors().forEach { addOverrideLink(node, it) }
529 }
530 }
531
Dmitry Jemerov0e76eed2015-02-06 16:10:04 +0100532 fun getDescriptorForNode(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400533 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300534 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400535 }
536
537 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100538 val resolvedContentChildren = content.children.map { resolveContentLink(node, it) }
539 content.children.clear()
540 content.children.addAll(resolvedContentChildren)
541 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400542
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100543 private fun resolveContentLink(node: DocumentationNode, content: ContentNode): ContentNode {
544 if (content is ContentExternalLink) {
545 val referenceText = content.href
Dmitry Jemerov0e76eed2015-02-06 16:10:04 +0100546 val symbols = resolveKDocLink(session, getDescriptorForNode(node), null, referenceText.split('.').toList())
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100547 // don't include unresolved links in generated doc
548 // assume that if an href doesn't contain '/', it's not an attempt to reference an external file
Dmitry Jemerov4ed5b2e2015-02-05 16:53:40 +0100549 if (symbols.isNotEmpty() || "/" !in referenceText) {
550 val targetNode = if (symbols.isEmpty()) null else descriptorToNode[symbols.first()]
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100551 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
552 contentLink.children.addAll(content.children.map { resolveContentLink(node, it) })
553 return contentLink
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400554 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400555 }
Dmitry Jemerovd093fc12015-01-16 13:25:16 +0100556 resolveContentLinks(node, content)
557 return content
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400558 }
Dmitry Jemerov4ed5b2e2015-02-05 16:53:40 +0100559}