blob: c3f8ca57e30420ca0a1acff2ce0a1c48a46cf1ab [file] [log] [blame]
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +04001package org.jetbrains.dokka
2
3import org.jetbrains.jet.lang.descriptors.*
4import org.jetbrains.dokka.DocumentationNode.Kind
5import org.jetbrains.jet.lang.types.TypeProjection
6import org.jetbrains.jet.lang.types.Variance
7import org.jetbrains.jet.lang.types.JetType
8import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
9import org.jetbrains.jet.lang.resolve.BindingContext
10import org.jetbrains.jet.lang.resolve.name.Name
11import org.jetbrains.jet.lang.resolve.scopes.JetScope
12import org.jetbrains.jet.lang.psi.JetFile
13import org.jetbrains.jet.lang.resolve.name.FqName
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040014import org.jetbrains.jet.lang.resolve.lazy.ResolveSession
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040015
Ilya Ryzhenkov471039e2014-10-12 22:37:07 +040016public data class DocumentationOptions(val includeNonPublic: Boolean = false)
17
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040018class DocumentationBuilder(val session: ResolveSession, val options: DocumentationOptions) {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +040019 val visibleToDocumentation = setOf(Visibilities.INTERNAL, Visibilities.PROTECTED, Visibilities.PUBLIC)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040020 val descriptorToNode = hashMapOf<DeclarationDescriptor, DocumentationNode>()
21 val nodeToDescriptor = hashMapOf<DocumentationNode, DeclarationDescriptor>()
22 val links = hashMapOf<DocumentationNode, DeclarationDescriptor>()
23 val packages = hashMapOf<FqName, DocumentationNode>()
24
25 fun parseDocumentation(descriptor: DeclarationDescriptor): Content {
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +040026 val docText = descriptor.getDocumentationElements().map { it.extractText() }.join("\n")
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040027 val tree = MarkdownProcessor.parse(docText)
28 //println(tree.toTestString())
Ilya Ryzhenkovad14ea92014-10-13 18:22:02 +040029 val content = buildContent(tree, descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040030 return content
31 }
32
33 fun link(node: DocumentationNode, descriptor: DeclarationDescriptor) {
34 links.put(node, descriptor)
35 }
36
37 fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) {
38 descriptorToNode.put(descriptor, node)
39 nodeToDescriptor.put(node, descriptor)
40 }
41
42 fun DocumentationNode<T>(descriptor: T, kind: Kind): DocumentationNode where T : DeclarationDescriptor, T : Named {
43 val doc = parseDocumentation(descriptor)
44 val node = DocumentationNode(descriptor.getName().asString(), doc, kind)
45 if (descriptor is MemberDescriptor) {
Ilya Ryzhenkov92b82522014-10-14 19:14:13 +040046 node.appendVisibility(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040047 if (descriptor !is ConstructorDescriptor) {
48 node.appendModality(descriptor)
49 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +040050 }
51 return node
52 }
53
54 fun DocumentationNode.append(child: DocumentationNode, kind: DocumentationReference.Kind) {
55 addReferenceTo(child, kind)
56 when (kind) {
57 DocumentationReference.Kind.Detail -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
58 DocumentationReference.Kind.Member -> child.addReferenceTo(this, DocumentationReference.Kind.Owner)
59 DocumentationReference.Kind.Owner -> child.addReferenceTo(this, DocumentationReference.Kind.Member)
60 }
61 }
62
63 fun DocumentationNode.appendModality(descriptor: MemberDescriptor) {
64 val modifier = descriptor.getModality().name().toLowerCase()
65 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
66 append(node, DocumentationReference.Kind.Detail)
67 }
68
69 fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) {
70 val modifier = descriptor.getVisibility().toString()
71 val node = DocumentationNode(modifier, Content.Empty, DocumentationNode.Kind.Modifier)
72 append(node, DocumentationReference.Kind.Detail)
73 }
74
75 fun DocumentationNode.appendSupertypes(descriptor: ClassDescriptor) {
76 val superTypes = descriptor.getTypeConstructor().getSupertypes()
77 for (superType in superTypes) {
78 if (superType.toString() != "Any")
79 appendType(superType, DocumentationNode.Kind.Supertype)
80 }
81 }
82
83 fun DocumentationNode.appendProjection(projection: TypeProjection, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type) {
84 val prefix = when (projection.getProjectionKind()) {
85 Variance.IN_VARIANCE -> "in "
86 Variance.OUT_VARIANCE -> "out "
87 else -> ""
88 }
89 appendType(projection.getType(), kind, prefix)
90 }
91
92 fun DocumentationNode.appendType(jetType: JetType?, kind: DocumentationNode.Kind = DocumentationNode.Kind.Type, prefix: String = "") {
93 if (jetType == null)
94 return
95 val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
96 val name = when (classifierDescriptor) {
97 is Named -> prefix + classifierDescriptor.getName().asString() + if (jetType.isNullable()) "?" else ""
98 else -> "<anonymous>"
99 }
100 val node = DocumentationNode(name, Content.Empty, kind)
101 if (classifierDescriptor != null)
102 link(node, classifierDescriptor)
103
104 append(node, DocumentationReference.Kind.Detail)
105 for (typeArgument in jetType.getArguments())
106 node.appendProjection(typeArgument)
107 }
108
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400109 fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: DocumentationReference.Kind) {
110 // do not include generated code
111 if (descriptor is CallableMemberDescriptor && descriptor.getKind() != CallableMemberDescriptor.Kind.DECLARATION)
112 return
113
114 if (options.includeNonPublic
115 || descriptor !is MemberDescriptor
116 || descriptor.getVisibility() in visibleToDocumentation) {
117 append(descriptor.build(), kind)
118 }
119 }
120
121 fun DocumentationNode.appendChildren(descriptors: Iterable<DeclarationDescriptor>, kind: DocumentationReference.Kind) {
122 descriptors.forEach { descriptor -> appendChild(descriptor, kind) }
123 }
124
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400125 fun DocumentationNode.appendFragments(fragments: Collection<PackageFragmentDescriptor>) {
126 val descriptors = hashMapOf<String, List<DeclarationDescriptor>>()
127 for ((name, parts) in fragments.groupBy { it.fqName }) {
128 descriptors.put(name.asString(), parts.flatMap { it.getMemberScope().getAllDescriptors() })
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400129 }
Ilya Ryzhenkov2ebfb982014-10-13 00:19:18 +0400130 for ((packageName, declarations) in descriptors) {
131 println(" package $packageName: ${declarations.count()} nodes")
132 val packageNode = DocumentationNode(packageName, Content.Empty, Kind.Package)
133 packageNode.appendChildren(declarations, DocumentationReference.Kind.Member)
134 append(packageNode, DocumentationReference.Kind.Member)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400135 }
136 }
137
138 fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
139 is ClassDescriptor -> build()
140 is ConstructorDescriptor -> build()
141 is ScriptDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400142 is PropertyDescriptor -> build()
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400143 is PropertyAccessorDescriptor -> build()
144 is FunctionDescriptor -> build()
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400145 is TypeParameterDescriptor -> build()
146 is ValueParameterDescriptor -> build()
147 is ReceiverParameterDescriptor -> build()
148 else -> throw IllegalStateException("Descriptor $this is not known")
149 }
150
151 fun ScriptDescriptor.build(): DocumentationNode = getClassDescriptor().build()
152 fun ClassDescriptor.build(): DocumentationNode {
153 val kind = when (getKind()) {
154 ClassKind.OBJECT -> Kind.Object
155 ClassKind.CLASS_OBJECT -> Kind.Object
156 ClassKind.TRAIT -> Kind.Interface
157 ClassKind.ENUM_CLASS -> Kind.Enum
158 ClassKind.ENUM_ENTRY -> Kind.EnumItem
159 else -> Kind.Class
160 }
161 val node = DocumentationNode(this, kind)
162 node.appendSupertypes(this)
163 if (getKind() != ClassKind.OBJECT) {
164 node.appendChildren(getTypeConstructor().getParameters(), DocumentationReference.Kind.Detail)
165 node.appendChildren(getConstructors(), DocumentationReference.Kind.Member)
166 val classObjectDescriptor = getClassObjectDescriptor()
167 if (classObjectDescriptor != null)
168 node.appendChild(classObjectDescriptor, DocumentationReference.Kind.Member)
169 }
170 node.appendChildren(getDefaultType().getMemberScope().getAllDescriptors(), DocumentationReference.Kind.Member)
171 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
182 fun FunctionDescriptor.build(): DocumentationNode {
183 val node = DocumentationNode(this, Kind.Function)
184
185 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
186 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
187 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
188 node.appendType(getReturnType())
189 register(this, node)
190 return node
191
192 }
193
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400194 fun PropertyAccessorDescriptor.build(): DocumentationNode {
195 val doc = parseDocumentation(this)
196 val specialName = getName().asString().drop(1).takeWhile { it != '-' }
197 val node = DocumentationNode(specialName, doc, Kind.PropertyAccessor)
198
199 node.appendChildren(getValueParameters(), DocumentationReference.Kind.Detail)
200 node.appendType(getReturnType())
201 register(this, node)
202 return node
203 }
204
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400205 fun PropertyDescriptor.build(): DocumentationNode {
206 val node = DocumentationNode(this, Kind.Property)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400207 node.appendChildren(getTypeParameters(), DocumentationReference.Kind.Detail)
Ilya Ryzhenkov5efc1a32014-10-13 00:35:57 +0400208 getExtensionReceiverParameter()?.let { node.appendChild(it, DocumentationReference.Kind.Detail) }
209 node.appendType(getReturnType())
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400210 getGetter()?.let {
211 if (!it.isDefault())
212 node.appendChild(it, DocumentationReference.Kind.Member)
213 }
214 getSetter()?.let {
215 if (!it.isDefault())
216 node.appendChild(it, DocumentationReference.Kind.Member)
217 }
218
219 register(this, node)
220 return node
221 }
222
223 fun ValueParameterDescriptor.build(): DocumentationNode {
224 val node = DocumentationNode(this, Kind.Parameter)
225 node.appendType(getType())
226 return node
227 }
228
229 fun TypeParameterDescriptor.build(): DocumentationNode {
230 val doc = parseDocumentation(this)
231 val name = getName().asString()
232 val prefix = when (getVariance()) {
233 Variance.IN_VARIANCE -> "in "
234 Variance.OUT_VARIANCE -> "out "
235 else -> ""
236 }
237
238 val node = DocumentationNode(prefix + name, doc, DocumentationNode.Kind.TypeParameter)
239
240 val builtIns = KotlinBuiltIns.getInstance()
241 for (constraint in getUpperBounds()) {
242 if (constraint == builtIns.getDefaultBound())
243 continue
244 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.UpperBound)
245 node.append(constraintNode, DocumentationReference.Kind.Detail)
246 }
247
248 for (constraint in getLowerBounds()) {
249 if (builtIns.isNothing(constraint))
250 continue
251 val constraintNode = DocumentationNode(constraint.toString(), Content.Empty, DocumentationNode.Kind.LowerBound)
252 node.append(constraintNode, DocumentationReference.Kind.Detail)
253 }
254 return node
255 }
256
257 fun ReceiverParameterDescriptor.build(): DocumentationNode {
Ilya Ryzhenkovba1f12d2014-10-12 23:25:12 +0400258 val node = DocumentationNode(getName().asString(), Content.Empty, Kind.Receiver)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400259 node.appendType(getType())
260 return node
261 }
262
263 /**
264 * Generates cross-references for documentation such as extensions for a type, inheritors, etc
265 *
266 * $receiver: [DocumentationContext] for node/descriptor resolutions
267 * $node: [DocumentationNode] to visit
268 */
269 public fun resolveReferences(node: DocumentationNode) {
Ilya Ryzhenkov49077362014-10-14 19:53:13 +0400270 if (node.kind != Kind.PropertyAccessor) {
271 node.details(DocumentationNode.Kind.Receiver).forEach { receiver ->
272 val receiverType = receiver.detail(DocumentationNode.Kind.Type)
273 val descriptor = links[receiverType]
274 if (descriptor != null) {
275 val typeNode = descriptorToNode[descriptor]
276 // if typeNode is null, extension is to external type like in a library
277 // should we create dummy node here?
278 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Extension)
279 }
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400280 }
281 }
282 node.details(DocumentationNode.Kind.Supertype).forEach { detail ->
283 val descriptor = links[detail]
284 if (descriptor != null) {
285 val typeNode = descriptorToNode[descriptor]
286 typeNode?.addReferenceTo(node, DocumentationReference.Kind.Inheritor)
287 }
288 }
289 node.details.forEach { detail ->
290 val descriptor = links[detail]
291 if (descriptor != null) {
292 val typeNode = descriptorToNode[descriptor]
293 if (typeNode != null) {
294 detail.addReferenceTo(typeNode, DocumentationReference.Kind.Link)
295 }
296 }
297 }
298
Ilya Ryzhenkov280dc292014-10-14 16:08:10 +0400299 resolveContentLinks(node, node.content)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400300
301 for (child in node.members) {
302 resolveReferences(child)
303 }
304 for (child in node.details) {
305 resolveReferences(child)
306 }
307 }
308
309 fun getResolutionScope(node: DocumentationNode): JetScope {
310 val descriptor = nodeToDescriptor[node] ?: throw IllegalArgumentException("Node is not known to this context")
Ilya Ryzhenkov1cb3af92014-10-13 20:14:45 +0400311 return getResolutionScope(descriptor)
Ilya Ryzhenkov11355ce2014-10-12 22:35:47 +0400312 }
313
314 fun resolveContentLinks(node: DocumentationNode, content: ContentNode) {
315 val snapshot = content.children.toList()
316 for (child in snapshot) {
317 if (child is ContentExternalLink) {
318 val referenceText = child.href
319 if (Name.isValidIdentifier(referenceText)) {
320 val scope = getResolutionScope(node)
321 val symbolName = Name.guess(referenceText)
322 val symbol = scope.getLocalVariable(symbolName) ?:
323 scope.getProperties(symbolName).firstOrNull() ?:
324 scope.getFunctions(symbolName).firstOrNull() ?:
325 scope.getClassifier(symbolName)
326
327 if (symbol != null) {
328 val targetNode = descriptorToNode[symbol]
329 val contentLink = if (targetNode != null) ContentNodeLink(targetNode) else ContentExternalLink("#")
330
331 val index = content.children.indexOf(child)
332 content.children.remove(index)
333 contentLink.children.addAll(child.children)
334 content.children.add(index, contentLink)
335 }
336 }
337 }
338 resolveContentLinks(node, child)
339 }
340 }
341}