diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2019-11-04 19:59:18 +0100 |
---|---|---|
committer | Paweł Marks <pmarks@virtuslab.com> | 2019-11-04 19:59:18 +0100 |
commit | 78ef161062eefe33633ad912817ad5c0e1555ed6 (patch) | |
tree | 1026a340548b8038038c8d40881869f098eafceb /core | |
parent | 93af17aba8858806f197a7e8b8383566a1debdeb (diff) | |
download | dokka-78ef161062eefe33633ad912817ad5c0e1555ed6.tar.gz dokka-78ef161062eefe33633ad912817ad5c0e1555ed6.tar.bz2 dokka-78ef161062eefe33633ad912817ad5c0e1555ed6.zip |
Some parsing for markdown links
Diffstat (limited to 'core')
7 files changed, 189 insertions, 123 deletions
diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt index 5dd56624..2ac95812 100644 --- a/core/src/main/kotlin/DokkaGenerator.kt +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -3,6 +3,8 @@ package org.jetbrains.dokka import org.jetbrains.dokka.Model.Module import org.jetbrains.dokka.Utilities.pretty import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.pages.MarkdownToContentConverter +import org.jetbrains.dokka.pages.PageNode import org.jetbrains.dokka.renderers.FileWriter import org.jetbrains.dokka.renderers.HtmlRenderer import org.jetbrains.dokka.resolvers.DefaultLocationProvider @@ -35,24 +37,32 @@ class DokkaGenerator( val environment = createCoreEnvironment() val (facade, _) = createResolutionFacade(environment) - environment.getSourceFiles().asSequence() + val markdownConverter = MarkdownToContentConverter(facade, logger) + + val module = 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) + .let { Module(it) } + .also { println("${pass.analysisPlatform}:\n${it.pretty()}\n\n") } + + DefaultDocumentationToPageTransformer(markdownConverter).transform(pass, module) + } } + .merge() + .also { + HtmlRenderer( + FileWriter(configuration.outputDir, ""), + DefaultLocationProvider(it, configuration, ".${configuration.format}") + ).render(it) + } } } + +private fun Iterable<PageNode>.merge(): PageNode = first() // TODO: implement + private class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { override fun clear() { seenErrors = false diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt index 7ab25444..fced7dde 100644 --- a/core/src/main/kotlin/links/DRI.kt +++ b/core/src/main/kotlin/links/DRI.kt @@ -1,7 +1,9 @@ package org.jetbrains.dokka.links -import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import java.text.ParseException /** @@ -48,6 +50,19 @@ data class DRI( throw ParseException(s, 0) } + fun from(descriptor: DeclarationDescriptor) = descriptor.parentsWithSelf.run { + val callable = firstIsInstanceOrNull<CallableDescriptor>() + val params = callable?.let { listOfNotNull(it.extensionReceiverParameter) + it.valueParameters }.orEmpty() + DRI( + firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString(), + filterIsInstance<ClassDescriptor>().toList().takeIf { it.isNotEmpty() }?.asReversed() + ?.joinToString(separator = ".") { it.name.asString() }, + callable?.let { Callable.from(it) }, + firstIsInstanceOrNull<ParameterDescriptor>()?.let { params.indexOf(it) }, + null + ) + } + val topLevel = DRI() } } diff --git a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt index d541dc91..14b56226 100644 --- a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt +++ b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt @@ -2,13 +2,28 @@ package org.jetbrains.dokka.pages import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.MarkdownTokenTypes +import org.jetbrains.dokka.DokkaLogger +import org.jetbrains.dokka.DokkaResolutionFacade import org.jetbrains.dokka.MarkdownNode - -class MarkdownToContentConverter { - fun buildContent(node: MarkdownNode, platforms: List<PlatformData>): List<ContentNode> { +import org.jetbrains.dokka.Model.DocumentationNode +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.visit +import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink + +class MarkdownToContentConverter( + private val resolutionFacade: DokkaResolutionFacade, + private val logger: DokkaLogger +) { + fun buildContent( + node: MarkdownNode, + platforms: List<PlatformData>, + documentationNode: DocumentationNode<*> + ): List<ContentNode> { // println(tree.toTestString()) - fun buildChildren(node: MarkdownNode) = node.children.flatMap { buildContent(it, platforms) }.coalesceText() + fun buildChildren(node: MarkdownNode) = node.children.flatMap { + buildContent(it, platforms, documentationNode) + }.coalesceText() return when (node.type) { MarkdownElementTypes.ATX_1 -> listOf(ContentHeader(buildChildren(node), 1, platforms)) @@ -47,7 +62,13 @@ class MarkdownToContentConverter { 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.PARAGRAPH -> listOf( + ContentStyle( + buildChildren(node), + Style.Paragraph, + platforms + ) + ) // TODO MarkdownElementTypes.INLINE_LINK -> { // val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) @@ -69,23 +90,27 @@ class MarkdownToContentConverter { } 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() + val descriptor = documentationNode.descriptor + if (descriptor != null) { + val destinationNode = node.children.find { it.type == MarkdownElementTypes.LINK_DESTINATION } + ?: node.children.first { it.type == MarkdownElementTypes.LINK_LABEL } + val destination = destinationNode.children.find { it.type == MarkdownTokenTypes.TEXT }?.text + ?:destinationNode.text + + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + descriptor, + null, + destination.split('.') + ) + .firstOrNull() + ?.let { ContentLink(destination, DRI.from(it), platforms) } + .let(::listOfNotNull) + } else { + logger.error("Apparently descriptor for $documentationNode was needed in model") + emptyList() + } } MarkdownTokenTypes.WHITE_SPACE -> { // Don't append first space if start of header (it is added during formatting later) @@ -169,23 +194,28 @@ class MarkdownToContentConverter { .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)) + is ContentText -> listOf( + ContentText( + nodes.joinToString("") { (it as ContentText).text }, + nodes.first().platforms + ) + ) else -> nodes } } } -fun <T> Collection<T>.sliceWhen(predicate: (before: T, after: T)->Boolean): Collection<Collection<T>> { +fun <T> Collection<T>.sliceWhen(predicate: (before: T, after: T) -> Boolean): Collection<Collection<T>> { val newCollection = mutableListOf<Collection<T>>() var currentSlice = mutableListOf<T>() for ((prev, next) in this.windowed(2, 1, false)) { currentSlice.add(prev) - if(predicate(prev, next)) { + if (predicate(prev, next)) { newCollection.add(currentSlice) currentSlice = mutableListOf<T>() } } - if(this.isNotEmpty()) { + if (this.isNotEmpty()) { currentSlice.add(this.last()) newCollection.add(currentSlice) } diff --git a/core/src/main/kotlin/renderers/DefaultRenderer.kt b/core/src/main/kotlin/renderers/DefaultRenderer.kt index 5c6b3751..a2ca4131 100644 --- a/core/src/main/kotlin/renderers/DefaultRenderer.kt +++ b/core/src/main/kotlin/renderers/DefaultRenderer.kt @@ -19,7 +19,7 @@ abstract class DefaultRenderer(val fileWriter: FileWriter, val locationProvider: protected open fun ContentNode.build(pageContext: PageNode): String = buildContentNode(this, pageContext) - protected open fun buildContentNode(node: ContentNode, pageContext: PageNode) = + protected open fun buildContentNode(node: ContentNode, pageContext: PageNode): String = when(node) { is ContentText -> buildText(node.text) is ContentComment -> buildComment(node.parts, pageContext) @@ -29,6 +29,7 @@ abstract class DefaultRenderer(val fileWriter: FileWriter, val locationProvider: 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) + is ContentStyle -> node.items.joinToString(separator = "\n") { buildContentNode(it, pageContext) } else -> "" } diff --git a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt index ce4691b6..54fd9a27 100644 --- a/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/DefaultDocumentationToPageTransformer.kt @@ -4,18 +4,22 @@ 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.links.DRI import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.parseMarkdown +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -class DefaultDocumentationToPageTransformer: DocumentationToPageTransformer { - override fun transform(modules: Collection<Pair<DokkaConfiguration.PassConfiguration, Module>>): 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) } +class DefaultDocumentationToPageTransformer( + private val markdownConverter: MarkdownToContentConverter +) : DocumentationToPageTransformer { + override fun transform(passConfiguration: DokkaConfiguration.PassConfiguration, module: Module): PageNode { + val platformData = passConfiguration.targets.map { PlatformData(it, passConfiguration.analysisPlatform) } return PageBuilder(platformData).pageForModule(module) } - class PageBuilder(private val platformData: List<PlatformData>) { + private inner class PageBuilder(private val platformData: List<PlatformData>) { fun pageForModule(m: Module) = ModulePageNode("root", contentForModule(m), documentationNode = m).apply { // TODO change name @@ -44,62 +48,92 @@ class DefaultDocumentationToPageTransformer: DocumentationToPageTransformer { 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(it.briefDocstring, platformData), - ContentText("signature for class", platformData) - ), platformData) - }, platformData), - ContentBlock("Functions", p.functions.map { ContentGroup( - listOf( - ContentLink(it.name, it.dri, platformData), - ContentText(it.briefDocstring, platformData), - ContentText("signature for function", platformData) - ), platformData) - }, platformData) - ) - - private fun contentForClass(c: Class) = listOf( - ContentHeader(listOf(ContentText(c.name, platformData)), 1, platformData), - ContentText(c.rawDocstring, 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("message to Pawel from the future: you forgot about extracting constructors, didn't you?", platformData), - ContentText("signature for constructor", platformData) - ), platformData) - }, platformData), - ContentBlock("Functions", c.functions.map { ContentGroup( - listOf( - ContentLink(it.name, it.dri, platformData), - ContentText(it.briefDocstring, 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(f.rawDocstring, platformData), - ContentBlock("Parameters", f.parameters.map { ContentGroup( - listOf( - ContentText(it.name ?: "?", platformData), - ContentText(it.rawDocstring, platformData) - ), platformData) - }, platformData) - ) + private fun contentForModule(m: Module) = content(platformData) { + header(1) { text("root") } + block("Packages", m.packages) { link(it.name, it.dri) } + text("Index") + text("Link to allpage here") + } + + private fun contentForPackage(p: Package) = content(platformData) { + header(1) { text("Package ${p.name}") } + block("Types", p.classes) { + link(it.name, it.dri) + text(it.briefDocstring) + text("signature for class") + } + block("Functions", p.functions) { + link(it.name, it.dri) + text(it.briefDocstring) + text("signature for function") + } + } + + private fun contentForClass(c: Class) = content(platformData) { + header(1) { text(c.name) } + markdown(c.rawDocstring, c) + text("PING PAWEL TO ADD CONSTRUCTORS TO MODEL!!!") + block("Constructors", emptyList<Function>() /* TODO: CONSTRUCTORS*/) { + link(it.name, it.dri) + text(it.briefDocstring) + text("message to Pawel from the future: you forgot about extracting constructors, didn't you?") + } + block("Functions", c.functions) { + link(it.name, it.dri) + text(it.briefDocstring) + text("signature for function") + } + } + + private fun contentForFunction(f: Function) = content(platformData) { + header(1) { text(f.name) } + text("signature for function") + markdown(f.rawDocstring, f) + block("Parameters", f.children) { + group { + text(it.name ?: "RECEIVER") + markdown(it.rawDocstring, it) + } + } + } + } + + // TODO: Make some public builder or merge it with page builder, whateva + private inner class ContentBuilder(private val platformData: List<PlatformData>) { + private val contents = mutableListOf<ContentNode>() + + fun build() = contents.toList() + + fun header(level: Int, block: ContentBuilder.() -> Unit) { + contents += ContentHeader(content(block), level, platformData) + } + + fun text(text: String) { + contents += ContentText(text, platformData) + } + + fun <T> block(name: String, elements: Iterable<T>, block: ContentBuilder.(T) -> Unit) { + contents += ContentBlock(name, content { elements.forEach { block(it) } }, platformData) + } + + fun group(block: ContentBuilder.() -> Unit) { + contents += ContentGroup(content(block), platformData) + } + + fun link(text: String, address: DRI) { + contents += ContentLink(text, address, platformData) + } + + fun markdown(raw: String, node: DocumentationNode<*>) { + contents += markdownConverter.buildContent(parseMarkdown(raw), platformData, node) + } + + private fun content(block: ContentBuilder.() -> Unit): List<ContentNode> = content(platformData, block) } + + private fun content(platformData: List<PlatformData>, block: ContentBuilder.() -> Unit): List<ContentNode> = + ContentBuilder(platformData).apply(block).build() + } fun DocumentationNode<*>.identifier(platformData: List<PlatformData>): List<ContentNode> { diff --git a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt index f75f8d8e..c7859f73 100644 --- a/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt +++ b/core/src/main/kotlin/transformers/DocumentationToPageTransformer.kt @@ -6,5 +6,5 @@ import org.jetbrains.dokka.Model.Module import org.jetbrains.dokka.pages.PageNode interface DocumentationToPageTransformer { - fun transform (modules: Collection<Pair<DokkaConfiguration.PassConfiguration, Module>>): PageNode // TODO refactor this + fun transform(passConfiguration: DokkaConfiguration.PassConfiguration, module: Module): PageNode // TODO refactor this... some more? }
\ No newline at end of file diff --git a/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt b/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt deleted file mode 100644 index 523819b4..00000000 --- a/core/src/test/kotlin/markdownParser/MarkdownParserTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.jetbrains.dokka.tests.markdownParser - -import org.jetbrains.dokka.pages.MarkdownToContentConverter -import org.jetbrains.dokka.parseMarkdown -import org.junit.Assert.assertTrue -import org.junit.Test - - -class MarkdownParserTest { - - @Test fun basicTest() { - val markdown = """ - # Header 1 test - this is some text - more text - let's say there are some parentheses, like ( and ) - """.trimIndent() - val node = parseMarkdown(markdown) - val content = MarkdownToContentConverter().buildContent(node, emptyList()) - assertTrue(content.isNotEmpty()) - } - -} - |