From 0a18e1c31f6a8dcec4b3e95f8795a53dd5df9fcc Mon Sep 17 00:00:00 2001 From: Kamil Doległo Date: Sun, 3 Nov 2019 23:53:44 +0100 Subject: [WIP] intergraph transformer --- core/src/main/kotlin/DokkaGenerator.kt | 63 ++++--- .../kotlin/pages/MarkdownToContentConverter.kt | 193 +++++++++++++++++++++ core/src/main/kotlin/pages/NewContentBuilder.kt | 193 --------------------- core/src/main/kotlin/pages/PageNodes.kt | 39 ++++- core/src/main/kotlin/renderers/DefaultRenderer.kt | 30 ++-- core/src/main/kotlin/renderers/HtmlRenderer.kt | 19 +- .../kotlin/resolvers/DefaultLocationProvider.kt | 26 +-- core/src/main/kotlin/resolvers/LocationProvider.kt | 4 +- .../DefaultDocumentationToPageTransformer.kt | 141 ++++++++++++--- .../transformers/DocumentationToPageTransformer.kt | 3 +- .../kotlin/markdownParser/MarkdownParserTest.kt | 4 +- 11 files changed, 425 insertions(+), 290 deletions(-) create mode 100644 core/src/main/kotlin/pages/MarkdownToContentConverter.kt delete mode 100644 core/src/main/kotlin/pages/NewContentBuilder.kt (limited to 'core') diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt index f1aa7b71..5dd56624 100644 --- a/core/src/main/kotlin/DokkaGenerator.kt +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -1,9 +1,12 @@ package org.jetbrains.dokka -import kotlinx.html.DD import org.jetbrains.dokka.Model.Module import org.jetbrains.dokka.Utilities.pretty import org.jetbrains.dokka.links.DRI +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.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -15,35 +18,41 @@ class DokkaGenerator( private val configuration: DokkaConfiguration, private val logger: DokkaLogger ) { - fun generate() = configuration.passesConfigurations.forEach { pass -> - AnalysisEnvironment(DokkaMessageCollector(logger), pass.analysisPlatform).run { - if (analysisPlatform == Platform.jvm) { - addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) - } - for (element in pass.classpath) { - addClasspath(File(element)) - } - - addSources(pass.sourceRoots.map { it.path }) - - loadLanguageVersionSettings(pass.languageVersion, pass.apiVersion) - - val environment = createCoreEnvironment() - val (facade, _) = createResolutionFacade(environment) - - environment.getSourceFiles().asSequence() - .map { it.packageFqName } - .distinct() - .mapNotNull { facade.resolveSession.getPackageFragment(it) } - .map { DokkaDescriptorVisitor.visitPackageFragmentDescriptor(it, DRI.topLevel) } - .toList() - .let { Module(it) } - }.also { println("${pass.analysisPlatform}:\n${it.pretty()}\n\n") } - } + fun generate(): Unit { + configuration.passesConfigurations.map { pass -> + AnalysisEnvironment(DokkaMessageCollector(logger), pass.analysisPlatform).run { + if (analysisPlatform == Platform.jvm) { + addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) + } + for (element in pass.classpath) { + addClasspath(File(element)) + } + addSources(pass.sourceRoots.map { it.path }) -} + loadLanguageVersionSettings(pass.languageVersion, pass.apiVersion) + + val environment = createCoreEnvironment() + val (facade, _) = createResolutionFacade(environment) + environment.getSourceFiles().asSequence() + .map { it.packageFqName } + .distinct() + .mapNotNull { facade.resolveSession.getPackageFragment(it) } + .map { DokkaDescriptorVisitor.visitPackageFragmentDescriptor(it, DRI.topLevel) } + .toList() + .let { Pair(pass, Module(it)) } + }.also { println("${pass.analysisPlatform}:\n${it.second.pretty()}\n\n") } + }.let { + DefaultDocumentationToPageTransformer().transform(it) + }.also { + HtmlRenderer( + FileWriter(configuration.outputDir, ""), + DefaultLocationProvider(it, configuration, ".${configuration.format}") + ).render(it) + } + } +} private class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { override fun clear() { seenErrors = false diff --git a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt new file mode 100644 index 00000000..d541dc91 --- /dev/null +++ b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt @@ -0,0 +1,193 @@ +package org.jetbrains.dokka.pages + +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.jetbrains.dokka.MarkdownNode + +class MarkdownToContentConverter { + fun buildContent(node: MarkdownNode, platforms: List): List { +// println(tree.toTestString()) + + fun buildChildren(node: MarkdownNode) = node.children.flatMap { buildContent(it, platforms) }.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.LIST_ITEM -> TODO() + MarkdownElementTypes.EMPH -> listOf( + ContentStyle( + buildChildren(node), + Style.Emphasis, + platforms + ) + )// TODO + MarkdownElementTypes.STRONG -> listOf( + ContentStyle( + buildChildren(node), + Style.Strong, + platforms + ) + ) // TODO + MarkdownElementTypes.CODE_SPAN -> TODO() +// val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text +// if (startDelimiter != null) { +// val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter) +// val codeSpan = ContentCode().apply { append(ContentText(text)) } +// parent.append(codeSpan) +// } + + MarkdownElementTypes.CODE_BLOCK, + MarkdownElementTypes.CODE_FENCE -> { + val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" + listOf(ContentCode(buildChildren(node).toString(), language, platforms)) // TODO + } + MarkdownElementTypes.PARAGRAPH -> listOf(ContentStyle(buildChildren(node), Style.Paragraph, platforms)) // TODO + + MarkdownElementTypes.INLINE_LINK -> { +// val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) +// val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) +// if (linkTextNode != null) { +// if (destination != null) { +// val link = ContentExternalLink(destination.text) +// renderLinkTextTo(linkTextNode, link, linkResolver) +// parent.append(link) +// } else { +// val link = ContentExternalLink(linkTextNode.getLabelText()) +// renderLinkTextTo(linkTextNode, link, linkResolver) +// parent.append(link) +// } +// } + //TODO: Linking!!! +// ContentLink() + TODO() + } + MarkdownElementTypes.SHORT_REFERENCE_LINK, + MarkdownElementTypes.FULL_REFERENCE_LINK -> { +// val labelElement = node.child(MarkdownElementTypes.LINK_LABEL) +// if (labelElement != null) { +// val linkInfo = linkResolver.getLinkInfo(labelElement.text) +// val labelText = labelElement.getLabelText() +// val link = +// linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve( +// labelText +// ) +// val linkText = node.child(MarkdownElementTypes.LINK_TEXT) +// if (linkText != null) { +// renderLinkTextTo(linkText, link, linkResolver) +// } else { +// link.append(ContentText(labelText)) +// } +// parent.append(link) +// } + TODO() + } + MarkdownTokenTypes.WHITE_SPACE -> { + // Don't append first space if start of header (it is added during formatting later) + // v + // #### Some Heading +// if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { +// parent.append(ContentText(node.text)) +// } + listOf(ContentText(" ", platforms)) + } + MarkdownTokenTypes.EOL -> { +// if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || +// // Keep extra blank lines when processing lists (affects Markdown formatting) +// (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { +// parent.append(ContentText(node.text)) +// } + emptyList() + } + + MarkdownTokenTypes.CODE_LINE -> { + listOf(ContentText(node.text, platforms)) // TODO check +// if (parent is ContentBlockCode) { +// parent.append(content) +// } else { +// parent.append(ContentBlockCode().apply { append(content) }) +// } + } + + MarkdownTokenTypes.TEXT -> +// fun createEntityOrText(text: String): ContentNode { +// if (text == "&" || text == """ || text == "<" || text == ">") { +// return ContentEntity(text) +// } +// if (text == "&") { +// return ContentEntity("&") +// } +// val decodedText = EntityConverter.replaceEntities(text, true, true) +// if (decodedText != text) { +// return ContentEntity(text) +// } +// return ContentText(text) +// } +// +// parent.append(createEntityOrText(node.text)) + listOf(ContentText(node.text, platforms)) // TODO + + + MarkdownTokenTypes.EMPH -> +// val parentNodeType = node.parent?.type +// if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { +// parent.append(ContentText(node.text)) +// } + listOf(ContentStyle(buildChildren(node), Style.Emphasis, platforms)) // TODO + + MarkdownTokenTypes.COLON, + MarkdownTokenTypes.SINGLE_QUOTE, + MarkdownTokenTypes.DOUBLE_QUOTE, + MarkdownTokenTypes.LT, + MarkdownTokenTypes.GT, + MarkdownTokenTypes.LPAREN, + MarkdownTokenTypes.RPAREN, + MarkdownTokenTypes.LBRACKET, + MarkdownTokenTypes.RBRACKET, + MarkdownTokenTypes.EXCLAMATION_MARK, + MarkdownTokenTypes.BACKTICK, + MarkdownTokenTypes.CODE_FENCE_CONTENT -> { + listOf(ContentText(node.text, platforms)) + } + + MarkdownElementTypes.LINK_DEFINITION -> TODO() + + MarkdownTokenTypes.EMAIL_AUTOLINK -> + listOf(ContentResolvedLink(node.text, "mailto:${node.text}", platforms)) + + else -> buildChildren(node) + } + } + + private fun Collection.coalesceText() = + this + .sliceWhen { prev, next -> prev::class != next::class } + .flatMap { nodes -> + when (nodes.first()) { + is ContentText -> listOf(ContentText(nodes.joinToString("") { (it as ContentText).text }, nodes.first().platforms)) + else -> nodes + } + } +} + +fun Collection.sliceWhen(predicate: (before: T, after: T)->Boolean): Collection> { + val newCollection = mutableListOf>() + var currentSlice = mutableListOf() + for ((prev, next) in this.windowed(2, 1, false)) { + currentSlice.add(prev) + if(predicate(prev, next)) { + newCollection.add(currentSlice) + currentSlice = mutableListOf() + } + } + if(this.isNotEmpty()) { + currentSlice.add(this.last()) + newCollection.add(currentSlice) + } + return newCollection +} \ No newline at end of file diff --git a/core/src/main/kotlin/pages/NewContentBuilder.kt b/core/src/main/kotlin/pages/NewContentBuilder.kt deleted file mode 100644 index 89c509ea..00000000 --- a/core/src/main/kotlin/pages/NewContentBuilder.kt +++ /dev/null @@ -1,193 +0,0 @@ -package org.jetbrains.dokka.pages - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.jetbrains.dokka.MarkdownNode - -class NewContentBuilder { - fun buildContent(node: MarkdownNode, platforms: List): List { -// println(tree.toTestString()) - - fun buildChildren(node: MarkdownNode) = node.children.flatMap { buildContent(it, platforms) }.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.LIST_ITEM -> TODO() - MarkdownElementTypes.EMPH -> listOf( - ContentStyle( - buildChildren(node), - Style.Emphasis, - platforms - ) - )// TODO - MarkdownElementTypes.STRONG -> listOf( - ContentStyle( - buildChildren(node), - Style.Strong, - platforms - ) - ) // TODO - MarkdownElementTypes.CODE_SPAN -> TODO() -// val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text -// if (startDelimiter != null) { -// val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter) -// val codeSpan = ContentCode().apply { append(ContentText(text)) } -// parent.append(codeSpan) -// } - - MarkdownElementTypes.CODE_BLOCK, - MarkdownElementTypes.CODE_FENCE -> { - val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" - listOf(ContentCode(buildChildren(node).toString(), language, platforms)) // TODO - } - MarkdownElementTypes.PARAGRAPH -> listOf(ContentStyle(buildChildren(node), Style.Paragraph, platforms)) // TODO - - MarkdownElementTypes.INLINE_LINK -> { -// val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) -// val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) -// if (linkTextNode != null) { -// if (destination != null) { -// val link = ContentExternalLink(destination.text) -// renderLinkTextTo(linkTextNode, link, linkResolver) -// parent.append(link) -// } else { -// val link = ContentExternalLink(linkTextNode.getLabelText()) -// renderLinkTextTo(linkTextNode, link, linkResolver) -// parent.append(link) -// } -// } - //TODO: Linking!!! -// ContentLink() - TODO() - } - MarkdownElementTypes.SHORT_REFERENCE_LINK, - MarkdownElementTypes.FULL_REFERENCE_LINK -> { -// val labelElement = node.child(MarkdownElementTypes.LINK_LABEL) -// if (labelElement != null) { -// val linkInfo = linkResolver.getLinkInfo(labelElement.text) -// val labelText = labelElement.getLabelText() -// val link = -// linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve( -// labelText -// ) -// val linkText = node.child(MarkdownElementTypes.LINK_TEXT) -// if (linkText != null) { -// renderLinkTextTo(linkText, link, linkResolver) -// } else { -// link.append(ContentText(labelText)) -// } -// parent.append(link) -// } - TODO() - } - MarkdownTokenTypes.WHITE_SPACE -> { - // Don't append first space if start of header (it is added during formatting later) - // v - // #### Some Heading -// if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { -// parent.append(ContentText(node.text)) -// } - listOf(ContentText(" ", platforms)) - } - MarkdownTokenTypes.EOL -> { -// if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || -// // Keep extra blank lines when processing lists (affects Markdown formatting) -// (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { -// parent.append(ContentText(node.text)) -// } - emptyList() - } - - MarkdownTokenTypes.CODE_LINE -> { - listOf(ContentText(node.text, platforms)) // TODO check -// if (parent is ContentBlockCode) { -// parent.append(content) -// } else { -// parent.append(ContentBlockCode().apply { append(content) }) -// } - } - - MarkdownTokenTypes.TEXT -> -// fun createEntityOrText(text: String): ContentNode { -// if (text == "&" || text == """ || text == "<" || text == ">") { -// return ContentEntity(text) -// } -// if (text == "&") { -// return ContentEntity("&") -// } -// val decodedText = EntityConverter.replaceEntities(text, true, true) -// if (decodedText != text) { -// return ContentEntity(text) -// } -// return ContentText(text) -// } -// -// parent.append(createEntityOrText(node.text)) - listOf(ContentText(node.text, platforms)) // TODO - - - MarkdownTokenTypes.EMPH -> -// val parentNodeType = node.parent?.type -// if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { -// parent.append(ContentText(node.text)) -// } - listOf(ContentStyle(buildChildren(node), Style.Emphasis, platforms)) // TODO - - MarkdownTokenTypes.COLON, - MarkdownTokenTypes.SINGLE_QUOTE, - MarkdownTokenTypes.DOUBLE_QUOTE, - MarkdownTokenTypes.LT, - MarkdownTokenTypes.GT, - MarkdownTokenTypes.LPAREN, - MarkdownTokenTypes.RPAREN, - MarkdownTokenTypes.LBRACKET, - MarkdownTokenTypes.RBRACKET, - MarkdownTokenTypes.EXCLAMATION_MARK, - MarkdownTokenTypes.BACKTICK, - MarkdownTokenTypes.CODE_FENCE_CONTENT -> { - listOf(ContentText(node.text, platforms)) - } - - MarkdownElementTypes.LINK_DEFINITION -> TODO() - - MarkdownTokenTypes.EMAIL_AUTOLINK -> - listOf(ContentResolvedLink(node.text, "mailto:${node.text}", platforms)) - - else -> buildChildren(node) - } - } - - private fun Collection.coalesceText() = - this - .sliceWhen { prev, next -> prev::class != next::class } - .flatMap { nodes -> - when (nodes.first()) { - is ContentText -> listOf(ContentText(nodes.joinToString("") { (it as ContentText).text }, nodes.first().platforms)) - else -> nodes - } - } -} - -fun Collection.sliceWhen(predicate: (before: T, after: T)->Boolean): Collection> { - val newCollection = mutableListOf>() - var currentSlice = mutableListOf() - for ((prev, next) in this.windowed(2, 1, false)) { - currentSlice.add(prev) - if(predicate(prev, next)) { - newCollection.add(currentSlice) - currentSlice = mutableListOf() - } - } - if(this.isNotEmpty()) { - currentSlice.add(this.last()) - newCollection.add(currentSlice) - } - return newCollection -} \ No newline at end of file diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index 1bd7f02f..46109d87 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.pages +import org.jetbrains.dokka.Model.DocumentationNode import org.jetbrains.dokka.Platform import org.jetbrains.dokka.links.DRI @@ -7,8 +8,8 @@ abstract class PageNode( val name: String, val content: List, val parent: PageNode?, - val dri: DRI? -// val declarationNode: DeclarationNode + val dri: DRI?, + val documentationNode: DocumentationNode<*>? ) { val children: List get() = _children @@ -17,14 +18,38 @@ abstract class PageNode( fun appendChildren(children: List) = _children.addAll(children) fun appendChild(child: PageNode) = _children.add(child) - } -class ModulePageNode(name: String, content: List, parent: PageNode?): PageNode(name, content, parent, null) -class PackagePageNode(name: String, content: List, parent: PageNode, dri: DRI): PageNode(name, content, parent, dri) -class ClassPageNode(name: String, content: List, parent: PageNode, dri: DRI): PageNode(name, content, parent, dri) // class, companion object -class MemberPageNode(name: String, content: List, parent: PageNode, dri: DRI): PageNode(name, content, parent, dri) // functions, extension functions, properties +class ModulePageNode( + name: String, + content: List, + parent: PageNode? = null, + documentationNode: DocumentationNode<*>? +): PageNode(name, content, parent, null, documentationNode) + +class PackagePageNode( + name: String, + content: List, + parent: PageNode, + dri: DRI, + documentationNode: DocumentationNode<*>? +): PageNode(name, content, parent, dri, documentationNode) + +class ClassPageNode( + name: String, + content: List, + parent: PageNode, + dri: DRI, + documentationNode: DocumentationNode<*>? +): PageNode(name, content, parent, dri, documentationNode) // class, companion object +class MemberPageNode( + name: String, + content: List, + parent: PageNode, + dri: DRI, + documentationNode: DocumentationNode<*>? +): PageNode(name, content, parent, dri, documentationNode) // functions, extension functions, properties data class PlatformData(val platformName: String, val platformType: Platform) diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt index 7dab7c5c..5c6b3751 100644 --- a/core/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt @@ -3,7 +3,7 @@ package org.jetbrains.dokka.renderers import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.resolvers.LocationProvider -abstract class DefaultRenderer(val outputDir: String, val fileWriter: FileWriter, val locationProvider: LocationProvider): Renderer { +abstract class DefaultRenderer(val fileWriter: FileWriter, val locationProvider: LocationProvider): Renderer { protected abstract fun buildHeader(level: Int, text: String): String protected abstract fun buildNewLine(): String @@ -11,29 +11,31 @@ abstract class DefaultRenderer(val outputDir: String, val fileWriter: FileWriter protected abstract fun buildCode(code: String): String protected abstract fun buildNavigation(): String // TODO protected open fun buildText(text: String): String = text - protected open fun buildGroup(children: List): String = children.joinToString { it.build() } - protected open fun buildComment(parts: List): String = parts.joinToString { it.build() } - protected open fun buildSymbol(parts: List): String = parts.joinToString { it.build() } - protected open fun buildBlock(name: String, content: List) = buildHeader(2, name) + content.joinToString("\n") { it.build() } + protected open fun buildHeader(level: Int, content: List, pageContext: PageNode): String = buildHeader(level, content.joinToString { it.build(pageContext) }) + protected open fun buildGroup(children: List, pageContext: PageNode): String = children.joinToString { it.build(pageContext) } + protected open fun buildComment(parts: List, pageContext: PageNode): String = parts.joinToString { it.build(pageContext) } + protected open fun buildSymbol(parts: List, pageContext: PageNode): String = parts.joinToString { it.build(pageContext) } + protected open fun buildBlock(name: String, content: List, pageContext: PageNode) = buildHeader(3, name) + content.joinToString("\n") { it.build(pageContext) } - protected open fun ContentNode.build(): String = buildContentNode(this) + protected open fun ContentNode.build(pageContext: PageNode): String = buildContentNode(this, pageContext) - protected open fun buildContentNode(node: ContentNode) = + protected open fun buildContentNode(node: ContentNode, pageContext: PageNode) = when(node) { is ContentText -> buildText(node.text) - is ContentComment -> buildComment(node.parts) - is ContentSymbol -> buildSymbol(node.parts) + is ContentComment -> buildComment(node.parts, pageContext) + is ContentSymbol -> buildSymbol(node.parts, pageContext) is ContentCode -> buildCode(node.code) - is ContentBlock -> buildBlock(node.name, node.children) - is ContentLink -> buildLink(node.text, locationProvider.resolve(node.address, node.platforms)) - is ContentGroup -> buildGroup(node.children) + is ContentBlock -> buildBlock(node.name, node.children, pageContext) + is ContentLink -> buildLink(node.text, locationProvider.resolve(node.address, node.platforms, pageContext)) + is ContentGroup -> buildGroup(node.children, pageContext) + is ContentHeader -> buildHeader(node.level, node.items, pageContext) else -> "" } protected open fun buildPageContent(page: PageNode): String = - buildHeader(1, page.name) + page.content.joinToString("\n") { it.build() } + /*buildHeader(1, page.name) + */ page.content.joinToString("\n") { it.build(page) } - protected open fun renderPage(page: PageNode) = fileWriter.write(locationProvider.resolve(page), buildPageContent(page)) + protected open fun renderPage(page: PageNode) = fileWriter.write(locationProvider.resolve(page), buildPageContent(page), "") protected open fun renderPages(root: PageNode) { renderPage(root) diff --git a/core/src/main/kotlin/renderers/HtmlRenderer.kt b/core/src/main/kotlin/renderers/HtmlRenderer.kt index 3a6e1576..b6d56fdc 100644 --- a/core/src/main/kotlin/renderers/HtmlRenderer.kt +++ b/core/src/main/kotlin/renderers/HtmlRenderer.kt @@ -5,11 +5,11 @@ import org.jetbrains.dokka.pages.ContentNode import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.resolvers.LocationProvider -open class HtmlRenderer(outputDir: String, fileWriter: FileWriter, locationProvider: LocationProvider): DefaultRenderer(outputDir, fileWriter, locationProvider) { +open class HtmlRenderer(fileWriter: FileWriter, locationProvider: LocationProvider): DefaultRenderer(fileWriter, locationProvider) { - override fun buildComment(parts: List): String = "

${super.buildComment(parts)}

" + override fun buildComment(parts: List, pageContext: PageNode): String = "

${super.buildComment(parts, pageContext)}

" - override fun buildSymbol(parts: List): String = "${super.buildSymbol(parts)}" + override fun buildSymbol(parts: List, pageContext: PageNode): String = "${super.buildSymbol(parts, pageContext)}" override fun buildHeader(level: Int, text: String): String = "$text\n" @@ -21,17 +21,16 @@ open class HtmlRenderer(outputDir: String, fileWriter: FileWriter, locationProvi override fun buildNavigation(): String = "" // TODO implement - override fun buildGroup(children: List): String = "\n" + - "" + children.find { it is ContentLink }?.build() + "\n" + - "" + children.filterNot { it is ContentLink }.joinToString("\n") { it.build() } + "\n" + - "\n" + override fun buildGroup(children: List, pageContext: PageNode): String = + children.find { it is ContentLink }?.build(pageContext) + "\n" + + "" + children.filterNot { it is ContentLink }.joinToString("\n") { it.build(pageContext) } - override fun buildBlock(name: String, content: List): String = - buildHeader(2, name) + "\n" + content.joinToString("\n") { it.build() } + "
" + override fun buildBlock(name: String, content: List, pageContext: PageNode): String = + buildHeader(3, name) + "\n\n\n\n\n\n
\n" + content.joinToString("
") { it.build(pageContext) } + "
" override fun renderPage(page: PageNode) { val pageText = buildStartHtml(page) + buildPageContent(page) + buildEndHtml() - fileWriter.write(locationProvider.resolve(page), pageText) + fileWriter.write(locationProvider.resolve(page), pageText, "") } protected open fun buildStartHtml(page: PageNode) = """ diff --git a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt index 6120e65e..a2756d42 100644 --- a/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt +++ b/core/src/main/kotlin/resolvers/DefaultLocationProvider.kt @@ -6,10 +6,10 @@ import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.pages.* open class DefaultLocationProvider(private val pageGraphRoot: PageNode, val configuration: DokkaConfiguration, val extension: String): LocationProvider { // TODO: cache - override fun resolve(node: PageNode): String = pathTo(node) + extension + override fun resolve(node: PageNode, context: PageNode?): String = pathTo(node, context) + extension - override fun resolve(dri: DRI, platforms: List): String { - findInPageGraph(dri, platforms)?.let { return resolve(it) } + override fun resolve(dri: DRI, platforms: List, context: PageNode?): String { + findInPageGraph(dri, platforms)?.let { return resolve(it, context) } // Not found in PageGraph, that means it's an external link val externalDocs = configuration.passesConfigurations @@ -21,19 +21,25 @@ open class DefaultLocationProvider(private val pageGraphRoot: PageNode, val conf protected open fun findInPageGraph(dri: DRI, platforms: List): PageNode? = pageGraphRoot.dfs { it.dri == dri } - protected open fun pathTo(node: PageNode): String { // TODO: can be refactored probably, also we should think about root - fun parentPath(parent: PageNode?): String { - if(parent == null) return "" - val parts = parent.parent?.let(::parentPath) ?: "" - return if(parent is PackagePageNode) {"$parts/${parent.name}"} else { "$parts/${identifierToFilename(parent.name)}" } + protected open fun pathTo(node: PageNode, context: PageNode?): String { // TODO: can be refactored probably, also we should think about root + fun parentPath(node: PageNode?): String { + if (node == null) return "" + val parts = node.parent?.let(::parentPath) ?: "" + return if(node is PackagePageNode) { + "$parts${node.name}/" + } else if (parts.isNotBlank()) { + "$parts${identifierToFilename(node.name)}/" + } else { + identifierToFilename(node.name) + "/" + } } - - return parentPath(node.parent) + "/${identifierToFilename(node.name)}" + + val resolved = parentPath(node.parent) + identifierToFilename(node.name) + if (node.children.isEmpty()) { "" } else { "/index" } + return context?.let { resolved.replace(pathTo(it, null).dropLastWhile { it != '/' }, "") } ?: resolved } } diff --git a/core/src/main/kotlin/resolvers/LocationProvider.kt b/core/src/main/kotlin/resolvers/LocationProvider.kt index b62aa999..e55c712d 100644 --- a/core/src/main/kotlin/resolvers/LocationProvider.kt +++ b/core/src/main/kotlin/resolvers/LocationProvider.kt @@ -5,6 +5,6 @@ import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.pages.PlatformData interface LocationProvider { - fun resolve(dri: DRI, platforms: List): String - fun resolve(node: PageNode): String + fun resolve(dri: DRI, platforms: List, context: PageNode? = null): String + fun resolve(node: PageNode, context: PageNode? = null): String } diff --git a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt index 010c46f4..c3236f14 100644 --- a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt @@ -1,27 +1,120 @@ package org.jetbrains.dokka.transformers -import org.jetbrains.dokka.pages.ContentNode -import org.jetbrains.dokka.pages.ContentSymbol -import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.pages.PageNode - -//class DefaultDocumentationToPageTransformer: DocumentationToPageTransformer { -// override fun transform(d: DocumentationNode): PageNode { -// assert(d is DocumentationNodes.Module) // TODO: remove this if it's true. Fix this it it's not -// val rootPage = ModulePageNode(d.name, contentFor(d), null) -// -// // TODO -// -// return rootPage -// } -// -// private fun contentFor(d: DocumentationNode): List { -// val symbol = ContentSymbol() -// } -// private fun moduleContent(d: DocumentationNodes.Module) -// -// private fun symbolFor(d: DocumentationNode): List { -// +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.Model.* +import org.jetbrains.dokka.Model.Function +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.pages.* +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe + + +class DefaultDocumentationToPageTransformer: DocumentationToPageTransformer { + override fun transform(modules: Collection>>): PageNode { + val module = modules.first().second // TODO only one module for starters + val platformData = modules.first().first.targets.map { PlatformData(it, modules.first().first.analysisPlatform) } + return PageBuilder(platformData).pageForModule(module as Module) + } + + class PageBuilder(private val platformData: List) { + fun pageForModule(m: Module) = + ModulePageNode("root", contentForModule(m), documentationNode = m).apply { + // TODO change name + appendChildren(m.packages.map { pageForPackage(it, this) }) + } + + private fun pageForPackage(p: Package, parent: PageNode) = + PackagePageNode(p.name, contentForPackage(p), parent, p.dri, p).apply { + appendChildren(p.classes.map { pageForClass(it, this) }) + appendChildren(p.functions.map { pageForMember(it, this) }) + appendChildren(p.properties.map { pageForMember(it, this) }) + } + + private fun pageForClass(c: Class, parent: PageNode): ClassPageNode = + ClassPageNode(c.name, contentForClass(c), parent, c.dri, c).apply { + // TODO: Pages for constructors + appendChildren(c.classes.map { pageForClass(it, this) }) + appendChildren(c.functions.map { pageForMember(it, this) }) + appendChildren(c.properties.map { pageForMember(it, this) }) + } + + private fun pageForMember(m: CallableNode<*>, parent: PageNode): MemberPageNode = + when (m) { + is Function -> MemberPageNode(m.name, contentForFunction(m), parent, m.dri, m) + is Property -> MemberPageNode(m.name, emptyList(), parent, m.dri, m) + else -> throw IllegalStateException("$m should not be present here") + } + + private fun contentForModule(m: Module) = listOf( + ContentHeader(listOf(ContentText("root", platformData)), 1, platformData), + ContentBlock("Packages", m.packages.map { ContentLink(it.name, it.dri, platformData) }, platformData), + ContentText("Index", platformData), + ContentText("Link to allpage here", platformData) + ) + + private fun contentForPackage(p: Package) = listOf( + ContentHeader(listOf(ContentText("Package ${p.name}", platformData)), 1, platformData), + ContentBlock("Types", p.classes.map { ContentGroup( + listOf( + ContentLink(it.name, it.dri, platformData), + ContentText("comment from class", platformData), + ContentText("signature for class", platformData) + ), platformData) + }, platformData), + ContentBlock("Functions", p.functions.map { ContentGroup( + listOf( + ContentLink(it.name, it.dri, platformData), + ContentText("comment for function", platformData), + ContentText("signature for function", platformData) + ), platformData) + }, platformData) + ) + + private fun contentForClass(c: Class) = listOf( + ContentHeader(listOf(ContentText(c.name, platformData)), 1, platformData), + ContentText("comment for class", platformData), + ContentBlock("Constructors", c.descriptor.constructors.map { ContentGroup( + listOf( + ContentLink(it.fqNameSafe.asString(), c.dri.copy(callable = Callable(it.fqNameSafe.asString() /* TODO: identifier for filename here */, "", "", it.valueParameters.map {it.fqNameSafe.asString()})), platformData), + ContentText("comment from constructor", platformData), + ContentText("signature for constructor", platformData) + ), platformData) + }, platformData), + ContentBlock("Functions", c.functions.map { ContentGroup( + listOf( + ContentLink(it.name, it.dri, platformData), + ContentText("comment for function", platformData), + ContentText("signature for function", platformData) + ), platformData) + }, platformData) + ) + + private fun contentForFunction(f: Function) = listOf( + ContentHeader(listOf(ContentText(f.name, platformData)), 1, platformData), + ContentText("signature for function", platformData), + ContentText("comment for function", platformData), + ContentBlock("Parameters", f.parameters.map { ContentGroup( + listOf( + ContentText(it.name ?: "?", platformData), + ContentText("comment from param", platformData) + ), platformData) + }, platformData) + ) + } +} + +fun DocumentationNode<*>.identifier(platformData: List): List { +// when(this) { +// is Class -> ContentText(this.descriptor.toString(), platforms), ContentText("(") this.properties.map { ContentText(it.descriptor.visibility + " " + it.descriptor.name + ":" + ),} +// is Function -> +// is Property -> +// else -> return emptyList() // } -// -//} \ No newline at end of file + TODO() +} +// take this ^ from old dokka +/* +pages are equal if the content and the children are equal +we then can merge the content by merging the platforms +and take an arbitrary set of the children +but we need to recursively process all of the children anyway + */ \ No newline at end of file diff --git a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt index bf9a80ea..6cbcada9 100644 --- a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt @@ -1,8 +1,9 @@ package org.jetbrains.dokka.transformers +import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.Model.DocumentationNode import org.jetbrains.dokka.pages.PageNode interface DocumentationToPageTransformer { - fun transform (d: DocumentationNode<*>): PageNode + fun transform (modules: Collection>>): PageNode // TODO refactor this } \ No newline at end of file diff --git a/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt b/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt index 70554045..523819b4 100644 --- a/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt +++ b/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.tests.markdownParser -import org.jetbrains.dokka.pages.NewContentBuilder +import org.jetbrains.dokka.pages.MarkdownToContentConverter import org.jetbrains.dokka.parseMarkdown import org.junit.Assert.assertTrue import org.junit.Test @@ -16,7 +16,7 @@ class MarkdownParserTest { let's say there are some parentheses, like ( and ) """.trimIndent() val node = parseMarkdown(markdown) - val content = NewContentBuilder().buildContent(node, emptyList()) + val content = MarkdownToContentConverter().buildContent(node, emptyList()) assertTrue(content.isNotEmpty()) } -- cgit