blob: 7d38480a59e3148f6d75fc48b9336f5024e9a6f0 [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)
38 val node = DocumentationNode(descriptor.getName().asString(), doc, kind)
39 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkov92b82522014-10-14 19:14:13 +040040 node.appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040041 if (descriptor !is ConstructorDescriptor) {
42 node.appendModality(descriptor)
43 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040044 }
45 return node
46 }
47
48 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
49 addReferenceTo(child, kind)
50 when (kind) {
51 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
52 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
53 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
54 }
55 }
56
57 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
58 val modifier = descriptor.getModality().name().toLowerCase()
59 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
60 append(node, DocumentationReference.Kind.Detail)
61 }
62
63 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
64 val modifier = descriptor.getVisibility().toString()
65 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
66 append(node, DocumentationReference.Kind.Detail)
67 }
68
69 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
70 val superTypes = descriptor.getTypeConstructor().getSupertypes()
71 for (superType in superTypes) {
72 if (superType.toString() != "Any")
73 appendType(superType, DocumentationNode.Kind.Supertype)
74 }
75 }
76
77 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
78 val prefix = when (projection.getProjectionKind()) {
79 Variance.IN_VARIANCE -> "in "
80 Variance.OUT_VARIANCE -> "out "
81 else -> ""
82 }
83 appendType(projection.getType(), kind, prefix)
84 }
85
86 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
87 if (jetType == null)
88 return
89 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
90 val name = when (classifierDescriptor) {
Ilya Ryzhenkov5d0af0b2014-12-04 15:52:12 +030091 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isMarkedNullable()) "?" else ""
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040092 else -> "<anonymous>"
93 }
94 val node = DocumentationNode(name, Content.Empty, kind)
95 if (classifierDescriptor != null)
96 link(node, classifierDescriptor)
97
98 append(node, DocumentationReference.Kind.Detail)
99 for (typeArgument in jetType.getArguments())
100 node.appendProjection(typeArgument)
101 }
102
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400103 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
104 // do not include generated code
105 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
106 return
107
108 if (options.includeNonPublic
109 || descriptor !is MemberDescriptor
110 || descriptor.getVisibility() in visibleToDocumentation) {
111 append(descriptor.build(), kind)
112 }
113 }
114
115 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
116 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
117 }
118
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400119 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
120 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
121 for ((name, parts) in fragments.groupBy { it.fqName }) {
122 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400123 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400124 for ((packageName, declarations) in descriptors) {
125 println(" package $packageName: ${declarations.count()} nodes")
126 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
127 packageNode.appendChildren(declarations, DocumentationReference.Kind.Member)
128 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400129 }
130 }
131
132 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
133 is ClassDescriptor -> build()
134 is ConstructorDescriptor -> build()
135 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400136 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400137 is PropertyAccessorDescriptor -> build()
138 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400139 is TypeParameterDescriptor -> build()
140 is ValueParameterDescriptor -> build()
141 is ReceiverParameterDescriptor -> build()
142 else -> throw IllegalStateException("Descriptor $this is not known")
143 }
144
145 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
146 fun ClassDescriptor.build(): DocumentationNode {
147 val kind = when (getKind()) {
148 ClassKind.OBJECT -> Kind.Object
149 ClassKind.CLASS_OBJECT -> Kind.Object
150 ClassKind.TRAIT -> Kind.Interface
151 ClassKind.ENUM_CLASS -> Kind.Enum
152 ClassKind.ENUM_ENTRY -> Kind.EnumItem
153 else -> Kind.Class
154 }
155 val node = DocumentationNode(this, kind)
156 node.appendSupertypes(this)
157 if (getKind() != ClassKind.OBJECT) {
158 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
159 node.appendChildren(getConstructors(), DocumentationReference.Kind.Member)
160 val classObjectDescriptor = getClassObjectDescriptor()
161 if (classObjectDescriptor != null)
162 node.appendChild(classObjectDescriptor, DocumentationReference.Kind.Member)
163 }
164 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
165 register(this, node)
166 return node
167 }
168
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400169 fun ConstructorDescriptor.build(): DocumentationNode {
170 val node = DocumentationNode(this, Kind.Constructor)
171 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
172 register(this, node)
173 return node
174 }
175
176 fun FunctionDescriptor.build(): DocumentationNode {
177 val node = DocumentationNode(this, Kind.Function)
178
179 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
180 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
181 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
182 node.appendType(getReturnType())
183 register(this, node)
184 return node
185
186 }
187
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400188 fun PropertyAccessorDescriptor.build(): DocumentationNode {
189 val doc = parseDocumentation(this)
190 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
191 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor)
192
193 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
194 node.appendType(getReturnType())
195 register(this, node)
196 return node
197 }
198
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400199 fun PropertyDescriptor.build(): DocumentationNode {
200 val node = DocumentationNode(this, Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400201 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400202 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
203 node.appendType(getReturnType())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400204 getGetter()?.let {
205 if (!it.isDefault())
206 node.appendChild(it, DocumentationReference.Kind.Member)
207 }
208 getSetter()?.let {
209 if (!it.isDefault())
210 node.appendChild(it, DocumentationReference.Kind.Member)
211 }
212
213 register(this, node)
214 return node
215 }
216
217 fun ValueParameterDescriptor.build(): DocumentationNode {
218 val node = DocumentationNode(this, Kind.Parameter)
219 node.appendType(getType())
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300220 register(this, node)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400221 return node
222 }
223
224 fun TypeParameterDescriptor.build(): DocumentationNode {
225 val doc = parseDocumentation(this)
226 val name = getName().asString()
227 val prefix = when (getVariance()) {
228 Variance.IN_VARIANCE -> "in "
229 Variance.OUT_VARIANCE -> "out "
230 else -> ""
231 }
232
233 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
234
235 val builtIns = KotlinBuiltIns.getInstance()
236 for (constraint in getUpperBounds()) {
237 if (constraint == builtIns.getDefaultBound())
238 continue
239 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
240 node.append(constraintNode, DocumentationReference.Kind.Detail)
241 }
242
243 for (constraint in getLowerBounds()) {
Ilya Ryzhenkov21174162014-12-04 14:57:12 +0300244 if (KotlinBuiltIns.isNothing(constraint))
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400245 continue
246 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
247 node.append(constraintNode, DocumentationReference.Kind.Detail)
248 }
249 return node
250 }
251
252 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400253 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400254 node.appendType(getType())
255 return node
256 }
257
258 /**
259 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
260 *
261 * $receiver: [DocumentationContext] for node/descriptor resolutions
262 * $node: [DocumentationNode] to visit
263 */
264 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400265 if (node.kind != Kind.PropertyAccessor) {
266 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
267 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
268 val descriptor = links[receiverType]
269 if (descriptor != null) {
270 val typeNode = descriptorToNode[descriptor]
271 // if typeNode is null, extension is to external type like in a library
272 // should we create dummy node here?
273 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
274 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400275 }
276 }
277 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
278 val descriptor = links[detail]
279 if (descriptor != null) {
280 val typeNode = descriptorToNode[descriptor]
281 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
282 }
283 }
284 node.details.forEach { detail ->
285 val descriptor = links[detail]
286 if (descriptor != null) {
287 val typeNode = descriptorToNode[descriptor]
288 if (typeNode != null) {
289 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
290 }
291 }
292 }
293
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400294 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400295
296 for (child in node.members) {
297 resolveReferences(child)
298 }
299 for (child in node.details) {
300 resolveReferences(child)
301 }
302 }
303
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300304 fun getResolutionScope(node: DocumentationNode): DeclarationDescriptor {
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400305 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300306 return descriptor
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400307 }
308
309 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
310 val snapshot = content.children.toList()
311 for (child in snapshot) {
312 if (child is ContentExternalLink) {
313 val referenceText = child.href
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300314 val symbol = resolveReference(getResolutionScope(node), referenceText)
315 if (symbol != null) {
316 val targetNode = descriptorToNode[symbol]
317 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400318
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300319 val index = content.children.indexOf(child)
320 content.children.remove(index)
321 contentLink.children.addAll(child.children)
322 content.children.add(index, contentLink)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400323 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300324
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400325 }
326 resolveContentLinks(node, child)
327 }
328 }
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300329
330 private fun resolveReference(context: DeclarationDescriptor, reference: String): DeclarationDescriptor? {
331 if (Name.isValidIdentifier(reference)) {
332 val scope = getResolutionScope(context)
333 val symbolName = Name.guess(reference)
334 return scope.getLocalVariable(symbolName) ?:
335 scope.getProperties(symbolName).firstOrNull() ?:
336 scope.getFunctions(symbolName).firstOrNull() ?:
337 scope.getClassifier(symbolName)
338
339 }
340
Ilya Ryzhenkov18399492014-12-22 09:50:17 +0200341 if ("." !in reference)
342 return null
343
Ilya Ryzhenkovbd6cddd2014-12-16 21:41:32 +0300344 val names = reference.split('.')
345 val result = names.fold<String, DeclarationDescriptor?>(context) {(nextContext, name) ->
346 nextContext?.let { resolveReference(it, name) }
347 }
348
349 return result
350 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400351}