diff options
Diffstat (limited to 'core/src/main')
9 files changed, 147 insertions, 58 deletions
diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt index 79585413..69c6fa41 100644 --- a/core/src/main/kotlin/DokkaGenerator.kt +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -1,15 +1,18 @@ package org.jetbrains.dokka import org.jetbrains.dokka.Model.Module +import org.jetbrains.dokka.Utilities.genericPretty import org.jetbrains.dokka.Model.transformers.ActualExpectedMerger import org.jetbrains.dokka.Utilities.pretty import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.MarkdownToContentConverter +import org.jetbrains.dokka.pages.ModulePageNode import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.renderers.FileWriter import org.jetbrains.dokka.renderers.HtmlRenderer import org.jetbrains.dokka.resolvers.DefaultLocationProvider import org.jetbrains.dokka.transformers.DefaultDocumentationToPageTransformer +import org.jetbrains.dokka.transformers.TopDownPageNodeMerger import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -63,7 +66,12 @@ class DokkaGenerator( } } -private fun Iterable<PageNode>.merge(): PageNode = last() // TODO: implement +private fun Iterable<ModulePageNode>.merge(): PageNode { + this.forEach { it.genericPretty().also(::println) } + return TopDownPageNodeMerger().mergeModules(this).also { + it.genericPretty().also(::println) + } +} private class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { override fun clear() { diff --git a/core/src/main/kotlin/Utilities/nodeDebug.kt b/core/src/main/kotlin/Utilities/nodeDebug.kt index c7a771d8..8e2a7079 100644 --- a/core/src/main/kotlin/Utilities/nodeDebug.kt +++ b/core/src/main/kotlin/Utilities/nodeDebug.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.Utilities import org.jetbrains.dokka.Model.DocumentationNode +import org.jetbrains.dokka.pages.* import org.jetbrains.kotlin.descriptors.DeclarationDescriptor const val DOWN = '\u2503' @@ -18,4 +19,33 @@ fun <T : DeclarationDescriptor> DocumentationNode<T>.pretty(prefix: String = "", .takeIf { it.isNotEmpty() } ?.joinToString(prefix = "\n", separator = "") .orEmpty() + if (children.isEmpty()) "\n" else "" -}
\ No newline at end of file +} + +fun Any.genericPretty(prefix: String = "", isLast: Boolean = true): String { + val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' + + return prefix + (if (isLast) LAST else BRANCH) + this.stringify() + + allChildren().dropLast(1) + .map { it.genericPretty(nextPrefix, false) } + .plus(allChildren().lastOrNull()?.genericPretty(nextPrefix)) + .filterNotNull() + .takeIf { it.isNotEmpty() } + ?.joinToString(prefix = "\n", separator = "") + .orEmpty() + if (allChildren().isEmpty()) "\n" else "" +} +private fun Any.stringify() = when(this) { + is ContentNode -> toString() + this.dci + is PageNode -> this.name + this::class.simpleName + else -> toString() +} +private fun Any.allChildren() = when(this){ + is PageNode -> children + content + is ContentBlock -> this.children + is ContentHeader -> this.items + is ContentStyle -> this.items + is ContentSymbol -> this.parts + is ContentComment -> this.parts + is ContentGroup -> this.children + is ContentList -> this.items + else -> emptyList() +} diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt index 2dffbb17..a2b884a8 100644 --- a/core/src/main/kotlin/pages/ContentNodes.kt +++ b/core/src/main/kotlin/pages/ContentNodes.kt @@ -3,80 +3,80 @@ package org.jetbrains.dokka.pages import org.jetbrains.dokka.links.DRI interface ContentNode { - val platforms: List<PlatformData> + val dci: DCI val annotations: List<Annotation> } /** Comment consisting of parts, eg. [ContentText]s, [ContentLink]s and so on */ data class ContentComment(val parts: List<ContentNode>, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Simple text */ data class ContentText(val text: String, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode ///** Headers */ TODO for next iteration data class ContentHeader(val items: List<ContentNode>, val level: Int, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Lists */ data class ContentList(val items: List<ContentNode>, val ordered: Boolean, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Styled elements, eg. bold, strikethrough, emphasis and so on **/ data class ContentStyle(val items: List<ContentNode>, val style: IStyle, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Code blocks */ data class ContentCode(val code: String, val language: String, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Symbols, eg. `open fun foo(): String`, or `class Bar`, consisting of parts like [ContentText] and [ContentLink] */ data class ContentSymbol(val parts: List<ContentNode>, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** All links to classes, packages, etc. that have te be resolved */ data class ContentLink(val text: String, val address: DRI, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** All links that do not need to be resolved */ data class ContentResolvedLink(val text: String, val address: String, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Blocks of [ContentNode]s with name, eg. Functions, Types, Properties, etc. */ data class ContentBlock(val name: String, val children: List<ContentNode>, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode /** Logical grouping of [ContentNode]s, eg. [ContentLink], [ContentText] and [ContentSymbol] for one entity */ data class ContentGroup(val children: List<ContentNode>, - override val platforms: List<PlatformData>, + override val dci: DCI, override val annotations: List<Annotation> = emptyList() ): ContentNode diff --git a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt index 9b249878..022826fa 100644 --- a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt +++ b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt @@ -15,37 +15,37 @@ class MarkdownToContentConverter( ) { fun buildContent( node: MarkdownNode, - platforms: List<PlatformData>, + dci: DCI, documentationNode: DocumentationNode<*> ): List<ContentNode> { // println(tree.toTestString()) fun buildChildren(node: MarkdownNode) = node.children.flatMap { - buildContent(it, platforms, documentationNode) + buildContent(it, dci, documentationNode) }.coalesceText() return when (node.type) { - MarkdownElementTypes.ATX_1 -> listOf(ContentHeader(buildChildren(node), 1, platforms)) - MarkdownElementTypes.ATX_2 -> listOf(ContentHeader(buildChildren(node), 2, platforms)) - MarkdownElementTypes.ATX_3 -> listOf(ContentHeader(buildChildren(node), 3, platforms)) - MarkdownElementTypes.ATX_4 -> listOf(ContentHeader(buildChildren(node), 4, platforms)) - MarkdownElementTypes.ATX_5 -> listOf(ContentHeader(buildChildren(node), 5, platforms)) - MarkdownElementTypes.ATX_6 -> listOf(ContentHeader(buildChildren(node), 6, platforms)) - MarkdownElementTypes.UNORDERED_LIST -> listOf(ContentList(buildChildren(node), false, platforms)) - MarkdownElementTypes.ORDERED_LIST -> listOf(ContentList(buildChildren(node), true, platforms)) + MarkdownElementTypes.ATX_1 -> listOf(ContentHeader(buildChildren(node), 1, dci)) + MarkdownElementTypes.ATX_2 -> listOf(ContentHeader(buildChildren(node), 2, dci)) + MarkdownElementTypes.ATX_3 -> listOf(ContentHeader(buildChildren(node), 3, dci)) + MarkdownElementTypes.ATX_4 -> listOf(ContentHeader(buildChildren(node), 4, dci)) + MarkdownElementTypes.ATX_5 -> listOf(ContentHeader(buildChildren(node), 5, dci)) + MarkdownElementTypes.ATX_6 -> listOf(ContentHeader(buildChildren(node), 6, dci)) + MarkdownElementTypes.UNORDERED_LIST -> listOf(ContentList(buildChildren(node), false, dci)) + MarkdownElementTypes.ORDERED_LIST -> listOf(ContentList(buildChildren(node), true, dci)) MarkdownElementTypes.LIST_ITEM -> TODO() MarkdownElementTypes.EMPH -> listOf( ContentStyle( buildChildren(node), Style.Emphasis, - platforms + dci ) )// TODO MarkdownElementTypes.STRONG -> listOf( ContentStyle( buildChildren(node), Style.Strong, - platforms + dci ) ) // TODO MarkdownElementTypes.CODE_SPAN -> TODO() @@ -59,13 +59,13 @@ class MarkdownToContentConverter( MarkdownElementTypes.CODE_BLOCK, MarkdownElementTypes.CODE_FENCE -> { val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" - listOf(ContentCode(buildChildren(node).toString(), language, platforms)) // TODO + listOf(ContentCode(buildChildren(node).toString(), language, dci)) // TODO } MarkdownElementTypes.PARAGRAPH -> listOf( ContentStyle( buildChildren(node), Style.Paragraph, - platforms + dci ) ) // TODO @@ -105,7 +105,7 @@ class MarkdownToContentConverter( ) } .firstOrNull() - ?.let { ContentLink(destination, DRI.from(it), platforms) } + ?.let { ContentLink(destination, DRI.from(it), dci) } .let(::listOfNotNull) } else { logger.error("Apparently descriptor for $documentationNode was needed in model") @@ -119,7 +119,7 @@ class MarkdownToContentConverter( // if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { // parent.append(ContentText(node.text)) // } - listOf(ContentText(" ", platforms)) + listOf(ContentText(" ", dci)) } MarkdownTokenTypes.EOL -> { // if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || @@ -131,7 +131,7 @@ class MarkdownToContentConverter( } MarkdownTokenTypes.CODE_LINE -> { - listOf(ContentText(node.text, platforms)) // TODO check + listOf(ContentText(node.text, dci)) // TODO check // if (parent is ContentBlockCode) { // parent.append(content) // } else { @@ -155,7 +155,7 @@ class MarkdownToContentConverter( // } // // parent.append(createEntityOrText(node.text)) - listOf(ContentText(node.text, platforms)) // TODO + listOf(ContentText(node.text, dci)) // TODO MarkdownTokenTypes.EMPH -> @@ -163,7 +163,7 @@ class MarkdownToContentConverter( // if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { // parent.append(ContentText(node.text)) // } - listOf(ContentStyle(buildChildren(node), Style.Emphasis, platforms)) // TODO + listOf(ContentStyle(buildChildren(node), Style.Emphasis, dci)) // TODO MarkdownTokenTypes.COLON, MarkdownTokenTypes.SINGLE_QUOTE, @@ -177,13 +177,13 @@ class MarkdownToContentConverter( MarkdownTokenTypes.EXCLAMATION_MARK, MarkdownTokenTypes.BACKTICK, MarkdownTokenTypes.CODE_FENCE_CONTENT -> { - listOf(ContentText(node.text, platforms)) + listOf(ContentText(node.text, dci)) } MarkdownElementTypes.LINK_DEFINITION -> TODO() MarkdownTokenTypes.EMAIL_AUTOLINK -> - listOf(ContentResolvedLink(node.text, "mailto:${node.text}", platforms)) + listOf(ContentResolvedLink(node.text, "mailto:${node.text}", dci)) else -> buildChildren(node) } @@ -197,7 +197,7 @@ class MarkdownToContentConverter( is ContentText -> listOf( ContentText( nodes.joinToString("") { (it as ContentText).text }, - nodes.first().platforms + nodes.first().dci ) ) else -> nodes diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index 3d2b6fb7..34662720 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -57,9 +57,15 @@ class MemberPageNode( documentationNode: DocumentationNode<*>? ): PageNode(name, content, parent, dri, documentationNode) // functions, extension functions, properties -data class PlatformData(val platformName: String, val platformType: Platform) +data class PlatformData(val platformName: String, val platformType: Platform) { + override fun toString() = platformName +} + +data class DCI(val dri: DRI, val platformDataList: List<PlatformData>) { + override fun toString() = "$dri[$platformDataList]" +} -fun PageNode.platforms(): List<PlatformData> = this.content.flatMap { it.platforms }.distinct() // TODO: Override equals??? +fun PageNode.platforms(): List<PlatformData> = this.content.flatMap { it.dci.platformDataList }.distinct() // TODO: Override equals??? fun PageNode.dfs(predicate: (PageNode) -> Boolean): PageNode? = if (predicate(this)) { this } else { this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt index a92273ef..e7c8d074 100644 --- a/core/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt @@ -26,7 +26,7 @@ abstract class DefaultRenderer(val fileWriter: FileWriter, val locationProvider: is ContentSymbol -> buildSymbol(node.parts, pageContext) is ContentCode -> buildCode(node.code) is ContentBlock -> buildBlock(node.name, node.children, pageContext) - is ContentLink -> buildLink(node.text, locationProvider.resolve(node.address, node.platforms, pageContext)) + is ContentLink -> buildLink(node.text, locationProvider.resolve(node.address, node.dci.platformDataList, pageContext)) is ContentGroup -> buildGroup(node.children, pageContext) is ContentHeader -> buildHeader(node.level, node.items, pageContext) is ContentStyle -> node.items.joinToString(separator = "\n") { buildContentNode(it, pageContext) } diff --git a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt index 2fc926ef..ee86613b 100644 --- a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt @@ -16,7 +16,7 @@ class DefaultDocumentationToPageTransformer( private val markdownConverter: MarkdownToContentConverter, private val logger: DokkaLogger ) : DocumentationToPageTransformer { - override fun transform(passConfiguration: DokkaConfiguration.PassConfiguration, module: Module): PageNode { + override fun transform(passConfiguration: DokkaConfiguration.PassConfiguration, module: Module): ModulePageNode { val platformData = passConfiguration.targets.map { PlatformData(it, passConfiguration.analysisPlatform) } return PageBuilder(platformData).pageForModule(module) } @@ -50,14 +50,14 @@ class DefaultDocumentationToPageTransformer( else -> throw IllegalStateException("$m should not be present here") } - private fun contentForModule(m: Module) = content(platformData) { + private fun contentForModule(m: Module) = content(DCI(m.dri, platformData)) { header(1) { text("root") } block("Packages", m.packages) { link(it.name, it.dri) } - text("Index") + text("Index\n") text("Link to allpage here") } - private fun contentForPackage(p: Package) = content(platformData) { + private fun contentForPackage(p: Package) = content(DCI(p.dri, platformData)) { header(1) { text("Package ${p.name}") } block("Types", p.classes) { link(it.name, it.dri) @@ -70,7 +70,7 @@ class DefaultDocumentationToPageTransformer( } } - private fun contentForClass(c: Class) = content(platformData) { + private fun contentForClass(c: Class) = content(DCI(c.dri, platformData)) { header(1) { text(c.name) } c.rawDocstrings.forEach { markdown(it, c) } block("Constructors", c.constructors) { @@ -84,7 +84,7 @@ class DefaultDocumentationToPageTransformer( } } - private fun contentForFunction(f: Function) = content(platformData) { + private fun contentForFunction(f: Function) = content(DCI(f.dri, platformData)) { header(1) { text(f.name) } signature(f) f.rawDocstrings.forEach { markdown(it, f) } @@ -98,29 +98,29 @@ class DefaultDocumentationToPageTransformer( } // TODO: Make some public builder or merge it with page builder, whateva - private inner class ContentBuilder(private val platformData: List<PlatformData>) { + private inner class ContentBuilder(private val dci: DCI) { private val contents = mutableListOf<ContentNode>() fun build() = contents.toList() // should include nodes coalescence inline fun header(level: Int, block: ContentBuilder.() -> Unit) { - contents += ContentHeader(content(block), level, platformData) + contents += ContentHeader(content(block), level, dci) } fun text(text: String) { - contents += ContentText(text, platformData) + contents += ContentText(text, dci) } inline fun symbol(block: ContentBuilder.() -> Unit) { - contents += ContentSymbol(content(block), platformData) + contents += ContentSymbol(content(block), dci) } - inline fun <T> block(name: String, elements: Iterable<T>, block: ContentBuilder.(T) -> Unit) { - contents += ContentBlock(name, content { elements.forEach { block(it) } }, platformData) + inline fun <T: DocumentationNode<*>> block(name: String, elements: Iterable<T>, block: ContentBuilder.(T) -> Unit) { + contents += ContentBlock(name, elements.flatMap { content(dci.copy(dri = it.dri)) { group { block(it) } } }, dci) } inline fun group(block: ContentBuilder.() -> Unit) { - contents += ContentGroup(content(block), platformData) + contents += ContentGroup(content(block), dci) } inline fun <T> list( @@ -142,18 +142,18 @@ class DefaultDocumentationToPageTransformer( } fun link(text: String, address: DRI) { - contents += ContentLink(text, address, platformData) + contents += ContentLink(text, address, dci) } fun markdown(raw: String, node: DocumentationNode<*>) { - contents += markdownConverter.buildContent(parseMarkdown(raw), platformData, node) + contents += markdownConverter.buildContent(parseMarkdown(raw), dci, node) } - private inline fun content(block: ContentBuilder.() -> Unit): List<ContentNode> = content(platformData, block) + private inline fun content(block: ContentBuilder.() -> Unit): List<ContentNode> = content(dci, block) } - private inline fun content(platformData: List<PlatformData>, block: ContentBuilder.() -> Unit): List<ContentNode> = - ContentBuilder(platformData).apply(block).build() + private inline fun content(dci: DCI, block: ContentBuilder.() -> Unit): List<ContentNode> = + ContentBuilder(dci).apply(block).build() // When builder is made public it will be moved as extension method to someplace near Function model private fun ContentBuilder.signature(f: Function) = symbol { diff --git a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt index c7859f73..b37b5439 100644 --- a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt @@ -3,8 +3,9 @@ package org.jetbrains.dokka.transformers import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.Model.DocumentationNode import org.jetbrains.dokka.Model.Module +import org.jetbrains.dokka.pages.ModulePageNode import org.jetbrains.dokka.pages.PageNode interface DocumentationToPageTransformer { - fun transform(passConfiguration: DokkaConfiguration.PassConfiguration, module: Module): PageNode // TODO refactor this... some more? + fun transform(passConfiguration: DokkaConfiguration.PassConfiguration, module: Module): ModulePageNode // TODO refactor this... some more? }
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/TopDownPageNodeMerger.kt b/core/src/main/kotlin/transformers/TopDownPageNodeMerger.kt new file mode 100644 index 00000000..36e543b3 --- /dev/null +++ b/core/src/main/kotlin/transformers/TopDownPageNodeMerger.kt @@ -0,0 +1,44 @@ +package org.jetbrains.dokka.transformers + +import org.jetbrains.dokka.pages.* +import kotlin.reflect.KClass + +class TopDownPageNodeMerger { + fun mergeModules(nodes: Iterable<ModulePageNode>): PageNode { + assert(nodes.all { it::class == nodes.first()::class }) // TODO check + + val merged = ModulePageNode(nodes.first().name, mergeContent(nodes), null, nodes.first().documentationNode) // TODO: merge documentationNodes + merged.appendChildren(mergeChildren(nodes.flatMap { it.children })) + + return merged + } + + private fun mergeChildren(nodes: Iterable<PageNode>): List<PageNode> = + nodes.groupBy { it.dri }.map { (_, list) -> + val merged = when (list.first()) { + is PackagePageNode -> PackagePageNode(list.first().name, mergeContent(list), list.first().parent!!, list.first().dri!!, list.first().documentationNode) // TODO: merge documentationlist + is ClassPageNode -> ClassPageNode(list.first().name, mergeContent(list), list.first().parent!!, list.first().dri!!, list.first().documentationNode) // TODO: merge documentationlist + is MemberPageNode -> MemberPageNode(list.first().name, mergeContent(list), list.first().parent!!, list.first().dri!!, list.first().documentationNode) // TODO: merge documentationNodes + else -> throw IllegalStateException("${list.first()} should not be present here") + } + merged.appendChildren(mergeChildren(list.flatMap { it.children })) + merged + } + + private fun mergeContent(nodes: Iterable<PageNode>): List<ContentNode> = nodes.flatMap { it.content }.groupBy { it.dci.dri }.flatMap { (_, list) -> list.mergeList() } + + + + private fun List<ContentBlock>.mergeContent(): List<ContentNode> = + this.flatMap { it.children }.groupBy { it.dci.dri }.flatMap { (_, list) -> list.mergeList() } + + private fun List<ContentNode>.mergeList(): List<ContentNode> = + this.groupBy { it::class }.flatMap { (_, list) -> + val thisClass = list.first() + when(thisClass) { + is ContentBlock -> listOf(ContentBlock(thisClass.name, (list as List<ContentBlock>).mergeContent(), thisClass.dci, list.flatMap { it.annotations }.distinct())) + else -> list.distinctBy { it.toString().replace("dci=.*,".toRegex(), "")} + } + } + +}
\ No newline at end of file |