blob: d2c072008dd6e54dd7608d5f8ecb4067be68d758 [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)
164 val classObjectDescriptor = getClassObjectDescriptor()
165 if (classObjectDescriptor != null)
166 node.appendChild(classObjectDescriptor, DocumentationReference.Kind.Member)
167 }
168 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
169 register(this, node)
170 return node
171 }
172
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400173 fun ConstructorDescriptor.build(): DocumentationNode {
174 val node = DocumentationNode(this, Kind.Constructor)
175 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
176 register(this, node)
177 return node
178 }
179
180 fun FunctionDescriptor.build(): DocumentationNode {
181 val node = DocumentationNode(this, Kind.Function)
182
183 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
184 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
185 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
186 node.appendType(getReturnType())
187 register(this, node)
188 return node
189
190 }
191
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400192 fun PropertyAccessorDescriptor.build(): DocumentationNode {
193 val doc = parseDocumentation(this)
194 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
Ilya Ryzhenkovc7598762014-12-22 17:38:56 +0300195 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor).withModifiers(this)
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400196
197 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
198 node.appendType(getReturnType())
199 register(this, node)
200 return node
201 }
202
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400203 fun PropertyDescriptor.build(): DocumentationNode {
204 val node = DocumentationNode(this, Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400205 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400206 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
207 node.appendType(getReturnType())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400208 getGetter()?.let {
209 if (!it.isDefault())
210 node.appendChild(it, DocumentationReference.Kind.Member)
211 }
212 getSetter()?.let {
213 if (!it.isDefault())
214 node.appendChild(it, DocumentationReference.Kind.Member)
215 }
216
217 register(this, node)
218 return node
219 }
220
221 fun ValueParameterDescriptor.build(): DocumentationNode {
222 val node = DocumentationNode(this, Kind.Parameter)
223 node.appendType(getType())
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300224 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400225 return node
226 }
227
228 fun TypeParameterDescriptor.build(): DocumentationNode {
229 val doc = parseDocumentation(this)
230 val name = getName().asString()
231 val prefix = when (getVariance()) {
232 Variance.IN_VARIANCE -> "in "
233 Variance.OUT_VARIANCE -> "out "
234 else -> ""
235 }
236
237 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
238
239 val builtIns = KotlinBuiltIns.getInstance()
240 for (constraint in getUpperBounds()) {
241 if (constraint == builtIns.getDefaultBound())
242 continue
243 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
244 node.append(constraintNode, DocumentationReference.Kind.Detail)
245 }
246
247 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300248 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400249 continue
250 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
251 node.append(constraintNode, DocumentationReference.Kind.Detail)
252 }
253 return node
254 }
255
256 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400257 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400258 node.appendType(getType())
259 return node
260 }
261
262 /**
263 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
264 *
265 * $receiver: [DocumentationContext] for node/descriptor resolutions
266 * $node: [DocumentationNode] to visit
267 */
268 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400269 if (node.kind != Kind.PropertyAccessor) {
270 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
271 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
272 val descriptor = links[receiverType]
273 if (descriptor != null) {
274 val typeNode = descriptorToNode[descriptor]
275 // if typeNode is null, extension is to external type like in a library
276 // should we create dummy node here?
277 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
278 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400279 }
280 }
281 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
282 val descriptor = links[detail]
283 if (descriptor != null) {
284 val typeNode = descriptorToNode[descriptor]
285 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
286 }
287 }
288 node.details.forEach { detail ->
289 val descriptor = links[detail]
290 if (descriptor != null) {
291 val typeNode = descriptorToNode[descriptor]
292 if (typeNode != null) {
293 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
294 }
295 }
296 }
297
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400298 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400299
300 for (child in node.members) {
301 resolveReferences(child)
302 }
303 for (child in node.details) {
304 resolveReferences(child)
305 }
306 }
307
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300308 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400309 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300310 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400311 }
312
313 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
314 val snapshot = content.children.toList()
315 for (child in snapshot) {
316 if (child is ContentExternalLink) {
317 val referenceText = child.href
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300318 val symbol = resolveReference(getResolutionScope(node), referenceText)
319 if (symbol != null) {
320 val targetNode = descriptorToNode[symbol]
321 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400322
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300323 val index = content.children.indexOf(child)
324 content.children.remove(index)
325 contentLink.children.addAll(child.children)
326 content.children.add(index, contentLink)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400327 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300328
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400329 }
330 resolveContentLinks(node, child)
331 }
332 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300333
334 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
335 if (Name.isValidIdentifier(reference)) {
336 val scope = getResolutionScope(context)
337 val symbolName = Name.guess(reference)
338 return scope.getLocalVariable(symbolName) ?:
339 scope.getProperties(symbolName).firstOrNull() ?:
340 scope.getFunctions(symbolName).firstOrNull() ?:
341 scope.getClassifier(symbolName)
342
343 }
344
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200345 if ("." !in reference)
346 return null
347
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300348 val names = reference.split('.')
349 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
350 nextContext?.let { resolveReference(it, name) }
351 }
352
353 return result
354 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400355}