diff options
author | Simon Ogorodnik <Simon.Ogorodnik@jetbrains.com> | 2018-01-18 03:45:26 +0300 |
---|---|---|
committer | Simon Ogorodnik <Simon.Ogorodnik@jetbrains.com> | 2018-01-18 03:45:26 +0300 |
commit | 1ffced5e6af4d04597c0a02e138785c25b2f1e5f (patch) | |
tree | 3a9d6bdd063c2f49dfa6a1e6df27dd73181edf5d /core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt | |
parent | bf2945df90b7cbed5b713a396585fc1376fb1a9a (diff) | |
download | dokka-1ffced5e6af4d04597c0a02e138785c25b2f1e5f.tar.gz |
Refactor OutputBuilder to decouple data extraction from structure
Diffstat (limited to 'core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt')
-rw-r--r-- | core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt | 713 |
1 files changed, 444 insertions, 269 deletions
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt index 1116c6c59..9ea5e9e4d 100644 --- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt +++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt @@ -12,19 +12,90 @@ import javax.inject.Inject open class JavaLayoutHtmlFormatOutputBuilder( - val output: Appendable, - val languageService: LanguageService, - val uriProvider: JavaLayoutHtmlUriProvider, - val templateService: JavaLayoutHtmlTemplateService, - val logger: DokkaLogger, - val uri: URI + val output: Appendable, + val languageService: LanguageService, + val uriProvider: JavaLayoutHtmlUriProvider, + val templateService: JavaLayoutHtmlTemplateService, + val logger: DokkaLogger, + val uri: URI ) { val htmlConsumer = output.appendHTML() - val contentToHtmlBuilder = ContentToHtmlBuilder(uriProvider, uri) - open fun <T> FlowContent.summaryNodeGroup(nodes: Iterable<T>, header: String, headerAsRow: Boolean = false, row: TBODY.(T) -> Unit) { + private fun FlowContent.hN( + level: Int, + classes: String? = null, + block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit + ) { + when (level) { + 1 -> h1(classes, block) + 2 -> h2(classes, block) + 3 -> h3(classes, block) + 4 -> h4(classes, block) + 5 -> h5(classes, block) + 6 -> h6(classes, block) + } + } + + fun FlowContent.metaMarkup(content: List<ContentNode>): Unit = content.forEach { metaMarkup(it) } + fun FlowContent.metaMarkup(content: ContentNode) { + when (content) { + is ContentText -> +content.text + is ContentSymbol -> span("symbol") { +content.text } + is ContentKeyword -> span("keyword") { +content.text } + is ContentIdentifier -> span("identifier") { + content.signature?.let { id = it } + +content.text + } + + is ContentHeading -> hN(level = content.level) { metaMarkup(content.children) } + + is ContentEntity -> +content.text + + is ContentStrong -> strong { metaMarkup(content.children) } + is ContentStrikethrough -> del { metaMarkup(content.children) } + is ContentEmphasis -> em { metaMarkup(content.children) } + + is ContentOrderedList -> ol { metaMarkup(content.children) } + is ContentUnorderedList -> ul { metaMarkup(content.children) } + is ContentListItem -> consumer.li { + (content.children.singleOrNull() as? ContentParagraph) + ?.let { paragraph -> metaMarkup(paragraph.children) } + ?: metaMarkup(content.children) + } + + + is ContentCode -> pre { code { metaMarkup(content.children) } } + is ContentBlockSampleCode -> pre { code {} } + is ContentBlockCode -> pre { code {} } + + + is ContentNonBreakingSpace -> +nbsp + is ContentSoftLineBreak, is ContentIndentedSoftLineBreak -> { + } + + is ContentParagraph -> p { metaMarkup(content.children) } + + is ContentNodeLink -> { + val href = content.node?.let { uriProvider.linkTo(it, uri) } ?: "#" + a(href = href) { metaMarkup(content.children) } + } + is ContentExternalLink -> { + a(href = content.href) { metaMarkup(content.children) } + } + + is ContentBlock -> metaMarkup(content.children) + } + } + + + protected open fun <T> FlowContent.summaryNodeGroup( + nodes: Iterable<T>, + header: String, + headerAsRow: Boolean = true, + row: TBODY.(T) -> Unit + ) { if (nodes.none()) return if (!headerAsRow) { h2 { +header } @@ -39,38 +110,31 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - fun FlowContent.metaMarkup(content: ContentNode) = with(contentToHtmlBuilder) { - appendContent(content) - } - - fun FlowContent.metaMarkup(content: List<ContentNode>) = with(contentToHtmlBuilder) { - appendContent(content) - } - open fun TBODY.classLikeRow(node: DocumentationNode) = tr { + protected open fun TBODY.classLikeRow(node: DocumentationNode) = tr { td { a(href = uriProvider.linkTo(node, uri)) { +node.simpleName() } } td { metaMarkup(node.summary) } } - fun FlowContent.modifiers(node: DocumentationNode) { + protected fun FlowContent.modifiers(node: DocumentationNode) { for (modifier in node.details(NodeKind.Modifier)) { renderedSignature(modifier, SUMMARY) } } - fun FlowContent.shortFunctionParametersList(func: DocumentationNode) { + protected fun FlowContent.shortFunctionParametersList(func: DocumentationNode) { val params = func.details(NodeKind.Parameter) - .map { languageService.render(it, FULL) } - .run { - drop(1).fold(listOfNotNull(firstOrNull())) { acc, node -> - acc + ContentText(", ") + node - } + .map { languageService.render(it, FULL) } + .run { + drop(1).fold(listOfNotNull(firstOrNull())) { acc, node -> + acc + ContentText(", ") + node } + } metaMarkup(listOf(ContentText("(")) + params + listOf(ContentText(")"))) } - open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr { + protected open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr { if (node.kind != NodeKind.Constructor) { td { modifiers(node) @@ -85,7 +149,7 @@ open class JavaLayoutHtmlFormatOutputBuilder( renderedSignature(receiver.detail(NodeKind.Type), SUMMARY) +"." } - a(href = uriProvider.linkTo(node, uri)) { +node.name } + a(href = node) { +node.name } shortFunctionParametersList(node) } } @@ -94,7 +158,7 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode) = tr { + protected open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode) = tr { td { modifiers(node) renderedSignature(node.detail(NodeKind.Type), SUMMARY) @@ -102,7 +166,7 @@ open class JavaLayoutHtmlFormatOutputBuilder( td { div { code { - a(href = uriProvider.linkTo(node, uri)) { +node.name } + a(href = node) { +node.name } } } @@ -110,14 +174,14 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr { + protected open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr { td { modifiers(node) } td { div { code { - a(href = uriProvider.linkTo(node, uri)) { +node.name } + a(href = node) { +node.name } } } @@ -125,11 +189,14 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - open fun TBODY.inheritRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, summaryRow: TBODY.(DocumentationNode) -> Unit) = tr { + protected open fun TBODY.inheritRow( + entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, + summaryRow: TBODY.(DocumentationNode) -> Unit + ) = tr { td { val (from, nodes) = entry +"From class " - a(href = uriProvider.linkTo(from.owner!!, uri)) { +from.qualifiedName() } + a(href = from.owner!!) { +from.qualifiedName() } table { tbody { for (node in nodes) { @@ -140,11 +207,14 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - open fun TBODY.extensionRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, summaryRow: TBODY.(DocumentationNode) -> Unit) = tr { + protected open fun TBODY.extensionRow( + entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, + summaryRow: TBODY.(DocumentationNode) -> Unit + ) = tr { td { val (from, nodes) = entry +"From " - a(href = uriProvider.linkTo(from, uri)) { +from.qualifiedName() } + a(href = from) { +from.qualifiedName() } table { tbody { for (node in nodes) { @@ -155,63 +225,53 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - private fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) { - metaMarkup(languageService.render(node, mode)) + protected open fun FlowContent.a(href: DocumentationNode, classes: String? = null, block: A.() -> Unit) { + val hrefText = if (href.kind == NodeKind.ExternalLink) + href.name + else + uriProvider.linkTo(href, uri) + a(href = hrefText, classes = classes, block = block) } - open fun FlowContent.memberDocs(node: DocumentationNode) { - div { - id = node.signatureForAnchor(logger) - h3 { +node.name } - pre { renderedSignature(node, FULL) } - metaMarkup(node.content) - for ((name, sections) in node.content.sections.groupBy { it.tag }) { - table { - thead { tr { td { h3 { +name } } } } - tbody { - sections.forEach { - tr { - td { it.subjectName?.let { +it } } - td { - metaMarkup(it.children) - } - } - } - } - } - } - } + protected open fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) { + metaMarkup(languageService.render(node, mode)) } - fun appendPackage(node: DocumentationNode) = templateService.composePage( - listOf(node), - htmlConsumer, - headContent = { - - }, - bodyContent = { - h1 { +node.name } - metaMarkup(node.content) - summaryNodeGroup(node.members(NodeKind.Class), "Classes") { classLikeRow(it) } - summaryNodeGroup(node.members(NodeKind.Exception), "Exceptions") { classLikeRow(it) } - summaryNodeGroup(node.members(NodeKind.TypeAlias), "Type-aliases") { classLikeRow(it) } - summaryNodeGroup(node.members(NodeKind.AnnotationClass), "Annotations") { classLikeRow(it) } - summaryNodeGroup(node.members(NodeKind.Enum), "Enums") { classLikeRow(it) } - - summaryNodeGroup(node.members(NodeKind.Function), "Top-level functions summary") { functionLikeSummaryRow(it) } - summaryNodeGroup(node.members(NodeKind.Property), "Top-level properties summary") { propertyLikeSummaryRow(it) } - - - fullDocs(node.members(NodeKind.Function), "Top-level functions") { memberDocs(it) } - fullDocs(node.members(NodeKind.Property), "Top-level properties") { memberDocs(it) } - } + protected open fun generatePackage(page: Page.PackagePage) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +page.node.name } + metaMarkup(page.node.content) + summaryNodeGroup(page.classes, "Classes", headerAsRow = false) { classLikeRow(it) } + summaryNodeGroup(page.exceptions, "Exceptions", headerAsRow = false) { classLikeRow(it) } + summaryNodeGroup(page.typeAliases, "Type-aliases", headerAsRow = false) { classLikeRow(it) } + summaryNodeGroup(page.annotations, "Annotations", headerAsRow = false) { classLikeRow(it) } + summaryNodeGroup(page.enums, "Enums", headerAsRow = false) { classLikeRow(it) } + + summaryNodeGroup( + page.functions, + "Top-level functions summary", + headerAsRow = false + ) { functionLikeSummaryRow(it) } + summaryNodeGroup( + page.properties, + "Top-level properties summary", + headerAsRow = false + ) { propertyLikeSummaryRow(it) } + + + fullMemberDocs(page.functions, "Top-level functions") + fullMemberDocs(page.properties, "Top-level properties") + } ) - fun FlowContent.qualifiedTypeReference(node: DocumentationNode) { + protected fun FlowContent.qualifiedTypeReference(node: DocumentationNode) { if (node.kind in classLike) { - a(href = uriProvider.linkTo(node, uri)) { - +node.qualifiedName() - } + a(href = node) { +node.qualifiedName() } return } @@ -222,12 +282,7 @@ open class JavaLayoutHtmlFormatOutputBuilder( return } - val href = if (targetLink.kind == NodeKind.ExternalLink) - targetLink.name - else - uriProvider.linkTo(targetLink, uri) - - a(href = href) { + a(href = targetLink) { +node.qualifiedNameFromType() } val typeParameters = node.details(NodeKind.Type) @@ -243,9 +298,7 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - open fun FlowContent.classHierarchy(node: DocumentationNode) { - - val superclasses = (sequenceOf(node) + node.superclassTypeSequence).toList().asReversed() + protected open fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) { table { superclasses.forEach { tr { @@ -262,7 +315,7 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) { + protected open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) { if (inheritors.isEmpty()) return div { table { @@ -277,13 +330,13 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } tbody { - inheritors.forEach { + inheritors.forEach { inheritor -> tr { td { - a(href = uriProvider.linkTo(it, uri)) { +it.classNodeNameWithOuterClass() } + a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() } } td { - metaMarkup(it.summary) + metaMarkup(inheritor.summary) } } } @@ -292,152 +345,161 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } - fun appendClassLike(node: DocumentationNode) = templateService.composePage( - listOf(node), - htmlConsumer, - headContent = { - - }, - bodyContent = { - h1 { +node.name } - pre { renderedSignature(node, FULL) } - classHierarchy(node) - - val inheritors = generateSequence(node.inheritors) { inheritors -> - inheritors - .flatMap { it.inheritors } - .takeUnless { it.isEmpty() } - } - subclasses(inheritors.first(), true) - subclasses(inheritors.drop(1).flatten().toList(), false) - - - metaMarkup(node.content) - - h2 { +"Summary" } - - val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" } - val hasMeaningfulCompanion = !isCompanion && node.companion != null - - fun DocumentationNode.thisTypeExtension() = detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node } - - val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction - val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty + protected open fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) { + summaryNodeGroup( + nestedClasses, + "Nested classes", + headerAsRow = true + ) { + nestedClassSummaryRow(it) + } - fun DocumentationNode.isFunction() = kind == functionKind - fun DocumentationNode.isProperty() = kind == propertyKind + summaryNodeGroup( + constructors, + "Constructors", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } - val functions = node.members(functionKind) - val properties = node.members(propertyKind) - val inheritedFunctionsByReceiver = node.inheritedMembers(functionKind).groupBy { it.owner!! } - val inheritedPropertiesByReceiver = node.inheritedMembers(propertyKind).groupBy { it.owner!! } + summaryNodeGroup(functions, "Functions", headerAsRow = true) { functionLikeSummaryRow(it) } + summaryNodeGroup( + companionFunctions, + "Companion functions", + headerAsRow = true + ) { + functionLikeSummaryRow(it) + } + summaryNodeGroup( + inheritedFunctionsByReceiver.entries, + "Inherited functions", + headerAsRow = true + ) { + inheritRow(it) { + functionLikeSummaryRow(it) + } + } + summaryNodeGroup( + extensionFunctions.entries, + "Extension functions", + headerAsRow = true + ) { + extensionRow(it) { + functionLikeSummaryRow(it) + } + } + summaryNodeGroup( + inheritedExtensionFunctions.entries, + "Inherited extension functions", + headerAsRow = true + ) { + extensionRow(it) { + functionLikeSummaryRow(it) + } + } - val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions - val (extensions, inheritedExtensions) = originalExtensions.partition { it.thisTypeExtension() } - val extensionFunctions = extensions.filter(DocumentationNode::isFunction).groupBy { it.owner!! } - val extensionProperties = extensions.filter(DocumentationNode::isProperty).groupBy { it.owner!! } - val inheritedExtensionFunctions = inheritedExtensions.filter(DocumentationNode::isFunction).groupBy { it.owner!! } - val inheritedExtensionProperties = inheritedExtensions.filter(DocumentationNode::isProperty).groupBy { it.owner!! } + summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) } + summaryNodeGroup( + companionProperties, + "Companion properties", + headerAsRow = true + ) { + propertyLikeSummaryRow(it) + } - val companionFunctions = node.members(NodeKind.CompanionObjectFunction) - val companionProperties = node.members(NodeKind.CompanionObjectProperty) + summaryNodeGroup( + inheritedPropertiesByReceiver.entries, + "Inherited properties", + headerAsRow = true + ) { + inheritRow(it) { + propertyLikeSummaryRow(it) + } + } + summaryNodeGroup( + extensionProperties.entries, + "Extension properties", + headerAsRow = true + ) { + extensionRow(it) { + propertyLikeSummaryRow(it) + } + } + summaryNodeGroup( + inheritedExtensionProperties.entries, + "Inherited extension properties", + headerAsRow = true + ) { + extensionRow(it) { + propertyLikeSummaryRow(it) + } + } + } - summaryNodeGroup(node.members.filter { it.kind in NodeKind.classLike }, "Nested classes", headerAsRow = true) { nestedClassSummaryRow(it) } + protected open fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) { + fullMemberDocs(constructors, "Constructors") + fullMemberDocs(functions, "Functions") + fullMemberDocs(properties, "Properties") + if (!hasMeaningfulCompanion) { + fullMemberDocs(companionFunctions, "Companion functions") + fullMemberDocs(companionProperties, "Companion properties") + } + } - summaryNodeGroup(node.members(NodeKind.Constructor), "Constructors", headerAsRow = true) { functionLikeSummaryRow(it) } + protected open fun generateClassLike(page: Page.ClassPage) = templateService.composePage( + page, + htmlConsumer, + headContent = { - summaryNodeGroup(functions, "Functions", headerAsRow = true) { functionLikeSummaryRow(it) } - if (!isCompanion) { - summaryNodeGroup(companionFunctions, "Companion functions", headerAsRow = true) { functionLikeSummaryRow(it) } - } - summaryNodeGroup(inheritedFunctionsByReceiver.entries, "Inherited functions", headerAsRow = true) { inheritRow(it) { functionLikeSummaryRow(it) } } - summaryNodeGroup(extensionFunctions.entries, "Extension functions", headerAsRow = true) { extensionRow(it) { functionLikeSummaryRow(it) } } - summaryNodeGroup(inheritedExtensionFunctions.entries, "Inherited extension functions", headerAsRow = true) { extensionRow(it) { functionLikeSummaryRow(it) } } + }, + bodyContent = { + val node = page.node + with(page) { + h1 { +node.name } + pre { renderedSignature(node, FULL) } + classHierarchy(page.superclasses) + subclasses(page.directInheritors, true) + subclasses(page.indirectInheritors, false) - summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) } - if (!isCompanion) { - summaryNodeGroup(companionProperties, "Companion properties", headerAsRow = true) { propertyLikeSummaryRow(it) } - } - summaryNodeGroup(inheritedPropertiesByReceiver.entries, "Inherited properties", headerAsRow = true) { inheritRow(it) { propertyLikeSummaryRow(it) } } - summaryNodeGroup(extensionProperties.entries, "Extension properties", headerAsRow = true) { extensionRow(it) { propertyLikeSummaryRow(it) } } - summaryNodeGroup(inheritedExtensionProperties.entries, "Inherited extension properties", headerAsRow = true) { extensionRow(it) { propertyLikeSummaryRow(it) } } + metaMarkup(node.content) + h2 { +"Summary" } + classLikeSummaries(page) - fullDocs(node.members(NodeKind.Constructor), "Constructors") { memberDocs(it) } - fullDocs(functions, "Functions") { memberDocs(it) } - fullDocs(properties, "Properties") { memberDocs(it) } - if (!isCompanion && !hasMeaningfulCompanion) { - fullDocs(companionFunctions, "Companion functions") { memberDocs(it) } - fullDocs(companionProperties, "Companion properties") { memberDocs(it) } - } + classLikeFullMemberDocs(page) } + } ) - fun generateClassesIndex(allTypesNode: DocumentationNode) = templateService.composePage( - listOf(allTypesNode), - htmlConsumer, - headContent = { - - }, - bodyContent = { - h1 { +"Class Index" } + protected open fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage( + page, + htmlConsumer, + headContent = { - fun DocumentationNode.classWithNestedClasses(): List<DocumentationNode> = - members.filter { it.kind in classLike }.flatMap(DocumentationNode::classWithNestedClasses) + this + }, + bodyContent = { + h1 { +"Class Index" } - val classesByFirstLetter = allTypesNode.members - .filterNot { it.kind == NodeKind.ExternalClass } - .flatMap(DocumentationNode::classWithNestedClasses) - .groupBy { - it.classNodeNameWithOuterClass().first().toString() - } - .entries - .sortedBy { (letter) -> letter } - ul { - classesByFirstLetter.forEach { (letter) -> - li { a(href = "#letter_$letter") { +letter } } - } - } - - classesByFirstLetter.forEach { (letter, nodes) -> - h2 { - id = "letter_$letter" - +letter - } - table { - tbody { - for (node in nodes.sortedBy { it.classNodeNameWithOuterClass() }) { - tr { - td { - a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() } - } - td { - metaMarkup(node.content) - } - } - } - } - } + ul { + page.classesByFirstLetter.forEach { (letter) -> + li { a(href = "#letter_$letter") { +letter } } } } - ) - - fun generatePackageIndex(nodes: List<DocumentationNode>) = templateService.composePage(nodes, - htmlConsumer, - headContent = { - }, - bodyContent = { - h1 { +"Package Index" } + page.classesByFirstLetter.forEach { (letter, classes) -> + h2 { + id = "letter_$letter" + +letter + } table { tbody { - for (node in nodes.sortedBy { it.name }) { + for (node in classes) { tr { td { - a(href = uriProvider.linkTo(node, uri)) { +node.name } + a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() } } td { metaMarkup(node.content) @@ -447,93 +509,206 @@ open class JavaLayoutHtmlFormatOutputBuilder( } } } + } ) - private fun FlowContent.fullDocs( - nodes: List<DocumentationNode>, - header: String, - renderNode: FlowContent.(DocumentationNode) -> Unit + protected open fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage( + page, + htmlConsumer, + headContent = { + + }, + bodyContent = { + h1 { +"Package Index" } + table { + tbody { + for (node in page.packages) { + tr { + td { + a(href = uriProvider.linkTo(node, uri)) { +node.name } + } + td { + metaMarkup(node.content) + } + } + } + } + } + } + ) + + fun generatePage(page: Page) { + when (page) { + is Page.PackageIndex -> generatePackageIndex(page) + is Page.ClassIndex -> generateClassIndex(page) + is Page.ClassPage -> generateClassLike(page) + is Page.PackagePage -> generatePackage(page) + } + } + + protected fun FlowContent.fullMemberDocs( + nodes: List<DocumentationNode>, + header: String ) { if (nodes.none()) return h2 { +header } for (node in nodes) { - renderNode(node) + fullMemberDocs(node) } } -} - -class ContentToHtmlBuilder(val uriProvider: JavaLayoutHtmlUriProvider, val uri: URI) { - fun FlowContent.appendContent(content: List<ContentNode>): Unit = content.forEach { appendContent(it) } - private fun FlowContent.hN(level: Int, classes: String? = null, block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit) { - when (level) { - 1 -> h1(classes, block) - 2 -> h2(classes, block) - 3 -> h3(classes, block) - 4 -> h4(classes, block) - 5 -> h5(classes, block) - 6 -> h6(classes, block) + protected open fun FlowContent.fullMemberDocs(node: DocumentationNode) { + div { + id = node.signatureForAnchor(logger) + h3 { +node.name } + pre { renderedSignature(node, FULL) } + metaMarkup(node.content) + for ((name, sections) in node.content.sections.groupBy { it.tag }) { + table { + thead { tr { td { h3 { +name } } } } + tbody { + sections.forEach { + tr { + td { it.subjectName?.let { +it } } + td { + metaMarkup(it.children) + } + } + } + } + } + } } } - fun FlowContent.appendContent(content: ContentNode) { - when (content) { - is ContentText -> +content.text - is ContentSymbol -> span("symbol") { +content.text } - is ContentKeyword -> span("keyword") { +content.text } - is ContentIdentifier -> span("identifier") { - content.signature?.let { id = it } - +content.text + sealed class Page { + class PackageIndex(packages: List<DocumentationNode>) : Page() { + init { + assert(packages.all { it.kind == NodeKind.Package }) } - is ContentHeading -> hN(level = content.level) { appendContent(content.children) } + val packages = packages.sortedBy { it.name } + } - is ContentEntity -> +content.text + class ClassIndex(allTypesNode: DocumentationNode) : Page() { + init { + assert(allTypesNode.kind == NodeKind.AllTypes) + } - is ContentStrong -> strong { appendContent(content.children) } - is ContentStrikethrough -> del { appendContent(content.children) } - is ContentEmphasis -> em { appendContent(content.children) } + // Wide-collect all nested classes + val classes: List<DocumentationNode> = + generateSequence(listOf(allTypesNode)) { nodes -> + nodes + .flatMap { it.members.filter { it.kind in NodeKind.classLike } } + .takeUnless { it.isEmpty() } + }.drop(1) + .flatten() + .sortedBy { it.classNodeNameWithOuterClass() } + .toList() + + + // Group all classes by it's first letter and sort + val classesByFirstLetter = + classes + .groupBy { + it.classNodeNameWithOuterClass().first().toString() + } + .entries + .sortedBy { (letter) -> letter } + } - is ContentOrderedList -> ol { appendContent(content.children) } - is ContentUnorderedList -> ul { appendContent(content.children) } - is ContentListItem -> consumer.li { - (content.children.singleOrNull() as? ContentParagraph) - ?.let { paragraph -> appendContent(paragraph.children) } - ?: appendContent(content.children) + class ClassPage(val node: DocumentationNode) : Page() { + + init { + assert(node.kind in NodeKind.classLike) } + val superclasses = (sequenceOf(node) + node.superclassTypeSequence).toList().asReversed() - is ContentCode -> pre { code { appendContent(content.children) } } - is ContentBlockSampleCode -> pre { code {} } - is ContentBlockCode -> pre { code {} } + val directInheritors: List<DocumentationNode> + val indirectInheritors: List<DocumentationNode> - is ContentNonBreakingSpace -> +nbsp - is ContentSoftLineBreak, is ContentIndentedSoftLineBreak -> { + init { + // Wide-collect all inheritors + val inheritors = generateSequence(node.inheritors) { inheritors -> + inheritors + .flatMap { it.inheritors } + .takeUnless { it.isEmpty() } + } + directInheritors = inheritors.first() + indirectInheritors = inheritors.drop(1).flatten().toList() } - is ContentParagraph -> p { appendContent(content.children) } + val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" } + val hasMeaningfulCompanion = !isCompanion && node.companion != null - is ContentNodeLink -> { - a(href = content.node?.let { uriProvider.linkTo(it, uri) } - ?: "#unresolved") { appendContent(content.children) } + private fun DocumentationNode.thisTypeExtension() = + detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node } + + val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction + val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty + + private fun DocumentationNode.isFunction() = kind == functionKind + private fun DocumentationNode.isProperty() = kind == propertyKind + + + val nestedClasses = node.members.filter { it.kind in NodeKind.classLike } + val constructors = node.members(NodeKind.Constructor) + val functions = node.members(functionKind) + val properties = node.members(propertyKind) + val inheritedFunctionsByReceiver = node.inheritedMembers(functionKind).groupBy { it.owner!! } + val inheritedPropertiesByReceiver = node.inheritedMembers(propertyKind).groupBy { it.owner!! } + + val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions + + val extensionFunctions: Map<DocumentationNode, List<DocumentationNode>> + val extensionProperties: Map<DocumentationNode, List<DocumentationNode>> + val inheritedExtensionFunctions: Map<DocumentationNode, List<DocumentationNode>> + val inheritedExtensionProperties: Map<DocumentationNode, List<DocumentationNode>> + + init { + val (extensions, inheritedExtensions) = originalExtensions.partition { it.thisTypeExtension() } + extensionFunctions = extensions.filter { it.isFunction() }.groupBy { it.owner!! } + extensionProperties = extensions.filter { it.isProperty() }.groupBy { it.owner!! } + inheritedExtensionFunctions = + inheritedExtensions.filter { it.isFunction() }.groupBy { it.owner!! } + inheritedExtensionProperties = + inheritedExtensions.filter { it.isProperty() }.groupBy { it.owner!! } } - is ContentExternalLink -> { - a(href = content.href) { appendContent(content.children) } + + val companionFunctions = node.members(NodeKind.CompanionObjectFunction).takeUnless { isCompanion }.orEmpty() + val companionProperties = + node.members(NodeKind.CompanionObjectProperty).takeUnless { isCompanion }.orEmpty() + + + } + + class PackagePage(val node: DocumentationNode) : Page() { + + init { + assert(node.kind == NodeKind.Package) } - is ContentBlock -> appendContent(content.children) + val classes = node.members(NodeKind.Class) + val exceptions = node.members(NodeKind.Exception) + val typeAliases = node.members(NodeKind.TypeAlias) + val annotations = node.members(NodeKind.AnnotationClass) + val enums = node.members(NodeKind.Enum) + + val functions = node.members(NodeKind.Function) + val properties = node.members(NodeKind.Property) } } } class JavaLayoutHtmlFormatOutputBuilderFactoryImpl @Inject constructor( - val uriProvider: JavaLayoutHtmlUriProvider, - val languageService: LanguageService, - val templateService: JavaLayoutHtmlTemplateService, - val logger: DokkaLogger + val uriProvider: JavaLayoutHtmlUriProvider, + val languageService: LanguageService, + val templateService: JavaLayoutHtmlTemplateService, + val logger: DokkaLogger ) : JavaLayoutHtmlFormatOutputBuilderFactory { override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder { return createOutputBuilder(output, uriProvider.mainUri(node)) |