• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package org.jetbrains.dokka.Formats
2 
3 import com.google.inject.Inject
4 import org.jetbrains.dokka.*
5 import java.net.URI
6 import com.google.inject.name.Named
7 import org.jetbrains.kotlin.cfg.pseudocode.AllTypes
8 
9 
10 interface DacOutlineFormatService {
11     fun computeOutlineURI(node: DocumentationNode): URI
12     fun format(to: Appendable, node: DocumentationNode)
13 }
14 
15 class DacOutlineFormatter @Inject constructor(
16         uriProvider: JavaLayoutHtmlUriProvider,
17         languageService: LanguageService,
18         @Named("dacRoot") dacRoot: String,
19         @Named("generateClassIndex") generateClassIndex: Boolean,
20         @Named("generatePackageIndex") generatePackageIndex: Boolean
21 ) : JavaLayoutHtmlFormatOutlineFactoryService {
22     val tocOutline = TocOutlineService(uriProvider, languageService, dacRoot, generateClassIndex, generatePackageIndex)
23     val outlines = listOf(tocOutline)
24 
generateOutlinesnull25     override fun generateOutlines(outputProvider: (URI) -> Appendable, nodes: Iterable<DocumentationNode>) {
26         for (node in nodes) {
27             for (outline in outlines) {
28                 val uri = outline.computeOutlineURI(node)
29                 val output = outputProvider(uri)
30                 outline.format(output, node)
31             }
32         }
33     }
34 }
35 
36 /**
37  * Outline service for generating a _toc.yaml file, responsible for pointing to the paths of each
38  * index.html file in the doc tree.
39  */
40 class BookOutlineService(
41         val uriProvider: JavaLayoutHtmlUriProvider,
42         val languageService: LanguageService,
43         val dacRoot: String,
44         val generateClassIndex: Boolean,
45         val generatePackageIndex: Boolean
46 ) : DacOutlineFormatService {
computeOutlineURInull47     override fun computeOutlineURI(node: DocumentationNode): URI = uriProvider.outlineRootUri(node).resolve("_book.yaml")
48 
49     override fun format(to: Appendable, node: DocumentationNode) {
50         appendOutline(to, listOf(node))
51     }
52 
53     var outlineLevel = 0
54 
55     /** Appends formatted outline to [StringBuilder](to) using specified [location] */
appendOutlinenull56     fun appendOutline(to: Appendable, nodes: Iterable<DocumentationNode>) {
57         if (outlineLevel == 0) to.appendln("reference:")
58         for (node in nodes) {
59             appendOutlineHeader(node, to)
60             val subPackages = node.members.filter {
61                 it.kind == NodeKind.Package
62             }
63             if (subPackages.any()) {
64                 val sortedMembers = subPackages.sortedBy { it.name.toLowerCase() }
65                 appendOutlineLevel(to) {
66                     appendOutline(to, sortedMembers)
67                 }
68             }
69 
70         }
71     }
72 
appendOutlineHeadernull73     fun appendOutlineHeader(node: DocumentationNode, to: Appendable) {
74         if (node is DocumentationModule) {
75             to.appendln("- title: Package Index")
76             to.appendln("  path: $dacRoot${uriProvider.outlineRootUri(node).resolve("packages.html")}")
77             to.appendln("  status_text: no-toggle")
78         } else {
79             to.appendln("- title: ${languageService.renderName(node)}")
80             to.appendln("  path: $dacRoot${uriProvider.mainUriOrWarn(node)}")
81             to.appendln("  status_text: no-toggle")
82         }
83     }
84 
appendOutlineLevelnull85     fun appendOutlineLevel(to: Appendable, body: () -> Unit) {
86         outlineLevel++
87         body()
88         outlineLevel--
89     }
90 }
91 
92 /**
93  * Outline service for generating a _toc.yaml file, responsible for pointing to the paths of each
94  * index.html file in the doc tree.
95  */
96 class TocOutlineService(
97         val uriProvider: JavaLayoutHtmlUriProvider,
98         val languageService: LanguageService,
99         val dacRoot: String,
100         val generateClassIndex: Boolean,
101         val generatePackageIndex: Boolean
102 ) : DacOutlineFormatService {
computeOutlineURInull103     override fun computeOutlineURI(node: DocumentationNode): URI = uriProvider.outlineRootUri(node).resolve("_toc.yaml")
104 
105     override fun format(to: Appendable, node: DocumentationNode) {
106         appendOutline(to, listOf(node))
107     }
108 
109     var outlineLevel = 0
110 
111     /** Appends formatted outline to [StringBuilder](to) using specified [location] */
appendOutlinenull112     fun appendOutline(to: Appendable, nodes: Iterable<DocumentationNode>) {
113         if (outlineLevel == 0) to.appendln("toc:")
114         for (node in nodes) {
115             appendOutlineHeader(node, to)
116             val subPackages = node.members.filter {
117                 it.kind == NodeKind.Package
118             }
119             if (subPackages.any()) {
120                 val sortedMembers = subPackages.sortedBy { it.nameWithOuterClass() }
121                 appendOutlineLevel {
122                     appendOutline(to, sortedMembers)
123                 }
124             }
125         }
126     }
127 
appendOutlineHeadernull128     fun appendOutlineHeader(node: DocumentationNode, to: Appendable) {
129         if (node is DocumentationModule) {
130             if (generateClassIndex) {
131                 node.members.filter { it.kind == NodeKind.AllTypes }.firstOrNull()?.let {
132                     to.appendln("- title: Class Index")
133                     to.appendln("  path: $dacRoot${uriProvider.outlineRootUri(it).resolve("classes.html")}")
134                     to.appendln()
135                 }
136             }
137             if (generatePackageIndex) {
138                 to.appendln("- title: Package Index")
139                 to.appendln("  path: $dacRoot${uriProvider.outlineRootUri(node).resolve("packages.html")}")
140                 to.appendln()
141             }
142         } else if (node.kind != NodeKind.AllTypes && !(node is DocumentationModule)) {
143             to.appendln("- title: ${languageService.renderName(node)}")
144             to.appendln("  path: $dacRoot${uriProvider.mainUriOrWarn(node)}")
145             to.appendln()
146             var addedSectionHeader = false
147             for (kind in NodeKind.classLike) {
148                 val members = node.getMembersOfKinds(kind)
149                 if (members.isNotEmpty()) {
150                     if (!addedSectionHeader) {
151                         to.appendln("  section:")
152                         addedSectionHeader = true
153                     }
154                     to.appendln("  - title: ${kind.pluralizedName()}")
155                     to.appendln()
156                     to.appendln("    section:")
157                     members.sortedBy { it.nameWithOuterClass().toLowerCase() }.forEach { member ->
158                         to.appendln("    - title: ${languageService.renderNameWithOuterClass(member)}")
159                         to.appendln("      path: $dacRoot${uriProvider.mainUriOrWarn(member)}".trimEnd('#'))
160                         to.appendln()
161                     }
162                 }
163             }
164             to.appendln().appendln()
165         }
166     }
167 
appendOutlineLevelnull168     fun appendOutlineLevel(body: () -> Unit) {
169         outlineLevel++
170         body()
171         outlineLevel--
172     }
173 }
174 
175 class DacNavOutlineService constructor(
176         val uriProvider: JavaLayoutHtmlUriProvider,
177         val languageService: LanguageService,
178         val dacRoot: String
179 ) : DacOutlineFormatService {
computeOutlineURInull180     override fun computeOutlineURI(node: DocumentationNode): URI =
181             uriProvider.outlineRootUri(node).resolve("navtree_data.js")
182 
183     override fun format(to: Appendable, node: DocumentationNode) {
184         to.append("var NAVTREE_DATA = ").appendNavTree(node.members).append(";")
185     }
186 
appendNavTreenull187     private fun Appendable.appendNavTree(nodes: Iterable<DocumentationNode>): Appendable {
188         append("[ ")
189         var first = true
190         for (node in nodes) {
191             if (!first) append(", ")
192             first = false
193             val interfaces = node.getMembersOfKinds(NodeKind.Interface)
194             val classes = node.getMembersOfKinds(NodeKind.Class)
195             val objects = node.getMembersOfKinds(NodeKind.Object)
196             val annotations = node.getMembersOfKinds(NodeKind.AnnotationClass)
197             val enums = node.getMembersOfKinds(NodeKind.Enum)
198             val exceptions = node.getMembersOfKinds(NodeKind.Exception)
199 
200             append("[ \"${node.name}\", \"$dacRoot${uriProvider.tryGetMainUri(node)}\", [ ")
201             var needComma = false
202             if (interfaces.firstOrNull() != null) {
203                 appendNavTreePagesOfKind("Interfaces", interfaces)
204                 needComma = true
205             }
206             if (classes.firstOrNull() != null) {
207                 if (needComma) append(", ")
208                 appendNavTreePagesOfKind("Classes", classes)
209                 needComma = true
210             }
211             if (objects.firstOrNull() != null) {
212                 if (needComma) append(", ")
213                 appendNavTreePagesOfKind("Objects", objects)
214             }
215             if (annotations.firstOrNull() != null) {
216                 if (needComma) append(", ")
217                 appendNavTreePagesOfKind("Annotations", annotations)
218                 needComma = true
219             }
220             if (enums.firstOrNull() != null) {
221                 if (needComma) append(", ")
222                 appendNavTreePagesOfKind("Enums", enums)
223                 needComma = true
224             }
225             if (exceptions.firstOrNull() != null) {
226                 if (needComma) append(", ")
227                 appendNavTreePagesOfKind("Exceptions", exceptions)
228             }
229             append(" ] ]")
230         }
231         append(" ]")
232         return this
233     }
234 
appendNavTreePagesOfKindnull235     private fun Appendable.appendNavTreePagesOfKind(kindTitle: String,
236                                                     nodesOfKind: Iterable<DocumentationNode>): Appendable {
237         append("[ \"$kindTitle\", null, [ ")
238         var started = false
239         for (node in nodesOfKind) {
240             if (started) append(", ")
241             started = true
242             appendNavTreeChild(node)
243         }
244         append(" ], null, null ]")
245         return this
246     }
247 
appendNavTreeChildnull248     private fun Appendable.appendNavTreeChild(node: DocumentationNode): Appendable {
249         append("[ \"${node.nameWithOuterClass()}\", \"${dacRoot}${uriProvider.tryGetMainUri(node)}\"")
250         append(", null, null, null ]")
251         return this
252     }
253 }
254 
255 class DacSearchOutlineService(
256         val uriProvider: JavaLayoutHtmlUriProvider,
257         val languageService: LanguageService,
258         val dacRoot: String
259 ) : DacOutlineFormatService {
260 
computeOutlineURInull261     override fun computeOutlineURI(node: DocumentationNode): URI =
262             uriProvider.outlineRootUri(node).resolve("lists.js")
263 
264     override fun format(to: Appendable, node: DocumentationNode) {
265         val pageNodes = node.getAllPageNodes()
266         var id = 0
267         to.append("var KTX_CORE_DATA = [\n")
268         var first = true
269         for (pageNode in pageNodes) {
270             if (pageNode.kind == NodeKind.Module) continue
271             if (!first) to.append(", \n")
272             first = false
273             to.append(" { " +
274                     "id:$id, " +
275                     "label:\"${pageNode.qualifiedName()}\", " +
276                     "link:\"${dacRoot}${uriProvider.tryGetMainUri(pageNode)}\", " +
277                     "type:\"${pageNode.getClassOrPackage()}\", " +
278                     "deprecated:\"false\" }")
279             id++
280         }
281         to.append("\n];")
282     }
283 
getClassOrPackagenull284     private fun DocumentationNode.getClassOrPackage(): String =
285             if (hasOwnPage())
286                 "class"
287             else if (isPackage()) {
288                 "package"
289             } else {
290                 ""
291             }
292 
DocumentationNodenull293     private fun DocumentationNode.getAllPageNodes(): Iterable<DocumentationNode> {
294         val allPageNodes = mutableListOf<DocumentationNode>()
295         recursiveSetAllPageNodes(allPageNodes)
296         return allPageNodes
297     }
298 
recursiveSetAllPageNodesnull299     private fun DocumentationNode.recursiveSetAllPageNodes(
300             allPageNodes: MutableList<DocumentationNode>) {
301         for (child in members) {
302             if (child.hasOwnPage() || child.isPackage()) {
303                 allPageNodes.add(child)
304                 child.qualifiedName()
305                 child.recursiveSetAllPageNodes(allPageNodes)
306             }
307         }
308     }
309 
310 }
311 
312 /**
313  * Return all children of the node who are one of the selected `NodeKind`s. It recursively fetches
314  * all offspring, not just immediate children.
315  */
getMembersOfKindsnull316 fun DocumentationNode.getMembersOfKinds(vararg kinds: NodeKind): MutableList<DocumentationNode> {
317     val membersOfKind = mutableListOf<DocumentationNode>()
318     recursiveSetMembersOfKinds(kinds, membersOfKind)
319     return membersOfKind
320 }
321 
DocumentationNodenull322 private fun DocumentationNode.recursiveSetMembersOfKinds(kinds: Array<out NodeKind>,
323                                                          membersOfKind: MutableList<DocumentationNode>) {
324     for (member in members) {
325         if (member.kind in kinds) {
326             membersOfKind.add(member)
327         }
328         member.recursiveSetMembersOfKinds(kinds, membersOfKind)
329     }
330 }
331 
332 /**
333  * Returns whether or not this node owns a page. The criteria for whether a node owns a page is
334  * similar to the way javadoc is structured. Classes, Interfaces, Enums, AnnotationClasses,
335  * Exceptions, and Objects (Kotlin-specific) meet the criteria.
336  */
hasOwnPagenull337 fun DocumentationNode.hasOwnPage() =
338         kind == NodeKind.Class || kind == NodeKind.Interface || kind == NodeKind.Enum ||
339                 kind == NodeKind.AnnotationClass || kind == NodeKind.Exception ||
340                 kind == NodeKind.Object
341 
342 /**
343  * In most cases, this returns the short name of the `Type`. When the Type is an inner Type, it
344  * prepends the name with the containing Type name(s).
345  *
346  * For example, if you have a class named OuterClass and an inner class named InnerClass, this would
347  * return OuterClass.InnerClass.
348  *
349  */
350 fun DocumentationNode.nameWithOuterClass(): String {
351     val nameBuilder = StringBuilder(name)
352     var parent = owner
353     if (hasOwnPage()) {
354         while (parent != null && parent.hasOwnPage()) {
355             nameBuilder.insert(0, "${parent.name}.")
356             parent = parent.owner
357         }
358     }
359     return nameBuilder.toString()
360 }
361 
362 /**
363  * Return whether the node is a package.
364  */
isPackagenull365 fun DocumentationNode.isPackage(): Boolean {
366     return kind == NodeKind.Package
367 }
368 
369 /**
370  * Return the 'page owner' of this node. `DocumentationNode.hasOwnPage()` defines the criteria for
371  * a page owner. If this node is not a page owner, then it iterates up through its ancestors to
372  * find the first page owner.
373  */
pageOwnernull374 fun DocumentationNode.pageOwner(): DocumentationNode {
375     if (hasOwnPage() || owner == null) {
376         return this
377     } else {
378         var parent: DocumentationNode = owner!!
379         while (!parent.hasOwnPage() && !parent.isPackage()) {
380             parent = parent.owner!!
381         }
382         return parent
383     }
384 }
385 
NodeKindnull386 fun NodeKind.pluralizedName() = when(this) {
387     NodeKind.Class -> "Classes"
388     NodeKind.Interface -> "Interfaces"
389     NodeKind.AnnotationClass -> "Annotations"
390     NodeKind.Enum -> "Enums"
391     NodeKind.Exception -> "Exceptions"
392     NodeKind.Object -> "Objects"
393     NodeKind.TypeAlias -> "TypeAliases"
394     else -> "${name}s"
395 }