From dd017a44ed7baae83f4f09a92d9691231f424eaa Mon Sep 17 00:00:00 2001 From: BarkingBad <32793002+BarkingBad@users.noreply.github.com> Date: Fri, 13 Dec 2019 14:01:25 +0100 Subject: Add abstract structure for MD/HTML comments and MD parser --- core/src/main/kotlin/model/Documentable.kt | 166 +++++++++++++++ core/src/main/kotlin/model/DocumentationNode.kt | 162 --------------- core/src/main/kotlin/model/doc/DocNode.kt | 74 +++++++ core/src/main/kotlin/model/doc/DocType.kt | 18 ++ .../src/main/kotlin/model/doc/DocumentationNode.kt | 3 + .../pages/DefaultMarkdownToContentConverter.kt | 229 --------------------- .../main/kotlin/pages/DocNodeToContentConverter.kt | 82 ++++++++ .../kotlin/pages/MarkdownToContentConverter.kt | 4 +- core/src/main/kotlin/pages/PageBuilder.kt | 23 ++- core/src/main/kotlin/pages/PageContentBuilder.kt | 26 +-- core/src/main/kotlin/pages/PageNodes.kt | 22 +- core/src/main/kotlin/parsers/HtmlParser.kt | 89 ++++++++ core/src/main/kotlin/parsers/MarkdownParser.kt | 206 ++++++++++++++++++ core/src/main/kotlin/parsers/Parser.kt | 44 ++++ .../factories/DocNodesFromIElementFactory.kt | 36 ++++ .../parsers/factories/DocNodesFromStringFactory.kt | 76 +++++++ .../main/kotlin/plugability/DefaultExtensions.kt | 4 +- core/src/main/kotlin/renderers/HtmlRenderer.kt | 9 +- .../DefaultDescriptorToDocumentationTranslator.kt | 38 +--- .../DefaultDocumentationNodeMerger.kt | 9 +- core/src/main/kotlin/utilities/nodeDebug.kt | 4 +- core/src/test/kotlin/markdown/ParserTest.kt | 8 +- 22 files changed, 859 insertions(+), 473 deletions(-) create mode 100644 core/src/main/kotlin/model/Documentable.kt delete mode 100644 core/src/main/kotlin/model/DocumentationNode.kt create mode 100644 core/src/main/kotlin/model/doc/DocNode.kt create mode 100644 core/src/main/kotlin/model/doc/DocType.kt create mode 100644 core/src/main/kotlin/model/doc/DocumentationNode.kt delete mode 100644 core/src/main/kotlin/pages/DefaultMarkdownToContentConverter.kt create mode 100644 core/src/main/kotlin/pages/DocNodeToContentConverter.kt create mode 100644 core/src/main/kotlin/parsers/HtmlParser.kt create mode 100644 core/src/main/kotlin/parsers/MarkdownParser.kt create mode 100644 core/src/main/kotlin/parsers/Parser.kt create mode 100644 core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt create mode 100644 core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt (limited to 'core/src') diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt new file mode 100644 index 00000000..9f676a1e --- /dev/null +++ b/core/src/main/kotlin/model/Documentable.kt @@ -0,0 +1,166 @@ +package org.jetbrains.dokka.model + +import model.doc.* +import org.jetbrains.dokka.transformers.descriptors.KotlinTypeWrapper +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.pages.PlatformData + +class Module(val packages: List) : Documentable() { + override val dri: DRI = DRI.topLevel + override val children: List = packages + override val extra: MutableSet = mutableSetOf() +} + +class Package( + override val dri: DRI, + override val functions: List, + override val properties: List, + override val classes: List, + override val extra: MutableSet = mutableSetOf() +) : ScopeNode() { + override val name = dri.packageName.orEmpty() +} + +class Class( + override val dri: DRI, + override val name: String, + val kind: ClassKind, + val constructors: List, + override val functions: List, + override val properties: List, + override val classes: List, + override val expected: ClassPlatformInfo?, + override val actual: List, + override val extra: MutableSet = mutableSetOf() +) : ScopeNode() { + val inherited by lazy { platformInfo.mapNotNull { (it as? ClassPlatformInfo)?.inherited }.flatten() } +} + +class Function( + override val dri: DRI, + override val name: String, + val returnType: TypeWrapper?, + val isConstructor: Boolean, + override val receiver: Parameter?, + val parameters: List, + override val expected: PlatformInfo?, + override val actual: List, + override val extra: MutableSet = mutableSetOf() +) : CallableNode() { + override val children: List + get() = listOfNotNull(receiver) + parameters +} + +class Property( + override val dri: DRI, + override val name: String, + override val receiver: Parameter?, + override val expected: PlatformInfo?, + override val actual: List, + override val extra: MutableSet = mutableSetOf() +) : CallableNode() { + override val children: List + get() = listOfNotNull(receiver) +} + +// TODO: treat named Parameters and receivers differently +class Parameter( + override val dri: DRI, + override val name: String?, + val type: TypeWrapper, + override val actual: List, + override val extra: MutableSet = mutableSetOf() +) : Documentable() { + override val children: List + get() = emptyList() +} + +interface PlatformInfo { + val documentationNode: DocumentationNode + val platformData: List +} + +class BasePlatformInfo( + override val documentationNode: DocumentationNode, + override val platformData: List) : PlatformInfo { + + override fun equals(other: Any?): Boolean = + other is PlatformInfo && documentationNode == other.documentationNode + + override fun hashCode(): Int = + documentationNode.hashCode() +} + +class ClassPlatformInfo( + val info: PlatformInfo, + val inherited: List) : PlatformInfo by info + +abstract class Documentable { + open val expected: PlatformInfo? = null + open val actual: List = emptyList() + open val name: String? = null + val platformInfo by lazy { listOfNotNull(expected) + actual } + val platformData by lazy { platformInfo.flatMap { it.platformData }.toSet() } + abstract val dri: DRI + + abstract val children: List + + override fun toString(): String { + return "${javaClass.simpleName}($dri)" + briefDocstring.takeIf { it.isNotBlank() }?.let { " [$it]" }.orEmpty() + } + + override fun equals(other: Any?) = other is Documentable && this.dri == other.dri + + override fun hashCode() = dri.hashCode() + + + + val commentsData: List + get() = platformInfo.map { it.documentationNode } + + val briefDocstring: String + get() = docNodeSummary(platformInfo.firstOrNull()?.documentationNode?.children?.firstOrNull()?.root ?: Text(body = "")).shorten(40) + + private fun docNodeSummary(docNode: DocNode): String { + if(docNode.children.isEmpty() && docNode is Text) + return docNode.body + + return docNode.children.joinToString(" ") { docNodeSummary(it) } + } + + open val extra: MutableSet = mutableSetOf() +} + +abstract class ScopeNode : Documentable() { + abstract val functions: List + abstract val properties: List + abstract val classes: List + + override val children: List + get() = functions + properties + classes +} + +abstract class CallableNode : Documentable() { + abstract val receiver: Parameter? +} + +private fun String.shorten(maxLength: Int) = lineSequence().first().let { + if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it +} + +interface TypeWrapper { + val constructorFqName: String? + val constructorNamePathSegments: List + val arguments: List + val dri: DRI? +} +interface ClassKind + +fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = + if (predicate(this)) { + this + } else { + this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() + } + +interface Extra \ No newline at end of file diff --git a/core/src/main/kotlin/model/DocumentationNode.kt b/core/src/main/kotlin/model/DocumentationNode.kt deleted file mode 100644 index 77225eca..00000000 --- a/core/src/main/kotlin/model/DocumentationNode.kt +++ /dev/null @@ -1,162 +0,0 @@ -package org.jetbrains.dokka.model - -import org.jetbrains.dokka.transformers.descriptors.KotlinTypeWrapper -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.pages.PlatformData -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag - -class Module(val packages: List) : DocumentationNode() { - override val dri: DRI = DRI.topLevel - override val children: List = packages - override val extra: MutableSet = mutableSetOf() -} - -class Package( - override val dri: DRI, - override val functions: List, - override val properties: List, - override val classes: List, - override val extra: MutableSet = mutableSetOf() -) : ScopeNode() { - override val name = dri.packageName.orEmpty() -} - -class Class( - override val dri: DRI, - override val name: String, - val kind: ClassKind, - val constructors: List, - override val functions: List, - override val properties: List, - override val classes: List, - override val expected: ClassPlatformInfo?, - override val actual: List, - override val extra: MutableSet = mutableSetOf() -) : ScopeNode() { - val inherited by lazy { platformInfo.mapNotNull { (it as? ClassPlatformInfo)?.inherited }.flatten() } -} - -class Function( - override val dri: DRI, - override val name: String, - val returnType: TypeWrapper?, - val isConstructor: Boolean, - override val receiver: Parameter?, - val parameters: List, - override val expected: PlatformInfo?, - override val actual: List, - override val extra: MutableSet = mutableSetOf() -) : CallableNode() { - override val children: List - get() = listOfNotNull(receiver) + parameters -} - -class Property( - override val dri: DRI, - override val name: String, - override val receiver: Parameter?, - override val expected: PlatformInfo?, - override val actual: List, - override val extra: MutableSet = mutableSetOf() -) : CallableNode() { - override val children: List - get() = listOfNotNull(receiver) -} - -// TODO: treat named Parameters and receivers differently -class Parameter( - override val dri: DRI, - override val name: String?, - val type: TypeWrapper, - override val actual: List, - override val extra: MutableSet = mutableSetOf() -) : DocumentationNode() { - override val children: List - get() = emptyList() -} - -interface PlatformInfo { - val docTag: KDocTag? - val links: Map - val platformData: List -} - -class BasePlatformInfo( - override val docTag: KDocTag?, - override val links: Map, - override val platformData: List) : PlatformInfo { - - override fun equals(other: Any?): Boolean = - other is PlatformInfo && ( - docTag?.text == other.docTag?.text && - links == other.links) - - override fun hashCode(): Int = - listOf(docTag?.text, links).hashCode() -} - -class ClassPlatformInfo( - val info: PlatformInfo, - val inherited: List) : PlatformInfo by info - -abstract class DocumentationNode { - open val expected: PlatformInfo? = null - open val actual: List = emptyList() - open val name: String? = null - val platformInfo by lazy { listOfNotNull(expected) + actual } - val platformData by lazy { platformInfo.flatMap { it.platformData }.toSet() } - - abstract val dri: DRI - - abstract val children: List - - override fun toString(): String { - return "${javaClass.simpleName}($dri)" + briefDocstring.takeIf { it.isNotBlank() }?.let { " [$it]" }.orEmpty() - } - - override fun equals(other: Any?) = other is DocumentationNode && this.dri == other.dri - - override fun hashCode() = dri.hashCode() - - val commentsData: List>> - get() = platformInfo.mapNotNull { it.docTag?.let { tag -> Pair(tag.getContent(), it.links) } } - - val briefDocstring: String - get() = platformInfo.firstOrNull()?.docTag?.getContent().orEmpty().shorten(40) - - open val extra: MutableSet = mutableSetOf() -} - -abstract class ScopeNode : DocumentationNode() { - abstract val functions: List - abstract val properties: List - abstract val classes: List - - override val children: List - get() = functions + properties + classes -} - -abstract class CallableNode : DocumentationNode() { - abstract val receiver: Parameter? -} - -private fun String.shorten(maxLength: Int) = lineSequence().first().let { - if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it -} - -interface TypeWrapper { - val constructorFqName: String? - val constructorNamePathSegments: List - val arguments: List - val dri: DRI? -} -interface ClassKind - -fun DocumentationNode.dfs(predicate: (DocumentationNode) -> Boolean): DocumentationNode? = - if (predicate(this)) { - this - } else { - this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() - } - -interface Extra \ No newline at end of file diff --git a/core/src/main/kotlin/model/doc/DocNode.kt b/core/src/main/kotlin/model/doc/DocNode.kt new file mode 100644 index 00000000..0c643551 --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocNode.kt @@ -0,0 +1,74 @@ +package model.doc + +import org.jetbrains.dokka.links.DRI + +sealed class DocNode( + val children: List, + val params: Map +) + +class A(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Big(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class B(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class BlockQuote(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Cite(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Code(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Dd(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Dfn(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Dir(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Div(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Dl(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Dt(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Em(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Font(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Footer(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Frame(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class FrameSet(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class H1(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class H2(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class H3(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class H4(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class H5(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class H6(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Head(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Header(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Html(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class I(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class IFrame(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Img(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Input(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Li(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Link(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Listing(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Main(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Menu(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Meta(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Nav(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class NoFrames(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class NoScript(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Ol(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class P(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Pre(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Script(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Section(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Small(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Span(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Strong(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Sub(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Sup(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Table(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Text(children: List = emptyList(), params: Map = emptyMap(), val body: String = "") : DocNode(children, params) +class TBody(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Td(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class TFoot(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Th(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class THead(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Title(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Tr(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Tt(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class U(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Ul(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class Var(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) +class DocumentationLink(children: List = emptyList(), params: Map = emptyMap(), val dri: DRI) : DocNode(children, params) +class HorizontalRule() : DocNode(emptyList(), emptyMap()) +class CustomNode(children: List = emptyList(), params: Map = emptyMap()) : DocNode(children, params) \ No newline at end of file diff --git a/core/src/main/kotlin/model/doc/DocType.kt b/core/src/main/kotlin/model/doc/DocType.kt new file mode 100644 index 00000000..0888876e --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocType.kt @@ -0,0 +1,18 @@ +package model.doc + +sealed class DocType(val root: DocNode) +class Description(root: DocNode) : DocType(root) +class Author(root: DocNode) : DocType(root) +class Version(root: DocNode) : DocType(root) +class Since(root: DocNode) : DocType(root) +class See(root: DocNode, val name: String) : DocType(root) +class Param(root: DocNode, val name: String) : DocType(root) +class Return(root: DocNode) : DocType(root) +class Receiver(root: DocNode) : DocType(root) +class Constructor(root: DocNode) : DocType(root) +class Throws(root: DocNode, val name: String) : DocType(root) +class Sample(root: DocNode, val name: String) : DocType(root) +class Deprecated(root: DocNode) : DocType(root) +class Property(root: DocNode, val name: String) : DocType(root) +class Suppress(root: DocNode) : DocType(root) +class CustomTag(root: DocNode, val name: String) : DocType(root) diff --git a/core/src/main/kotlin/model/doc/DocumentationNode.kt b/core/src/main/kotlin/model/doc/DocumentationNode.kt new file mode 100644 index 00000000..73424616 --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocumentationNode.kt @@ -0,0 +1,3 @@ +package model.doc + +data class DocumentationNode(val children: List) \ No newline at end of file diff --git a/core/src/main/kotlin/pages/DefaultMarkdownToContentConverter.kt b/core/src/main/kotlin/pages/DefaultMarkdownToContentConverter.kt deleted file mode 100644 index bb8a826d..00000000 --- a/core/src/main/kotlin/pages/DefaultMarkdownToContentConverter.kt +++ /dev/null @@ -1,229 +0,0 @@ -package org.jetbrains.dokka.pages - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.jetbrains.dokka.markdown.MarkdownNode -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.plugability.DokkaContext - -class DefaultMarkdownToContentConverter( - private val context: DokkaContext -) : MarkdownToContentConverter { - override fun buildContent( - node: MarkdownNode, - dci: DCI, - platforms: Set, - links: Map, - styles: Set