diff options
Diffstat (limited to 'core')
22 files changed, 721 insertions, 335 deletions
diff --git a/core/build.gradle b/core/build.gradle index 20ff8760..f9ab0f37 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -28,7 +28,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-reflect:$bundled_kotlin_compiler_version" compile group: 'com.google.inject', name: 'guice', version: '3.0' - compile "org.jsoup:jsoup:1.8.3" + implementation "org.jsoup:jsoup:1.12.1" compile "org.jetbrains.kotlin:kotlin-compiler:$bundled_kotlin_compiler_version" compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version" diff --git a/core/src/main/kotlin/model/DocumentationNode.kt b/core/src/main/kotlin/model/Documentable.kt index 77225eca..9f676a1e 100644 --- a/core/src/main/kotlin/model/DocumentationNode.kt +++ b/core/src/main/kotlin/model/Documentable.kt @@ -1,11 +1,11 @@ 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 -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -class Module(val packages: List<Package>) : DocumentationNode() { +class Module(val packages: List<Package>) : Documentable() { override val dri: DRI = DRI.topLevel override val children: List<Package> = packages override val extra: MutableSet<Extra> = mutableSetOf() @@ -70,73 +70,77 @@ class Parameter( val type: TypeWrapper, override val actual: List<PlatformInfo>, override val extra: MutableSet<Extra> = mutableSetOf() -) : DocumentationNode() { - override val children: List<DocumentationNode> +) : Documentable() { + override val children: List<Documentable> get() = emptyList() } interface PlatformInfo { - val docTag: KDocTag? - val links: Map<String, DRI> + val documentationNode: DocumentationNode val platformData: List<PlatformData> } class BasePlatformInfo( - override val docTag: KDocTag?, - override val links: Map<String, DRI>, + override val documentationNode: DocumentationNode, override val platformData: List<PlatformData>) : PlatformInfo { override fun equals(other: Any?): Boolean = - other is PlatformInfo && ( - docTag?.text == other.docTag?.text && - links == other.links) + other is PlatformInfo && documentationNode == other.documentationNode override fun hashCode(): Int = - listOf(docTag?.text, links).hashCode() + documentationNode.hashCode() } class ClassPlatformInfo( val info: PlatformInfo, val inherited: List<DRI>) : PlatformInfo by info -abstract class DocumentationNode { +abstract class Documentable { open val expected: PlatformInfo? = null open val actual: List<PlatformInfo> = 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<DocumentationNode> + abstract val children: List<Documentable> 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 equals(other: Any?) = other is Documentable && this.dri == other.dri override fun hashCode() = dri.hashCode() - val commentsData: List<Pair<String, Map<String, DRI>>> - get() = platformInfo.mapNotNull { it.docTag?.let { tag -> Pair(tag.getContent(), it.links) } } + + + val commentsData: List<DocumentationNode> + get() = platformInfo.map { it.documentationNode } val briefDocstring: String - get() = platformInfo.firstOrNull()?.docTag?.getContent().orEmpty().shorten(40) + 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<Extra> = mutableSetOf() } -abstract class ScopeNode : DocumentationNode() { +abstract class ScopeNode : Documentable() { abstract val functions: List<Function> abstract val properties: List<Property> abstract val classes: List<Class> - override val children: List<DocumentationNode> + override val children: List<Documentable> get() = functions + properties + classes } -abstract class CallableNode : DocumentationNode() { +abstract class CallableNode : Documentable() { abstract val receiver: Parameter? } @@ -152,7 +156,7 @@ interface TypeWrapper { } interface ClassKind -fun DocumentationNode.dfs(predicate: (DocumentationNode) -> Boolean): DocumentationNode? = +fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = if (predicate(this)) { this } else { 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<DocNode>, + val params: Map<String, String> +) + +class A(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Big(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class B(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class BlockQuote(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Cite(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Code(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Dd(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Dfn(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Dir(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Div(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Dl(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Dt(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Em(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Font(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Footer(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Frame(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class FrameSet(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class H1(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class H2(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class H3(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class H4(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class H5(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class H6(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Head(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Header(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Html(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class I(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class IFrame(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Img(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Input(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Li(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Link(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Listing(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Main(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Menu(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Meta(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Nav(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class NoFrames(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class NoScript(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Ol(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class P(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Pre(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Script(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Section(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Small(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Span(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Strong(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Sub(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Sup(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Table(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Text(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap(), val body: String = "") : DocNode(children, params) +class TBody(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Td(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class TFoot(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Th(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class THead(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Title(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Tr(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Tt(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class U(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Ul(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class Var(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap()) : DocNode(children, params) +class DocumentationLink(children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap(), val dri: DRI) : DocNode(children, params) +class HorizontalRule() : DocNode(emptyList(), emptyMap()) +class CustomNode(children: List<DocNode> = emptyList(), params: Map<String, String> = 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<DocType>)
\ 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<PlatformData>, - links: Map<String, DRI>, - styles: Set<Style>, - extras: Set<Extra> - - ): List<ContentNode> { -// println(tree.toTestString()) - - fun buildChildren(node: MarkdownNode, newStyles: Set<Style> = emptySet(), newExtras: Set<Extra> = emptySet()) = - node.children.flatMap { - buildContent(it, dci, platforms, links, styles + newStyles, extras + newExtras) - }.coalesceText(platforms, styles + newStyles, extras + newExtras) - - fun buildHeader(level: Int) = - ContentHeader(buildChildren(node), level, dci, platforms, styles) - - return when (node.type) { - MarkdownElementTypes.ATX_1 -> listOf(buildHeader(1)) - MarkdownElementTypes.ATX_2 -> listOf(buildHeader(2)) - MarkdownElementTypes.ATX_3 -> listOf(buildHeader(3)) - MarkdownElementTypes.ATX_4 -> listOf(buildHeader(4)) - MarkdownElementTypes.ATX_5 -> listOf(buildHeader(5)) - MarkdownElementTypes.ATX_6 -> listOf(buildHeader(6)) - MarkdownElementTypes.UNORDERED_LIST -> listOf( - ContentList( - buildChildren(node), - false, - dci, - platforms, - styles, - extras - ) - ) - MarkdownElementTypes.ORDERED_LIST -> listOf( - ContentList( - buildChildren(node), - true, - dci, - platforms, - styles, - extras - ) - ) - MarkdownElementTypes.LIST_ITEM -> TODO() - MarkdownElementTypes.STRONG, - MarkdownTokenTypes.EMPH, - MarkdownElementTypes.EMPH -> - buildChildren(node, setOf(TextStyle.Strong)) - // 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), language, dci, platforms, styles, extras)) // TODO - } - MarkdownElementTypes.PARAGRAPH -> buildChildren(node, newStyles = setOf(TextStyle.Paragraph)) - - 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 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 - links[destination]?.let { dri -> - listOf( - ContentDRILink( // TODO: differentiate between KDoc link and some external link (http://...) - buildChildren(node), - dri, - DCI(dri, ContentKind.Symbol), - platforms, - styles, - extras - ) - ) - } ?: let { - context.logger.error("Apparently there is no link resolved for $destination") - emptyList<ContentNode>() - } - } - 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(" ", dci, platforms, styles, extras)) - } - 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)) -// } - listOf(ContentText(" ", dci, platforms, styles, extras)) - } - - MarkdownTokenTypes.CODE_LINE -> { - listOf(ContentText(node.text, dci, platforms, styles, extras)) // 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, dci, platforms, styles, extras)) // 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, dci, platforms, styles, extras)) - } - - MarkdownElementTypes.LINK_DEFINITION -> TODO() - - MarkdownTokenTypes.EMAIL_AUTOLINK -> - listOf( - ContentResolvedLink( - listOf(ContentText(node.text, dci, platforms, styles, extras)), - "mailto:${node.text}", - dci, platforms, styles, extras - ) - ) - - else -> buildChildren(node) - } - } - - private fun Collection<ContentNode>.coalesceText( - platforms: Set<PlatformData>, - styles: Set<Style>, - extras: Set<Extra> - ) = - 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().dci, platforms, styles, extras - ) - ) - else -> nodes - } - } -} - -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)) { - newCollection.add(currentSlice) - currentSlice = mutableListOf<T>() - } - } - 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/DocNodeToContentConverter.kt b/core/src/main/kotlin/pages/DocNodeToContentConverter.kt new file mode 100644 index 00000000..dce69114 --- /dev/null +++ b/core/src/main/kotlin/pages/DocNodeToContentConverter.kt @@ -0,0 +1,82 @@ +package org.jetbrains.dokka.pages + +import model.doc.* +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.jetbrains.dokka.markdown.MarkdownNode +import org.jetbrains.dokka.plugability.DokkaContext + +class DocNodeToContentConverter( + private val context: DokkaContext +) : MarkdownToContentConverter { + override fun buildContent( + docNode: DocNode, + dci: DCI, + platforms: Set<PlatformData>, + styles: Set<Style>, + extras: Set<Extra> + + ): List<ContentNode> { + + fun buildChildren(docNode: DocNode, newStyles: Set<Style> = emptySet(), newExtras: Set<Extra> = emptySet()) = + docNode.children.flatMap { + buildContent(it, dci, platforms, styles + newStyles, extras + newExtras) + } + + fun buildHeader(level: Int) = + listOf(ContentHeader(buildChildren(docNode), level, dci, platforms, styles, extras)) + + fun buildList(ordered: Boolean) = + listOf(ContentList(buildChildren(docNode), ordered, dci, platforms, styles, extras)) + + return when (docNode) { + is H1 -> buildHeader(1) + is H2 -> buildHeader(2) + is H3 -> buildHeader(3) + is H4 -> buildHeader(4) + is H5 -> buildHeader(5) + is H6 -> buildHeader(6) + is Ul -> buildList(false) + is Ol -> buildList(true) + is Li -> buildChildren(docNode) + is B -> buildChildren(docNode, setOf(TextStyle.Strong)) + is I -> buildChildren(docNode, setOf(TextStyle.Italic)) + is P -> buildChildren(docNode, newStyles = setOf(TextStyle.Paragraph)) + is A -> listOf( + ContentResolvedLink( + buildChildren(docNode), + docNode.params.get("href")!!, + dci, + platforms, + styles, + extras + ) + ) + is DocumentationLink -> listOf( + ContentDRILink( + buildChildren(docNode), + docNode.dri, + DCI(docNode.dri, ContentKind.Symbol), + platforms, + styles, + extras + ) + ) + is BlockQuote -> throw NotImplementedError("Implement DocNotToContent BlockQuote!") + is Code -> listOf( + ContentCode( + buildChildren(docNode), + "", + dci, + platforms, + styles, + extras + ) + ) + is Img -> throw NotImplementedError("Implement DocNotToContent Img!") + is HorizontalRule -> listOf(ContentText("", dci, platforms, setOf())) + is Text -> listOf(ContentText(docNode.body, dci, platforms, styles, extras)) + else -> buildChildren(docNode) + } + } +} diff --git a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt index cd96ff79..321a3f02 100644 --- a/core/src/main/kotlin/pages/MarkdownToContentConverter.kt +++ b/core/src/main/kotlin/pages/MarkdownToContentConverter.kt @@ -1,14 +1,14 @@ package org.jetbrains.dokka.pages +import model.doc.DocNode import org.jetbrains.dokka.markdown.MarkdownNode import org.jetbrains.dokka.links.DRI interface MarkdownToContentConverter { fun buildContent( - node: MarkdownNode, + docNode: DocNode, dci: DCI, platforms: Set<PlatformData>, - links: Map<String, DRI> = emptyMap(), styles: Set<Style> = emptySet(), extras: Set<Extra> = emptySet() ): List<ContentNode> diff --git a/core/src/main/kotlin/pages/PageBuilder.kt b/core/src/main/kotlin/pages/PageBuilder.kt index 92e2c5fe..8951219a 100644 --- a/core/src/main/kotlin/pages/PageBuilder.kt +++ b/core/src/main/kotlin/pages/PageBuilder.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.pages +import model.doc.DocType import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.Function @@ -29,7 +30,7 @@ class DefaultPageBuilder( else -> throw IllegalStateException("$m should not be present here") } - private fun group(node: DocumentationNode, content: PageContentBuilderFunction) = + private fun group(node: Documentable, content: PageContentBuilderFunction) = rootContentGroup(node, ContentKind.Main, content) private fun contentForModule(m: Module) = group(m) { @@ -60,7 +61,13 @@ class DefaultPageBuilder( header(2) { text("SuperInterfaces") } linkTable(it) } - c.commentsData.forEach { (doc, links) -> comment(doc, links) } + c.commentsData.forEach { + it.children.forEach { + header(3) { text(it.toHeaderString()) } + comment(it.root) + text("\n") + } + } block("Constructors", 2, ContentKind.Functions, c.constructors, c.platformData) { link(it.name, it.dri) signature(it) @@ -71,20 +78,26 @@ class DefaultPageBuilder( signature(it) text(it.briefDocstring) } + block("Properties", 2, ContentKind.Properties, c.properties, c.platformData) { + link(it.name, it.dri) + text(it.briefDocstring) + } } private fun contentForFunction(f: Function) = group(f) { header(1) { text(f.name) } signature(f) - f.commentsData.forEach { (doc, links) -> markdown(doc, links) } + f.commentsData.forEach { it.children.forEach { comment(it.root) } } block("Parameters", 2, ContentKind.Parameters, f.children, f.platformData) { text(it.name ?: "<receiver>") - it.commentsData.forEach { (doc, links) -> markdown(doc, links) } + it.commentsData.forEach { it.children.forEach { comment(it.root) } } } } + + private fun DocType.toHeaderString() = this.javaClass.toGenericString().split('.').last() } -typealias RootContentBuilder = (DocumentationNode, Kind, PageContentBuilderFunction) -> ContentGroup +typealias RootContentBuilder = (Documentable, Kind, PageContentBuilderFunction) -> ContentGroup interface PageBuilder { val rootContentGroup: RootContentBuilder diff --git a/core/src/main/kotlin/pages/PageContentBuilder.kt b/core/src/main/kotlin/pages/PageContentBuilder.kt index 3e852306..e3c924d1 100644 --- a/core/src/main/kotlin/pages/PageContentBuilder.kt +++ b/core/src/main/kotlin/pages/PageContentBuilder.kt @@ -1,12 +1,12 @@ package org.jetbrains.dokka.pages +import model.doc.DocNode import org.jetbrains.dokka.utilities.DokkaLogger -import org.jetbrains.dokka.model.DocumentationNode +import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.model.Function import org.jetbrains.dokka.model.Parameter import org.jetbrains.dokka.model.TypeWrapper import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.markdown.parseMarkdown class DefaultPageContentBuilder( private val dri: DRI, @@ -73,7 +73,7 @@ class DefaultPageContentBuilder( ) } - override fun <T : DocumentationNode> block( + override fun <T : Documentable> block( name: String, level: Int, kind: Kind, @@ -127,27 +127,18 @@ class DefaultPageContentBuilder( ) } - override fun comment(raw: String, links: Map<String, DRI>) { + override fun comment(docNode: DocNode) { contents += group(ContentKind.Comment) { with(this as DefaultPageContentBuilder) { contents += markdownConverter.buildContent( - parseMarkdown(raw), + docNode, DCI(dri, ContentKind.Comment), - platformData, - links + platformData ) } } } - override fun markdown(raw: String, links: Map<String, DRI>) { - contents += markdownConverter.buildContent( - parseMarkdown(raw), DCI(dri, ContentKind.Sample), - platformData, - links - ) - } - fun group(kind: Kind, block: PageContentBuilderFunction): ContentGroup = group(dri, platformData, kind, block) @@ -200,8 +191,7 @@ interface PageContentBuilder { fun link(text: String, address: DRI, kind: Kind = ContentKind.Symbol) fun link(address: DRI, kind: Kind = ContentKind.Symbol, block: PageContentBuilderFunction) fun linkTable(elements: List<DRI>) - fun comment(raw: String, links: Map<String, DRI>) - fun markdown(raw: String, links: Map<String, DRI>) + fun comment(docNode: DocNode) fun header(level: Int, block: PageContentBuilderFunction) fun <T> list( elements: List<T>, @@ -211,7 +201,7 @@ interface PageContentBuilder { operation: PageContentBuilder.(T) -> Unit ) - fun <T : DocumentationNode> block( + fun <T : Documentable> block( name: String, level: Int, kind: Kind, diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt index f1ad430f..0aa439de 100644 --- a/core/src/main/kotlin/pages/PageNodes.kt +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -1,6 +1,6 @@ package org.jetbrains.dokka.pages -import org.jetbrains.dokka.model.DocumentationNode +import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.Platform import org.jetbrains.dokka.links.DRI import java.util.* @@ -9,7 +9,7 @@ interface PageNode { val name: String val content: ContentNode val dri: DRI - val documentationNode: DocumentationNode? + val documentable: Documentable? val embeddedResources: List<String> val children: List<PageNode> @@ -24,7 +24,8 @@ interface PageNode { class ModulePageNode( override val name: String, override val content: ContentNode, - override val documentationNode: DocumentationNode?, + + override val documentable: Documentable?, override val children: List<PageNode>, override val embeddedResources: List<String> = listOf() ) : PageNode { @@ -37,7 +38,7 @@ class ModulePageNode( children: List<PageNode> ): ModulePageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this - else ModulePageNode(name, content, documentationNode, children, embeddedResources) + else ModulePageNode(name, content, documentable, children, embeddedResources) private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode = operation(this).let { newNode -> @@ -64,7 +65,8 @@ class PackagePageNode( override val name: String, override val content: ContentNode, override val dri: DRI, - override val documentationNode: DocumentationNode?, + + override val documentable: Documentable?, override val children: List<PageNode>, override val embeddedResources: List<String> = listOf() ) : PageNode { @@ -76,14 +78,14 @@ class PackagePageNode( children: List<PageNode> ): PackagePageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this - else PackagePageNode(name, content, dri, documentationNode, children, embeddedResources) + else PackagePageNode(name, content, dri, documentable, children, embeddedResources) } class ClassPageNode( override val name: String, override val content: ContentNode, override val dri: DRI, - override val documentationNode: DocumentationNode?, + override val documentable: Documentable?, override val children: List<PageNode>, override val embeddedResources: List<String> = listOf() ) : PageNode { @@ -95,14 +97,14 @@ class ClassPageNode( children: List<PageNode> ): ClassPageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this - else ClassPageNode(name, content, dri, documentationNode, children, embeddedResources) + else ClassPageNode(name, content, dri, documentable, children, embeddedResources) } class MemberPageNode( override val name: String, override val content: ContentNode, override val dri: DRI, - override val documentationNode: DocumentationNode?, + override val documentable: Documentable?, override val children: List<PageNode> = emptyList(), override val embeddedResources: List<String> = listOf() ) : PageNode { @@ -114,7 +116,7 @@ class MemberPageNode( children: List<PageNode> ): MemberPageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this - else MemberPageNode(name, content, dri, documentationNode, children, embeddedResources) + else MemberPageNode(name, content, dri, documentable, children, embeddedResources) } data class PlatformData(val platformType: Platform, val targets: List<String>) { diff --git a/core/src/main/kotlin/parsers/HtmlParser.kt b/core/src/main/kotlin/parsers/HtmlParser.kt new file mode 100644 index 00000000..30f882b1 --- /dev/null +++ b/core/src/main/kotlin/parsers/HtmlParser.kt @@ -0,0 +1,89 @@ +package parsers + +import model.doc.* +import org.jetbrains.dokka.parsers.factories.DocNodesFromStringFactory +import org.jsoup.Jsoup +import org.jsoup.nodes.Node +import org.jsoup.select.NodeFilter +import org.jsoup.select.NodeTraversor + +class HtmlParser : Parser() { + + inner class NodeFilterImpl : NodeFilter { + + private val nodesCache: MutableMap<Int, MutableList<DocNode>> = mutableMapOf() + private var currentDepth = 0 + + fun collect(): DocNode = nodesCache[currentDepth]!![0] + + override fun tail(node: Node?, depth: Int): NodeFilter.FilterResult { + val nodeName = node!!.nodeName() + val nodeAttributes = node.attributes() + + if(nodeName in listOf("#document", "html", "head")) + return NodeFilter.FilterResult.CONTINUE + + val body: String + val params: Map<String, String> + + + if(nodeName != "#text") { + body = "" + params = nodeAttributes.map { it.key to it.value }.toMap() + } else { + body = nodeAttributes["#text"] + params = emptyMap() + } + + val docNode = if(depth < currentDepth) { + DocNodesFromStringFactory.getInstance(nodeName, nodesCache.getOrDefault(currentDepth, mutableListOf()).toList(), params, body).also { + nodesCache[currentDepth] = mutableListOf() + currentDepth = depth + } + } else { + DocNodesFromStringFactory.getInstance(nodeName, emptyList(), params, body) + } + + nodesCache.getOrDefault(depth, mutableListOf()) += docNode + return NodeFilter.FilterResult.CONTINUE + } + + override fun head(node: Node?, depth: Int): NodeFilter.FilterResult { + + val nodeName = node!!.nodeName() + + if(currentDepth < depth) { + currentDepth = depth + nodesCache[currentDepth] = mutableListOf() + } + + if(nodeName in listOf("#document", "html", "head")) + return NodeFilter.FilterResult.CONTINUE + + return NodeFilter.FilterResult.CONTINUE + } + } + + + private fun htmlToDocNode(string: String): DocNode { + val document = Jsoup.parse(string) + val nodeFilterImpl = NodeFilterImpl() + NodeTraversor.filter(nodeFilterImpl, document.root()) + return nodeFilterImpl.collect() + } + + private fun replaceLinksWithHrefs(javadoc: String): String = Regex("\\{@link .*?}").replace(javadoc) { + val split = it.value.dropLast(1).split(" ") + if(split.size !in listOf(2, 3)) + return@replace it.value + if(split.size == 3) + return@replace "<documentationlink href=\"${split[1]}\">${split[2]}</documentationlink>" + else + return@replace "<documentationlink href=\"${split[1]}\">${split[1]}</documentationlink>" + } + + override fun parseStringToDocNode(extractedString: String) = htmlToDocNode(extractedString) + override fun preparse(text: String) = replaceLinksWithHrefs(text) +} + + diff --git a/core/src/main/kotlin/parsers/MarkdownParser.kt b/core/src/main/kotlin/parsers/MarkdownParser.kt new file mode 100644 index 00000000..44c917e3 --- /dev/null +++ b/core/src/main/kotlin/parsers/MarkdownParser.kt @@ -0,0 +1,206 @@ +package parsers + +import model.doc.* +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.intellij.markdown.ast.ASTNode +import org.intellij.markdown.ast.impl.ListItemCompositeNode +import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor +import org.jetbrains.dokka.analysis.DokkaResolutionFacade +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.parsers.factories.DocNodesFromIElementFactory +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink +import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import org.intellij.markdown.parser.MarkdownParser as IntellijMarkdownParser + +class MarkdownParser ( + private val resolutionFacade: DokkaResolutionFacade, + private val declarationDescriptor: DeclarationDescriptor + ) : Parser() { + + inner class MarkdownVisitor(val text: String) { + + private fun headersHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance(node.type, visitNode(node.children.find { it.type == MarkdownTokenTypes.ATX_CONTENT }!!).children.drop(1)) + + private fun horizontalRulesHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance(MarkdownTokenTypes.HORIZONTAL_RULE) + + private fun emphasisHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance(node.type, children = listOf(visitNode(node.children[node.children.size/2]))) + + private fun blockquotesHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance(node.type, children = node.children.drop(1).map { visitNode(it) }) + + private fun listsHandler(node: ASTNode): DocNode { + + val children = node.children.filterIsInstance<ListItemCompositeNode>().flatMap { + if( it.children.last().type in listOf(MarkdownElementTypes.ORDERED_LIST, MarkdownElementTypes.UNORDERED_LIST) ) { + val nestedList = it.children.last() + (it.children as MutableList).removeAt(it.children.lastIndex) + listOf(it, nestedList) + } + else + listOf(it) + } + + return DocNodesFromIElementFactory.getInstance( + node.type, + children = + children + .map { + if(it.type == MarkdownElementTypes.LIST_ITEM) + DocNodesFromIElementFactory.getInstance( + it.type, + children = it + .children + .drop(1) + .filter { it.type !in listOf(MarkdownTokenTypes.WHITE_SPACE, MarkdownTokenTypes.EOL) } + .map { visitNode(it) } + ) + else + visitNode(it) + }, + params = + if (node.type == MarkdownElementTypes.ORDERED_LIST) { + val listNumberNode = node.children.first().children.first() + mapOf("start" to text.substring(listNumberNode.startOffset, listNumberNode.endOffset).dropLast(2)) + } else + emptyMap() + ) + } + + private fun linksHandler(node: ASTNode): DocNode { + val linkNode = node.children.find { it.type == MarkdownElementTypes.LINK_LABEL }!! + val link = text.substring(linkNode.startOffset+1, linkNode.endOffset-1) + + val dri: DRI? = if (link.startsWith("http") || link.startsWith("www")) { + null + } else { + DRI.from( + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + declarationDescriptor, + null, + link.split('.') + ).single() + ) + } + val href = mapOf("href" to link) + return when (node.type) { + MarkdownElementTypes.FULL_REFERENCE_LINK -> DocNodesFromIElementFactory.getInstance(node.type, params = href, children = node.children.find { it.type == MarkdownElementTypes.LINK_TEXT }!!.children.drop(1).dropLast(1).map { visitNode(it) }, dri = dri) + else -> DocNodesFromIElementFactory.getInstance(node.type, params = href, children = listOf(visitNode(linkNode)), dri = dri) + } + } + + private fun imagesHandler(node: ASTNode): DocNode { + val linkNode = node.children.last().children.find { it.type == MarkdownElementTypes.LINK_LABEL }!!.children[1] + val link = text.substring(linkNode.startOffset, linkNode.endOffset) + val src = mapOf("src" to link) + return DocNodesFromIElementFactory.getInstance(node.type, params = src, children = listOf(visitNode(node.children.last().children.find { it.type == MarkdownElementTypes.LINK_TEXT }!!))) + } + + private fun codeSpansHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance( + node.type, + children = listOf( + DocNodesFromIElementFactory.getInstance( + MarkdownTokenTypes.TEXT, + body = text.substring(node.startOffset+1, node.endOffset-1).replace('\n', ' ').trimIndent() + ) + + ) + ) + + private fun codeFencesHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance( + node.type, + children = node + .children + .filter { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT } + .map { visitNode(it) }, + params = node + .children + .find { it.type == MarkdownTokenTypes.FENCE_LANG } + ?.let { mapOf("lang" to text.substring(it.startOffset, it.endOffset)) } + ?: emptyMap() + ) + + private fun codeBlocksHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance(node.type, children = node.children.map { visitNode(it) }) + + private fun defaultHandler(node: ASTNode): DocNode = + DocNodesFromIElementFactory.getInstance(MarkdownElementTypes.PARAGRAPH, children = node.children.map { visitNode(it) }) + + fun visitNode(node: ASTNode): DocNode = + when (node.type) { + MarkdownElementTypes.ATX_1, + MarkdownElementTypes.ATX_2, + MarkdownElementTypes.ATX_3, + MarkdownElementTypes.ATX_4, + MarkdownElementTypes.ATX_5, + MarkdownElementTypes.ATX_6 -> headersHandler(node) + MarkdownTokenTypes.HORIZONTAL_RULE -> horizontalRulesHandler(node) + MarkdownElementTypes.STRONG, + MarkdownElementTypes.EMPH -> emphasisHandler(node) + MarkdownElementTypes.FULL_REFERENCE_LINK, + MarkdownElementTypes.SHORT_REFERENCE_LINK -> linksHandler(node) + MarkdownElementTypes.BLOCK_QUOTE -> blockquotesHandler(node) + MarkdownElementTypes.UNORDERED_LIST, + MarkdownElementTypes.ORDERED_LIST -> listsHandler(node) + MarkdownElementTypes.CODE_BLOCK -> codeBlocksHandler(node) + MarkdownElementTypes.CODE_FENCE -> codeFencesHandler(node) + MarkdownElementTypes.CODE_SPAN -> codeSpansHandler(node) + MarkdownElementTypes.IMAGE -> imagesHandler(node) + MarkdownTokenTypes.EOL -> DocNodesFromIElementFactory.getInstance(MarkdownTokenTypes.TEXT, body = "\n") + MarkdownTokenTypes.WHITE_SPACE -> DocNodesFromIElementFactory.getInstance(MarkdownTokenTypes.TEXT, body = " ") + MarkdownTokenTypes.CODE_FENCE_CONTENT, + MarkdownTokenTypes.CODE_LINE, + MarkdownTokenTypes.TEXT -> DocNodesFromIElementFactory.getInstance(MarkdownTokenTypes.TEXT, body = text.substring(node.startOffset, node.endOffset)) + else -> defaultHandler(node) + } + } + + private fun markdownToDocNode(text: String): DocNode { + + val flavourDescriptor = CommonMarkFlavourDescriptor() + val markdownAstRoot: ASTNode = IntellijMarkdownParser(flavourDescriptor).buildMarkdownTreeFromString(text) + + return MarkdownVisitor(text).visitNode(markdownAstRoot) + } + + override fun parseStringToDocNode(extractedString: String) = markdownToDocNode(extractedString) + override fun preparse(text: String) = text + + fun parseFromKDocTag(kDocTag: KDocTag?): DocumentationNode { + return if(kDocTag == null) + DocumentationNode(emptyList()) + else + DocumentationNode( + (listOf(kDocTag) + kDocTag.children).filterIsInstance<KDocTag>().map { + when( it.knownTag ) { + null -> Description(parseStringToDocNode(it.getContent())) + KDocKnownTag.AUTHOR -> Author(parseStringToDocNode(it.getContent())) + KDocKnownTag.THROWS -> Throws(parseStringToDocNode(it.getContent()), it.getSubjectName()!!) + KDocKnownTag.EXCEPTION -> Throws(parseStringToDocNode(it.getContent()), it.getSubjectName()!!) + KDocKnownTag.PARAM -> Param(parseStringToDocNode(it.getContent()), it.getSubjectName()!!) + KDocKnownTag.RECEIVER -> Receiver(parseStringToDocNode(it.getContent())) + KDocKnownTag.RETURN -> Return(parseStringToDocNode(it.getContent())) + KDocKnownTag.SEE -> See(parseStringToDocNode(it.getContent()), it.getSubjectName()!!) + KDocKnownTag.SINCE -> Since(parseStringToDocNode(it.getContent())) + KDocKnownTag.CONSTRUCTOR -> Constructor(parseStringToDocNode(it.getContent())) + KDocKnownTag.PROPERTY -> Property(parseStringToDocNode(it.getContent()), it.getSubjectName()!!) + KDocKnownTag.SAMPLE -> Sample(parseStringToDocNode(it.getContent()), it.getSubjectName()!!) + KDocKnownTag.SUPPRESS -> Suppress(parseStringToDocNode(it.getContent())) + } + } + ) + } + + + + +}
\ No newline at end of file diff --git a/core/src/main/kotlin/parsers/Parser.kt b/core/src/main/kotlin/parsers/Parser.kt new file mode 100644 index 00000000..a2a90dcd --- /dev/null +++ b/core/src/main/kotlin/parsers/Parser.kt @@ -0,0 +1,44 @@ +package parsers + +import model.doc.* +import model.doc.Deprecated + + +abstract class Parser { + + abstract fun parseStringToDocNode(extractedString: String): DocNode + abstract fun preparse(text: String): String + + fun parse(text: String): DocumentationNode { + + val list = jkdocToListOfPairs(preparse(text)) + + val mappedList: List<DocType> = list.map { + when(it.first) { + "description" -> Description(parseStringToDocNode(it.second)) + "author" -> Author(parseStringToDocNode(it.second)) + "version" -> Version(parseStringToDocNode(it.second)) + "since" -> Since(parseStringToDocNode(it.second)) + "see" -> See(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "param" -> Param(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "property" -> Property(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "return" -> Return(parseStringToDocNode(it.second)) + "constructor" -> Constructor(parseStringToDocNode(it.second)) + "receiver" -> Receiver(parseStringToDocNode(it.second)) + "throws", "exception" -> Throws(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "deprecated" -> Deprecated(parseStringToDocNode(it.second)) + "sample" -> Sample(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "suppress" -> Suppress(parseStringToDocNode(it.second)) + else -> CustomTag(parseStringToDocNode(it.second), it.first) + } + } + return DocumentationNode(mappedList) + } + + private fun jkdocToListOfPairs(javadoc: String): List<Pair<String, String>> = + "description $javadoc" + .split("\n@") + .map { + it.substringBefore(' ') to it.substringAfter(' ') + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt b/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt new file mode 100644 index 00000000..a93be0d3 --- /dev/null +++ b/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt @@ -0,0 +1,36 @@ +package org.jetbrains.dokka.parsers.factories + +import model.doc.* +import org.intellij.markdown.IElementType +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.jetbrains.dokka.links.DRI +import java.lang.NullPointerException + +object DocNodesFromIElementFactory { + fun getInstance(type: IElementType, children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap(), body: String? = null, dri: DRI? = null) = + when(type) { + MarkdownElementTypes.SHORT_REFERENCE_LINK, + MarkdownElementTypes.FULL_REFERENCE_LINK -> if(dri == null) A(children, params) else DocumentationLink(children, params, dri) + MarkdownElementTypes.STRONG -> B(children, params) + MarkdownElementTypes.BLOCK_QUOTE -> BlockQuote(children, params) + MarkdownElementTypes.CODE_SPAN, + MarkdownElementTypes.CODE_BLOCK, + MarkdownElementTypes.CODE_FENCE -> Code(children, params) + MarkdownElementTypes.ATX_1 -> H1(children, params) + MarkdownElementTypes.ATX_2 -> H2(children, params) + MarkdownElementTypes.ATX_3 -> H3(children, params) + MarkdownElementTypes.ATX_4 -> H4(children, params) + MarkdownElementTypes.ATX_5 -> H5(children, params) + MarkdownElementTypes.ATX_6 -> H6(children, params) + MarkdownElementTypes.EMPH -> I(children, params) + MarkdownElementTypes.IMAGE -> Img(children, params) + MarkdownElementTypes.LIST_ITEM -> Li(children, params) + MarkdownElementTypes.ORDERED_LIST -> Ol(children, params) + MarkdownElementTypes.UNORDERED_LIST -> Ul(children, params) + MarkdownElementTypes.PARAGRAPH -> P(children, params) + MarkdownTokenTypes.TEXT -> Text(children, params, body ?: throw NullPointerException("Text body should be at least empty string passed to DocNodes factory!")) + MarkdownTokenTypes.HORIZONTAL_RULE -> HorizontalRule() + else -> CustomNode(children, params) + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt b/core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt new file mode 100644 index 00000000..49102ed0 --- /dev/null +++ b/core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt @@ -0,0 +1,76 @@ +package org.jetbrains.dokka.parsers.factories + +import model.doc.* +import org.jetbrains.dokka.links.DRI +import java.lang.NullPointerException + +object DocNodesFromStringFactory { + fun getInstance(name: String, children: List<DocNode> = emptyList(), params: Map<String, String> = emptyMap(), body: String? = null, dri: DRI? = null) = + when(name) { + "a" -> A(children, params) + "big" -> Big(children, params) + "b" -> B(children, params) + "blockquote" -> BlockQuote(children, params) + "bite" -> Cite(children, params) + "bode" -> Code(children, params) + "dd" -> Dd(children, params) + "dfn" -> Dfn(children, params) + "dir" -> Dir(children, params) + "div" -> Div(children, params) + "dl" -> Dl(children, params) + "dt" -> Dt(children, params) + "Em" -> Em(children, params) + "font" -> Font(children, params) + "footer" -> Footer(children, params) + "frame" -> Frame(children, params) + "frameset" -> FrameSet(children, params) + "h1" -> H1(children, params) + "h2" -> H2(children, params) + "h3" -> H3(children, params) + "h4" -> H4(children, params) + "h5" -> H5(children, params) + "h6" -> H6(children, params) + "head" -> Head(children, params) + "header" -> Header(children, params) + "html" -> Html(children, params) + "i" -> I(children, params) + "iframe" -> IFrame(children, params) + "img" -> Img(children, params) + "input" -> Input(children, params) + "li" -> Li(children, params) + "link" -> Link(children, params) + "listing" -> Listing(children, params) + "main" -> Main(children, params) + "menu" -> Menu(children, params) + "meta" -> Meta(children, params) + "nav" -> Nav(children, params) + "noframes" -> NoFrames(children, params) + "noscript" -> NoScript(children, params) + "ol" -> Ol(children, params) + "p" -> P(children, params) + "pre" -> Pre(children, params) + "script" -> Script(children, params) + "section" -> Section(children, params) + "small" -> Small(children, params) + "span" -> Span(children, params) + "strong" -> Strong(children, params) + "sub" -> Sub(children, params) + "sup" -> Sup(children, params) + "table" -> Table(children, params) + "#text" -> Text(children, params, body ?: throw NullPointerException("Text body should be at least empty string passed to DocNodes factory!")) + "tBody" -> TBody(children, params) + "td" -> Td(children, params) + "tFoot" -> TFoot(children, params) + "th" -> Th(children, params) + "tHead" -> THead(children, params) + "title" -> Title(children, params) + "tr" -> Tr(children, params) + "tt" -> Tt(children, params) + "u" -> U(children, params) + "ul" -> Ul(children, params) + "var" -> Var(children, params) + "documentationlink" -> DocumentationLink(children, params, dri ?: throw NullPointerException("DRI cannot be passed null while constructing documentation link!")) + "hr" -> HorizontalRule() + else -> CustomNode(children, params) + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/plugability/DefaultExtensions.kt b/core/src/main/kotlin/plugability/DefaultExtensions.kt index d89fc031..e46ade01 100644 --- a/core/src/main/kotlin/plugability/DefaultExtensions.kt +++ b/core/src/main/kotlin/plugability/DefaultExtensions.kt @@ -1,7 +1,7 @@ package org.jetbrains.dokka.plugability import org.jetbrains.dokka.CoreExtensions -import org.jetbrains.dokka.pages.DefaultMarkdownToContentConverter +import org.jetbrains.dokka.pages.DocNodeToContentConverter import org.jetbrains.dokka.renderers.HtmlRenderer import org.jetbrains.dokka.resolvers.DefaultLocationProvider import org.jetbrains.dokka.transformers.descriptors.DefaultDescriptorToDocumentationTranslator @@ -14,7 +14,7 @@ object DefaultExtensions : DokkaExtensionHandler { when (point) { CoreExtensions.descriptorToDocumentationTranslator -> DefaultDescriptorToDocumentationTranslator CoreExtensions.documentationMerger -> DefaultDocumentationNodeMerger - CoreExtensions.markdownToContentConverterFactory -> ::DefaultMarkdownToContentConverter + CoreExtensions.markdownToContentConverterFactory -> ::DocNodeToContentConverter CoreExtensions.documentationToPageTranslator -> DefaultDocumentationToPageTranslator CoreExtensions.rendererFactory -> ::HtmlRenderer CoreExtensions.locationProviderFactory -> ::DefaultLocationProvider diff --git a/core/src/main/kotlin/renderers/HtmlRenderer.kt b/core/src/main/kotlin/renderers/HtmlRenderer.kt index c04a18b4..e9f2801e 100644 --- a/core/src/main/kotlin/renderers/HtmlRenderer.kt +++ b/core/src/main/kotlin/renderers/HtmlRenderer.kt @@ -20,7 +20,12 @@ open class HtmlRenderer( } protected open fun buildListItems(items: List<ContentNode>, pageContext: PageNode) = - "<li>\n${items.joinToString("\n</li>\n<li>\n") { it.build(pageContext) }}\n</li>" + items.joinToString("") { + if (it is ContentText) "<li>\n${it.build(pageContext)}\n</li>\n" else buildList( + it as ContentList, + pageContext + ) + } override fun buildResource(node: ContentEmbeddedResource, pageContext: PageNode): String { // TODO: extension point there val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg") @@ -59,7 +64,7 @@ open class HtmlRenderer( override fun buildLink(text: String, address: String): String = "<a href=\"$address\">$text</a>" - override fun buildCode(code: List<ContentNode>, language: String, pageContext: PageNode): String = "<code>$code</code>" + override fun buildCode(code: List<ContentNode>, language: String, pageContext: PageNode): String = buildNewLine() + "<code>${code.joinToString("") { (it as ContentText).text + buildNewLine() }}</code>" override fun buildText(textNode: ContentText): String = super.buildText(textNode).htmlEscape() diff --git a/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt b/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt index 4ffa4295..bdd345f1 100644 --- a/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt +++ b/core/src/main/kotlin/transformers/descriptors/DefaultDescriptorToDocumentationTranslator.kt @@ -12,15 +12,13 @@ import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink -import org.jetbrains.kotlin.kdoc.psi.impl.KDocName import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType +import parsers.MarkdownParser object DefaultDescriptorToDocumentationTranslator: DescriptorToDocumentationTranslator { override fun invoke( @@ -36,7 +34,7 @@ object DefaultDescriptorToDocumentationTranslator: DescriptorToDocumentationTran class DokkaDescriptorVisitor( private val platformData: PlatformData, private val resolutionFacade: DokkaResolutionFacade -) : DeclarationDescriptorVisitorEmptyBodies<DocumentationNode, DRI>() { +) : DeclarationDescriptorVisitorEmptyBodies<Documentable, DRI>() { override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor, parent: DRI): Nothing { throw IllegalStateException("${javaClass.simpleName} should never enter ${descriptor.javaClass.simpleName}") } @@ -69,7 +67,7 @@ class DokkaDescriptorVisitor( scope.classes(dri), descriptor.takeIf { it.isExpect }?.resolveClassDescriptionData(), listOfNotNull(descriptorData), - getXMLDRIs(descriptor, descriptorData).toMutableSet() + mutableSetOf() // TODO Implement following method to return proper results getXMLDRIs(descriptor, descriptorData).toMutableSet() ) } @@ -147,39 +145,15 @@ class DokkaDescriptorVisitor( private fun DeclarationDescriptor.resolveDescriptorData(): PlatformInfo { val doc = findKDoc() - val links = doc?.children?.filter { it is KDocLink }?.flatMap { link -> - val destination = link.children.first { it is KDocName }.text - resolveKDocLink( - resolutionFacade.resolveSession.bindingContext, - resolutionFacade, - this, - null, - destination.split('.') - ).map { Pair(destination, DRI.from(it)) } - }?.toMap() ?: emptyMap() - return BasePlatformInfo(doc, links, listOf(platformData)) + val parser: MarkdownParser = MarkdownParser(resolutionFacade, this) + val docHeader = parser.parseFromKDocTag(doc) + return BasePlatformInfo(docHeader, listOf(platformData)) } private fun ClassDescriptor.resolveClassDescriptionData(): ClassPlatformInfo { return ClassPlatformInfo(resolveDescriptorData(), (getSuperInterfaces() + getAllSuperclassesWithoutAny()).map { DRI.from(it) }) } - - private fun getXMLDRIs(descriptor: DeclarationDescriptor, platformInfo: PlatformInfo?) = - platformInfo?.docTag?.children - ?.filter { - it.text.contains("@attr") - }?.flatMap { ref -> - val matchResult = "@attr\\s+ref\\s+(.+)".toRegex().matchEntire(ref.text) - val toFind = matchResult?.groups?.last()?.value.orEmpty() - resolveKDocLink( - resolutionFacade.resolveSession.bindingContext, - resolutionFacade, - descriptor, - null, - toFind.split('.') - ).map { XMLMega("@attr ref", DRI.from(it)) } - }.orEmpty() } data class XMLMega(val key: String, val dri: DRI) : Extra diff --git a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt index 2b00c582..ef0f48d6 100644 --- a/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt +++ b/core/src/main/kotlin/transformers/documentation/DefaultDocumentationNodeMerger.kt @@ -14,14 +14,13 @@ internal object DefaultDocumentationNodeMerger : DocumentationNodeMerger { ) } -private fun <T: DocumentationNode> merge(elements: List<T>, reducer: (T, T) -> T): List<T> = +private fun <T: Documentable> merge(elements: List<T>, reducer: (T, T) -> T): List<T> = elements.groupingBy { it.dri } .reduce { _, left, right -> reducer(left, right)} .values.toList() fun PlatformInfo.mergeWith(other: PlatformInfo?) = BasePlatformInfo( - docTag, - links, + documentationNode, (platformData + (other?.platformData ?: emptyList())).distinct() ) @@ -31,12 +30,12 @@ fun ClassPlatformInfo.mergeWith(other: ClassPlatformInfo?) = ClassPlatformInfo( ) fun List<ClassPlatformInfo>.mergeClassPlatformInfo() : List<ClassPlatformInfo> = - groupingBy { it.docTag.toString() + it.links + it.inherited}.reduce { + groupingBy { it.documentationNode.children + it.inherited}.reduce { _, left, right -> left.mergeWith(right) }.values.toList() fun List<PlatformInfo>.merge() : List<PlatformInfo> = - groupingBy { it.docTag.toString() + it.links }.reduce { + groupingBy { it.documentationNode }.reduce { _, left, right -> left.mergeWith(right) }.values.toList() diff --git a/core/src/main/kotlin/utilities/nodeDebug.kt b/core/src/main/kotlin/utilities/nodeDebug.kt index 3290202e..984f13ed 100644 --- a/core/src/main/kotlin/utilities/nodeDebug.kt +++ b/core/src/main/kotlin/utilities/nodeDebug.kt @@ -1,13 +1,13 @@ package org.jetbrains.dokka.utilities -import org.jetbrains.dokka.model.DocumentationNode +import org.jetbrains.dokka.model.Documentable import org.jetbrains.dokka.pages.* const val DOWN = '\u2503' const val BRANCH = '\u2523' const val LAST = '\u2517' -fun DocumentationNode.pretty(prefix: String = "", isLast: Boolean = true): String { +fun Documentable.pretty(prefix: String = "", isLast: Boolean = true): String { val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' return prefix + (if (isLast) LAST else BRANCH) + this.toString() + diff --git a/core/src/test/kotlin/markdown/ParserTest.kt b/core/src/test/kotlin/markdown/ParserTest.kt index b0ec68ff..e5944e17 100644 --- a/core/src/test/kotlin/markdown/ParserTest.kt +++ b/core/src/test/kotlin/markdown/ParserTest.kt @@ -1,17 +1,17 @@ package org.jetbrains.dokka.tests import org.junit.Test -import org.jetbrains.dokka.toTestString -import org.jetbrains.dokka.parseMarkdown +//import org.jetbrains.dokkatoTestString +//import org.jetbrains.dokka.parseMarkdown import org.junit.Ignore @Ignore public class ParserTest { fun runTestFor(text : String) { println("MD: ---") println(text) - val markdownTree = parseMarkdown(text) +// val markdownTree = parseMarkdown(text) println("AST: ---") - println(markdownTree.toTestString()) +// println(markdownTree.toTestString()) println() } |