blob: aeea4a55c86d3082fc796f3761ad039a9924d7bf [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.*
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +04009
Ilya Ryzhenkov471039e2014-10-12 22:37:07 +040010public data class DocumentationOptions(val includeNonPublic: Boolean = false)
11
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040012class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +040013 val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040014 val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
15 val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
16 val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
17 val packages = hashMapOf<FqName, DocumentationNode>()
18
19 fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040020 val docText = descriptor.getDocumentationElements().map { it.extractText() }.join("\n")
Ilya Ryzhenkovc9ca0d82014-12-15 20:54:16 +030021 val tree = parseMarkdown(docText)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040022 //println(tree.toTestString())
Ilya Ryzhenkovad14ea92014-10-13 18:22:02 +040023 val content = buildContent(tree, descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040024 return content
25 }
26
27 fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
28 links.put(node, descriptor)
29 }
30
31 fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
32 descriptorToNode.put(descriptor, node)
33 nodeToDescriptor.put(node, descriptor)
34 }
35
36 fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
37 val doc = parseDocumentation(descriptor)
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030038 val node = DocumentationNode(descriptor.getName().asString(), doc, kind).withModifiers(descriptor)
39 return node
40 }
41
42 private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor) : DocumentationNode{
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040043 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030044 appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040045 if (descriptor !is ConstructorDescriptor) {
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030046 appendModality(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040047 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040048 }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +030049 return this
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040050 }
51
52 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
53 addReferenceTo(child, kind)
54 when (kind) {
55 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
56 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
57 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
58 }
59 }
60
61 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
62 val modifier = descriptor.getModality().name().toLowerCase()
63 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
64 append(node, DocumentationReference.Kind.Detail)
65 }
66
67 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
68 val modifier = descriptor.getVisibility().toString()
69 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
70 append(node, DocumentationReference.Kind.Detail)
71 }
72
73 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
74 val superTypes = descriptor.getTypeConstructor().getSupertypes()
75 for (superType in superTypes) {
76 if (superType.toString() != "Any")
77 appendType(superType, DocumentationNode.Kind.Supertype)
78 }
79 }
80
81 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
82 val prefix = when (projection.getProjectionKind()) {
83 Variance.IN_VARIANCE -> "in "
84 Variance.OUT_VARIANCE -> "out "
85 else -> ""
86 }
87 appendType(projection.getType(), kind, prefix)
88 }
89
90 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
91 if (jetType == null)
92 return
93 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
94 val name = when (classifierDescriptor) {
Ilya Ryzhenkov5d0af0b2014-12-04 15:52:12 +030095 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isMarkedNullable()) "?" else ""
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040096 else -> "<anonymous>"
97 }
98 val node = DocumentationNode(name, Content.Empty, kind)
99 if (classifierDescriptor != null)
100 link(node, classifierDescriptor)
101
102 append(node, DocumentationReference.Kind.Detail)
103 for (typeArgument in jetType.getArguments())
104 node.appendProjection(typeArgument)
105 }
106
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400107 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
108 // do not include generated code
109 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
110 return
111
112 if (options.includeNonPublic
113 || descriptor !is MemberDescriptor
114 || descriptor.getVisibility() in visibleToDocumentation) {
115 append(descriptor.build(), kind)
116 }
117 }
118
119 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
120 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
121 }
122
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400123 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
124 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
125 for ((name, parts) in fragments.groupBy { it.fqName }) {
126 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400127 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400128 for ((packageName, declarations) in descriptors) {
129 println(" package $packageName: ${declarations.count()} nodes")
130 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
131 packageNode.appendChildren(declarations, DocumentationReference.Kind.Member)
132 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400133 }
134 }
135
136 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
137 is ClassDescriptor -> build()
138 is ConstructorDescriptor -> build()
139 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400140 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400141 is PropertyAccessorDescriptor -> build()
142 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400143 is TypeParameterDescriptor -> build()
144 is ValueParameterDescriptor -> build()
145 is ReceiverParameterDescriptor -> build()
146 else -> throw IllegalStateException("Descriptor $this is not known")
147 }
148
149 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
150 fun ClassDescriptor.build(): DocumentationNode {
151 val kind = when (getKind()) {
152 ClassKind.OBJECT -> Kind.Object
153 ClassKind.CLASS_OBJECT -> Kind.Object
154 ClassKind.TRAIT -> Kind.Interface
155 ClassKind.ENUM_CLASS -> Kind.Enum
156 ClassKind.ENUM_ENTRY -> Kind.EnumItem
157 else -> Kind.Class
158 }
159 val node = DocumentationNode(this, kind)
160 node.appendSupertypes(this)
161 if (getKind() != ClassKind.OBJECT) {
162 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
163 node.appendChildren(getConstructors(), DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400164 }
165 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100166 val classObjectDescriptor = getClassObjectDescriptor()
167 if (classObjectDescriptor != null) {
168 node.appendChildren(classObjectDescriptor.getDefaultType().getMemberScope().getAllDescriptors(),
169 DocumentationReference.Kind.Member)
170 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400171 register(this, node)
172 return node
173 }
174
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400175 fun ConstructorDescriptor.build(): DocumentationNode {
176 val node = DocumentationNode(this, Kind.Constructor)
177 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
178 register(this, node)
179 return node
180 }
181
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100182 private fun DeclarationDescriptor.inClassObject() =
183 getContainingDeclaration().let { it is ClassDescriptor && it.getKind() == ClassKind.CLASS_OBJECT }
184
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400185 fun FunctionDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100186 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectFunction else Kind.Function)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400187
188 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
189 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
190 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
191 node.appendType(getReturnType())
192 register(this, node)
193 return node
194
195 }
196
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400197 fun PropertyAccessorDescriptor.build(): DocumentationNode {
198 val doc = parseDocumentation(this)
199 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300200 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400201
202 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
203 node.appendType(getReturnType())
204 register(this, node)
205 return node
206 }
207
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400208 fun PropertyDescriptor.build(): DocumentationNode {
Dmitry Jemerovcedaeb42014-12-29 20:50:26 +0100209 val node = DocumentationNode(this, if (inClassObject()) Kind.ClassObjectProperty else Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400210 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400211 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
212 node.appendType(getReturnType())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400213 getGetter()?.let {
214 if (!it.isDefault())
215 node.appendChild(it, DocumentationReference.Kind.Member)
216 }
217 getSetter()?.let {
218 if (!it.isDefault())
219 node.appendChild(it, DocumentationReference.Kind.Member)
220 }
221
222 register(this, node)
223 return node
224 }
225
226 fun ValueParameterDescriptor.build(): DocumentationNode {
227 val node = DocumentationNode(this, Kind.Parameter)
228 node.appendType(getType())
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300229 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400230 return node
231 }
232
233 fun TypeParameterDescriptor.build(): DocumentationNode {
234 val doc = parseDocumentation(this)
235 val name = getName().asString()
236 val prefix = when (getVariance()) {
237 Variance.IN_VARIANCE -> "in "
238 Variance.OUT_VARIANCE -> "out "
239 else -> ""
240 }
241
242 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
243
244 val builtIns = KotlinBuiltIns.getInstance()
245 for (constraint in getUpperBounds()) {
246 if (constraint == builtIns.getDefaultBound())
247 continue
248 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
249 node.append(constraintNode, DocumentationReference.Kind.Detail)
250 }
251
252 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300253 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400254 continue
255 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
256 node.append(constraintNode, DocumentationReference.Kind.Detail)
257 }
258 return node
259 }
260
261 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400262 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400263 node.appendType(getType())
264 return node
265 }
266
267 /**
268 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
269 *
270 * $receiver: [DocumentationContext] for node/descriptor resolutions
271 * $node: [DocumentationNode] to visit
272 */
273 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400274 if (node.kind != Kind.PropertyAccessor) {
275 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
276 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
277 val descriptor = links[receiverType]
278 if (descriptor != null) {
279 val typeNode = descriptorToNode[descriptor]
280 // if typeNode is null, extension is to external type like in a library
281 // should we create dummy node here?
282 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
283 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400284 }
285 }
286 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
287 val descriptor = links[detail]
288 if (descriptor != null) {
289 val typeNode = descriptorToNode[descriptor]
290 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
291 }
292 }
293 node.details.forEach { detail ->
294 val descriptor = links[detail]
295 if (descriptor != null) {
296 val typeNode = descriptorToNode[descriptor]
297 if (typeNode != null) {
298 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
299 }
300 }
301 }
302
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400303 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400304
305 for (child in node.members) {
306 resolveReferences(child)
307 }
308 for (child in node.details) {
309 resolveReferences(child)
310 }
311 }
312
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300313 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400314 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300315 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400316 }
317
318 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
319 val snapshot = content.children.toList()
320 for (child in snapshot) {
321 if (child is ContentExternalLink) {
322 val referenceText = child.href
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300323 val symbol = resolveReference(getResolutionScope(node), referenceText)
324 if (symbol != null) {
325 val targetNode = descriptorToNode[symbol]
326 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400327
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300328 val index = content.children.indexOf(child)
329 content.children.remove(index)
330 contentLink.children.addAll(child.children)
331 content.children.add(index, contentLink)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400332 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300333
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400334 }
335 resolveContentLinks(node, child)
336 }
337 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300338
339 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
340 if (Name.isValidIdentifier(reference)) {
341 val scope = getResolutionScope(context)
342 val symbolName = Name.guess(reference)
343 return scope.getLocalVariable(symbolName) ?:
344 scope.getProperties(symbolName).firstOrNull() ?:
345 scope.getFunctions(symbolName).firstOrNull() ?:
346 scope.getClassifier(symbolName)
347
348 }
349
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200350 if ("." !in reference)
351 return null
352
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300353 val names = reference.split('.')
354 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
355 nextContext?.let { resolveReference(it, name) }
356 }
357
358 return result
359 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400360}