aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/build.gradle.kts2
-rw-r--r--core/src/main/kotlin/model/doc/DocTag.kt2
-rw-r--r--core/src/main/kotlin/parsers/MarkdownParser.kt166
-rw-r--r--core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt5
-rw-r--r--core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt2
-rw-r--r--core/src/test/kotlin/markdown/ParserTest.kt939
6 files changed, 982 insertions, 134 deletions
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 06610a38..e70791f3 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -16,7 +16,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
implementation("org.jsoup:jsoup:1.12.1")
implementation("com.google.code.gson:gson:2.8.5")
- implementation("org.jetbrains:markdown:0.1.40")
+ implementation("org.jetbrains:markdown:0.1.41")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.10")
testImplementation(project(":testApi"))
diff --git a/core/src/main/kotlin/model/doc/DocTag.kt b/core/src/main/kotlin/model/doc/DocTag.kt
index 0d8b361a..0224634b 100644
--- a/core/src/main/kotlin/model/doc/DocTag.kt
+++ b/core/src/main/kotlin/model/doc/DocTag.kt
@@ -93,5 +93,5 @@ class DocumentationLink(children: List<DocTag> = emptyList(), params: Map<String
override fun equals(other: Any?): Boolean = super.equals(other) && this.dri == (other as DocumentationLink).dri
override fun hashCode(): Int = super.hashCode() + dri.hashCode()
}
-class HorizontalRule : DocTag(emptyList(), emptyMap())
+object HorizontalRule : DocTag(emptyList(), emptyMap())
class CustomDocTag(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) \ No newline at end of file
diff --git a/core/src/main/kotlin/parsers/MarkdownParser.kt b/core/src/main/kotlin/parsers/MarkdownParser.kt
index 30a507df..ff27dc2e 100644
--- a/core/src/main/kotlin/parsers/MarkdownParser.kt
+++ b/core/src/main/kotlin/parsers/MarkdownParser.kt
@@ -6,6 +6,7 @@ import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.CompositeASTNode
+import org.intellij.markdown.ast.LeafASTNode
import org.intellij.markdown.ast.impl.ListItemCompositeNode
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
@@ -15,7 +16,6 @@ import org.jetbrains.dokka.utilities.DokkaConsoleLogger
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.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocImpl
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
@@ -26,12 +26,13 @@ class MarkdownParser (
private val declarationDescriptor: DeclarationDescriptor
) : Parser() {
- inner class MarkdownVisitor(val text: String) {
+ inner class MarkdownVisitor(val text: String, val destinationLinksMap: Map<String, String>) {
private fun headersHandler(node: ASTNode): DocTag =
DocNodesFromIElementFactory.getInstance(
node.type,
- visitNode(node.children.find { it.type == MarkdownTokenTypes.ATX_CONTENT }!!).children
+ visitNode(node.children.find { it.type == MarkdownTokenTypes.ATX_CONTENT } ?:
+ throw Error("Wrong AST Tree. ATX Header does not contain expected content")).children
)
private fun horizontalRulesHandler(node: ASTNode): DocTag =
@@ -44,7 +45,8 @@ class MarkdownParser (
)
private fun blockquotesHandler(node: ASTNode): DocTag =
- DocNodesFromIElementFactory.getInstance(node.type, children = node.children.drop(1).map { visitNode(it) })
+ DocNodesFromIElementFactory.getInstance(node.type, children = node.children
+ .filterIsInstance<CompositeASTNode>().evaluateChildren())
private fun listsHandler(node: ASTNode): DocTag {
@@ -68,7 +70,7 @@ class MarkdownParser (
it.type,
children = it
.children
- .drop(1)
+ .filterIsInstance<CompositeASTNode>()
.evaluateChildren()
)
else
@@ -77,34 +79,63 @@ class MarkdownParser (
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))
+ mapOf("start" to text.substring(listNumberNode.startOffset, listNumberNode.endOffset).trim().dropLast(1))
} else
emptyMap()
)
}
- private fun linksHandler(node: ASTNode): DocTag {
- 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 {
- resolveKDocLink(
- resolutionFacade.resolveSession.bindingContext,
- resolutionFacade,
- declarationDescriptor,
- null,
- link.split('.')
- ).also { if (it.size > 1) DokkaConsoleLogger.warn("Markdown link resolved more than one element: $it") }.firstOrNull()//.single()
- ?.let { DRI.from(it) }
+ private fun resolveDRI(link: String): DRI? = if (link.startsWith("http") || link.startsWith("www")) {
+ null
+ } else {
+ resolveKDocLink(
+ resolutionFacade.resolveSession.bindingContext,
+ resolutionFacade,
+ declarationDescriptor,
+ null,
+ link.split('.')
+ ).also { if (it.size > 1) throw Error("Markdown link resolved more than one element: $it") }.firstOrNull()//.single()
+ ?.let { DRI.from(it) }
+ }
- }
- 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).evaluateChildren(), dri = dri)
- else -> DocNodesFromIElementFactory.getInstance(node.type, params = href, children = listOf(visitNode(linkNode)), dri = dri)
- }
+ private fun referenceLinksHandler(node: ASTNode): DocTag {
+ val linkLabel = node.children.find { it.type == MarkdownElementTypes.LINK_LABEL } ?:
+ throw Error("Wrong AST Tree. Reference link does not contain expected content")
+ val linkText = node.children.findLast { it.type == MarkdownElementTypes.LINK_TEXT } ?: linkLabel
+
+ val linkKey = text.substring(linkLabel.startOffset, linkLabel.endOffset)
+
+ val link = destinationLinksMap[linkKey.toLowerCase()] ?: linkKey
+
+ return linksHandler(linkText, link)
+ }
+
+ private fun inlineLinksHandler(node: ASTNode): DocTag {
+ val linkText = node.children.find { it.type == MarkdownElementTypes.LINK_TEXT } ?:
+ throw Error("Wrong AST Tree. Inline link does not contain expected content")
+ val linkDestination = node.children.find { it.type == MarkdownElementTypes.LINK_DESTINATION } ?:
+ throw Error("Wrong AST Tree. Inline link does not contain expected content")
+ val linkTitle = node.children.find { it.type == MarkdownElementTypes.LINK_TITLE }
+
+ val link = text.substring(linkDestination.startOffset, linkDestination.endOffset)
+
+ return linksHandler(linkText, link, linkTitle)
+ }
+
+ private fun autoLinksHandler(node: ASTNode): DocTag {
+ val link = text.substring(node.startOffset + 1, node.endOffset - 1)
+
+ return linksHandler(node, link)
+ }
+
+ private fun linksHandler(linkText: ASTNode, link: String, linkTitle: ASTNode? = null): DocTag {
+ val dri: DRI? = resolveDRI(link)
+ val params = if(linkTitle == null)
+ mapOf("href" to link)
+ else
+ mapOf("href" to link, "title" to text.substring(linkTitle.startOffset + 1, linkTitle.endOffset - 1))
+
+ return DocNodesFromIElementFactory.getInstance(MarkdownElementTypes.INLINE_LINK, params = params, children = linkText.children.drop(1).dropLast(1).evaluateChildren(), dri = dri)
}
private fun imagesHandler(node: ASTNode): DocTag {
@@ -132,8 +163,11 @@ class MarkdownParser (
node.type,
children = node
.children
- .filter { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }
- .map { visitNode(it) },
+ .let { listOf(Text( body = text.substring(
+ it.find { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }?.startOffset ?: 0,
+ it.findLast { it.type == MarkdownTokenTypes.CODE_FENCE_CONTENT }?.endOffset ?: 0 //TODO: Problem with empty code fence
+ ))
+ ) },
params = node
.children
.find { it.type == MarkdownTokenTypes.FENCE_LANG }
@@ -161,7 +195,9 @@ class MarkdownParser (
MarkdownElementTypes.STRONG,
MarkdownElementTypes.EMPH -> emphasisHandler(node)
MarkdownElementTypes.FULL_REFERENCE_LINK,
- MarkdownElementTypes.SHORT_REFERENCE_LINK -> linksHandler(node)
+ MarkdownElementTypes.SHORT_REFERENCE_LINK -> referenceLinksHandler(node)
+ MarkdownElementTypes.INLINE_LINK -> inlineLinksHandler(node)
+ MarkdownElementTypes.AUTOLINK -> autoLinksHandler(node)
MarkdownElementTypes.BLOCK_QUOTE -> blockquotesHandler(node)
MarkdownElementTypes.UNORDERED_LIST,
MarkdownElementTypes.ORDERED_LIST -> listsHandler(node)
@@ -171,20 +207,84 @@ class MarkdownParser (
MarkdownElementTypes.IMAGE -> imagesHandler(node)
MarkdownTokenTypes.CODE_FENCE_CONTENT,
MarkdownTokenTypes.CODE_LINE,
- MarkdownTokenTypes.TEXT -> DocNodesFromIElementFactory.getInstance(MarkdownTokenTypes.TEXT, body = text.substring(node.startOffset, node.endOffset))
+ MarkdownTokenTypes.TEXT -> DocNodesFromIElementFactory.getInstance(
+ MarkdownTokenTypes.TEXT,
+ body = text
+ .substring(node.startOffset, node.endOffset).transform()
+ )
+ MarkdownElementTypes.MARKDOWN_FILE -> if(node.children.size == 1) visitNode(node.children.first()) else defaultHandler(node)
else -> defaultHandler(node)
}
private fun List<ASTNode>.evaluateChildren(): List<DocTag> =
- this.filter { it is CompositeASTNode || it.type == MarkdownTokenTypes.TEXT }.map { visitNode(it) }
+ this.removeUselessTokens().mergeLeafASTNodes().map { visitNode(it) }
+
+ private fun List<ASTNode>.removeUselessTokens(): List<ASTNode> =
+ this.filterIndexed { index, _ ->
+ !(this[index].type == MarkdownTokenTypes.EOL &&
+ this.isLeaf(index - 1) && this.getOrNull(index - 1)?.type !in leafNodes &&
+ this.isLeaf(index + 1) && this.getOrNull(index + 1)?.type !in leafNodes) &&
+ this[index].type != MarkdownElementTypes.LINK_DEFINITION
+ }
+
+ private val notLeafNodes = listOf(MarkdownTokenTypes.HORIZONTAL_RULE)
+ private val leafNodes = listOf(MarkdownElementTypes.STRONG, MarkdownElementTypes.EMPH)
+
+ private fun List<ASTNode>.isLeaf(index: Int): Boolean =
+ if(index in 0..this.lastIndex)
+ (this[index] is CompositeASTNode)|| this[index].type in notLeafNodes
+ else
+ false
+
+ private fun List<ASTNode>.mergeLeafASTNodes(): List<ASTNode> {
+ val children: MutableList<ASTNode> = mutableListOf()
+ var index = 0
+ while(index <= this.lastIndex) {
+ if(this.isLeaf(index)) {
+ children += this[index]
+ }
+ else {
+ val startOffset = this[index].startOffset
+ while(index < this.lastIndex) {
+ if(this.isLeaf(index + 1)) {
+ val endOffset = this[index].endOffset
+ if(text.substring(startOffset, endOffset).transform().isNotEmpty())
+ children += LeafASTNode(MarkdownTokenTypes.TEXT, startOffset, endOffset)
+ break
+ }
+ index++
+ }
+ if(index == this.lastIndex) {
+ val endOffset = this[index].endOffset
+ if(text.substring(startOffset, endOffset).transform().isNotEmpty())
+ children += LeafASTNode(MarkdownTokenTypes.TEXT, startOffset, endOffset)
+ }
+ }
+ index++
+ }
+ return children
+ }
+
+ private fun String.transform() = this
+ .replace(Regex("\n\n+"), "")
+ .replace(Regex("\n>+ "), "\n")
}
+
+ private fun getAllDestinationLinks(text: String, node: ASTNode): List<Pair<String, String>> =
+ node.children
+ .filter { it.type == MarkdownElementTypes.LINK_DEFINITION }
+ .map { text.substring(it.children[0].startOffset, it.children[0].endOffset).toLowerCase() to
+ text.substring(it.children[2].startOffset, it.children[2].endOffset) } +
+ node.children.filterIsInstance<CompositeASTNode>().flatMap { getAllDestinationLinks(text, it) }
+
+
private fun markdownToDocNode(text: String): DocTag {
val flavourDescriptor = CommonMarkFlavourDescriptor()
val markdownAstRoot: ASTNode = IntellijMarkdownParser(flavourDescriptor).buildMarkdownTreeFromString(text)
- return MarkdownVisitor(text).visitNode(markdownAstRoot)
+ return MarkdownVisitor(text, getAllDestinationLinks(text, markdownAstRoot).toMap()).visitNode(markdownAstRoot)
}
override fun parseStringToDocNode(extractedString: String) = markdownToDocNode(extractedString)
diff --git a/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt b/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt
index 1a302176..b899679d 100644
--- a/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt
+++ b/core/src/main/kotlin/parsers/factories/DocNodesFromIElementFactory.kt
@@ -11,7 +11,8 @@ object DocNodesFromIElementFactory {
fun getInstance(type: IElementType, children: List<DocTag> = 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.FULL_REFERENCE_LINK,
+ MarkdownElementTypes.INLINE_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,
@@ -30,7 +31,7 @@ object DocNodesFromIElementFactory {
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()
+ MarkdownTokenTypes.HORIZONTAL_RULE -> HorizontalRule
else -> CustomDocTag(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
index 6090cdb4..dc74ecc1 100644
--- a/core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt
+++ b/core/src/main/kotlin/parsers/factories/DocNodesFromStringFactory.kt
@@ -70,7 +70,7 @@ object DocNodesFromStringFactory {
"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()
+ "hr" -> HorizontalRule
else -> CustomDocTag(children, params)
}
} \ No newline at end of file
diff --git a/core/src/test/kotlin/markdown/ParserTest.kt b/core/src/test/kotlin/markdown/ParserTest.kt
index e5944e17..77ccd769 100644
--- a/core/src/test/kotlin/markdown/ParserTest.kt
+++ b/core/src/test/kotlin/markdown/ParserTest.kt
@@ -1,154 +1,901 @@
package org.jetbrains.dokka.tests
-import org.junit.Test
-//import org.jetbrains.dokkatoTestString
-//import org.jetbrains.dokka.parseMarkdown
+import org.jetbrains.dokka.model.doc.*
import org.junit.Ignore
+import org.junit.Test
+import testApi.testRunner.AbstractKDocTest
+
+
+class ParserTest : AbstractKDocTest() {
-@Ignore public class ParserTest {
- fun runTestFor(text : String) {
- println("MD: ---")
- println(text)
-// val markdownTree = parseMarkdown(text)
- println("AST: ---")
-// println(markdownTree.toTestString())
- println()
+ @Test fun `Simple text`() {
+ val kdoc = """
+ | This is simple test of string
+ | Next line
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text(body = "This is simple test of string\nNext line")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun text() {
- runTestFor("text")
+ @Test fun `Text with Bold and Emphasis decorators`() {
+ val kdoc = """
+ | This is **simple** test of _string_
+ | Next **_line_**
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Text(body = "This is "),
+ B(listOf(Text(body = "simple"))),
+ Text(body = " test of "),
+ I(listOf(Text(body = "string"))),
+ Text(body = "\nNext "),
+ B(listOf(I(listOf(Text(body = "line")))))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun textWithSpaces() {
- runTestFor("text and string")
+ @Test fun `Text with Colon`() {
+ val kdoc = """
+ | This is simple text with: colon!
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text(body = "This is simple text with: colon!")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun textWithColon() {
- runTestFor("text and string: cool!")
+ @Test fun `Multilined text`() {
+ val kdoc = """
+ | Text
+ | and
+ | String
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text(body = "Text\nand\nString")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun link() {
- runTestFor("text [links]")
+ @Test fun `Paragraphs`() {
+ val kdoc = """
+ | Paragraph number
+ | one
+ |
+ | Paragraph
+ | number two
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ P(listOf(Text(body = "Paragraph number\none"))),
+ P(listOf(Text(body = "Paragraph\nnumber two")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun linkWithHref() {
- runTestFor("text [links](http://google.com)")
+ @Test fun `Emphasis with star`() {
+ val kdoc = "*text*"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(I(listOf(Text(body = "text")))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun multiline() {
- runTestFor(
- """
-text
-and
-string
-""")
+ @Test fun `Underscores that are not Emphasis`() {
+ val kdoc = "text_with_underscores"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text(body = "text_with_underscores")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun para() {
- runTestFor(
- """
-paragraph number
-one
+ @Test fun `Emphasis with underscores`() {
+ val kdoc = "_text_"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(I(listOf(Text(body = "text")))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
-paragraph
-number two
-""")
+ @Test fun `Embedded star`() {
+ val kdoc = "Embedded*Star"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text(body = "Embedded*Star")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun bulletList() {
- runTestFor(
- """* list item 1
-* list item 2
-""")
+
+ @Test fun `Unordered list`() {
+ val kdoc = """
+ | * list item 1
+ | * list item 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text(body = "list item 1"))))),
+ Li(listOf(P(listOf(Text(body = "list item 2")))))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun bulletListWithLines() {
- runTestFor(
- """
-* list item 1
- continue 1
-* list item 2
- continue 2
- """)
+ @Test fun `Unordered list with multilines`() {
+ val kdoc = """
+ | * list item 1
+ | continue 1
+ | * list item 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text(body = "list item 1\ncontinue 1"))))),
+ Li(listOf(P(listOf(Text(body = "list item 2\ncontinue 2")))))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun bulletListStrong() {
- runTestFor(
- """
-* list *item* 1
- continue 1
-* list *item* 2
- continue 2
- """)
+ @Test fun `Unordered list with Bold`() {
+ val kdoc = """
+ | * list **item** 1
+ | continue 1
+ | * list __item__ 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ul(listOf(
+ Li(listOf(P(listOf(
+ Text(body = "list "),
+ B(listOf(Text(body = "item"))),
+ Text(body = " 1\ncontinue 1")
+ )))),
+ Li(listOf(P(listOf(
+ Text(body = "list "),
+ B(listOf(Text(body = "item"))),
+ Text(body = " 2\ncontinue 2")
+ ))))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun emph() {
- runTestFor("*text*")
+ @Test fun `Unordered list with nested bullets`() {
+ val kdoc = """
+ | * Outer first
+ | Outer next line
+ | * Outer second
+ | - Middle first
+ | Middle next line
+ | - Middle second
+ | + Inner first
+ | Inner next line
+ | - Middle third
+ | * Outer third
+ |
+ | New paragraph""".trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ Ul(listOf(
+ Li(listOf(P(listOf(Text(body = "Outer first\nOuter next line"))))),
+ Li(listOf(P(listOf(Text(body = "Outer second"))))),
+ Ul(listOf(
+ Li(listOf(P(listOf(Text(body = "Middle first\nMiddle next line"))))),
+ Li(listOf(P(listOf(Text(body = "Middle second"))))),
+ Ul(listOf(
+ Li(listOf(P(listOf(Text(body = "Inner first\nInner next line")))))
+ )),
+ Li(listOf(P(listOf(Text(body = "Middle third")))))
+ )),
+ Li(listOf(P(listOf(Text(body = "Outer third")))))
+ )),
+ P(listOf(Text(body = "New paragraph")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun underscoresNoEmph() {
- runTestFor("text_with_underscores")
+ @Test fun `Ordered list`() {
+ val kdoc = """
+ | 1. list item 1
+ | 2. list item 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text(body = "list item 1"))))),
+ Li(listOf(P(listOf(Text(body = "list item 2")))))
+ ),
+ mapOf("start" to "1")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun emphUnderscores() {
- runTestFor("_text_")
+
+ @Test fun `Ordered list beginning from other number`() {
+ val kdoc = """
+ | 9. list item 1
+ | 12. list item 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text(body = "list item 1"))))),
+ Li(listOf(P(listOf(Text(body = "list item 2")))))
+ ),
+ mapOf("start" to "9")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun singleStar() {
- runTestFor("Embedded*Star")
+ @Test fun `Ordered list with multilines`() {
+ val kdoc = """
+ | 2. list item 1
+ | continue 1
+ | 3. list item 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text(body = "list item 1\ncontinue 1"))))),
+ Li(listOf(P(listOf(Text(body = "list item 2\ncontinue 2")))))
+ ),
+ mapOf("start" to "2")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun directive() {
- runTestFor("A text \${code with.another.value} with directive")
+ @Test fun `Ordered list with Bold`() {
+ val kdoc = """
+ | 1. list **item** 1
+ | continue 1
+ | 2. list __item__ 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(listOf(
+ Li(listOf(P(listOf(
+ Text(body = "list "),
+ B(listOf(Text(body = "item"))),
+ Text(body = " 1\ncontinue 1")
+ )))),
+ Li(listOf(P(listOf(
+ Text(body = "list "),
+ B(listOf(Text(body = "item"))),
+ Text(body = " 2\ncontinue 2")
+ ))))
+ ),
+ mapOf("start" to "1")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun emphAndEmptySection() {
- runTestFor("*text*\n\$sec:\n")
+ @Test fun `Ordered list with nested bullets`() {
+ val kdoc = """
+ | 1. Outer first
+ | Outer next line
+ | 2. Outer second
+ | 1. Middle first
+ | Middle next line
+ | 2. Middle second
+ | 1. Inner first
+ | Inner next line
+ | 5. Middle third
+ | 4. Outer third
+ |
+ | New paragraph""".trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ Ol(listOf(
+ Li(listOf(P(listOf(Text(body = "Outer first\nOuter next line"))))),
+ Li(listOf(P(listOf(Text(body = "Outer second"))))),
+ Ol(listOf(
+ Li(listOf(P(listOf(Text(body = "Middle first\nMiddle next line"))))),
+ Li(listOf(P(listOf(Text(body = "Middle second"))))),
+ Ol(listOf(
+ Li(listOf(P(listOf(Text(body = "Inner first\nInner next line")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text(body = "Middle third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text(body = "Outer third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ P(listOf(Text(body = "New paragraph")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun emphAndSection() {
- runTestFor("*text*\n\$sec: some text\n")
+ @Test fun `Ordered nested in Unordered nested in Ordered list`() {
+ val kdoc = """
+ | 1. Outer first
+ | Outer next line
+ | 2. Outer second
+ | + Middle first
+ | Middle next line
+ | + Middle second
+ | 1. Inner first
+ | Inner next line
+ | + Middle third
+ | 4. Outer third
+ |
+ | New paragraph""".trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ Ol(listOf(
+ Li(listOf(P(listOf(Text(body = "Outer first\nOuter next line"))))),
+ Li(listOf(P(listOf(Text(body = "Outer second"))))),
+ Ul(listOf(
+ Li(listOf(P(listOf(Text(body = "Middle first\nMiddle next line"))))),
+ Li(listOf(P(listOf(Text(body = "Middle second"))))),
+ Ol(listOf(
+ Li(listOf(P(listOf(Text(body = "Inner first\nInner next line")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text(body = "Middle third")))))
+ )),
+ Li(listOf(P(listOf(Text(body = "Outer third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ P(listOf(Text(body = "New paragraph")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun emphAndBracedSection() {
- runTestFor("Text *bold* text \n\${sec}: some text")
+ @Test fun `Header and two paragraphs`() {
+ val kdoc = """
+ | # Header 1
+ | Following text
+ |
+ | New paragraph
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ H1(listOf(Text(body = "Header 1"))),
+ P(listOf(Text(body = "Following text"))),
+ P(listOf(Text(body = "New paragraph")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun section() {
- runTestFor(
- "Plain text \n\$one: Summary \n\${two}: Description with *emphasis* \n\${An example of a section}: Example")
+ @Ignore //TODO: ATX_2 to ATX_6 and sometimes ATX_1 from jetbrains parser consumes white space. Need to handle it in their library
+ @Test fun `All headers`() {
+ val kdoc = """
+ | # Header 1
+ | Text 1
+ | ## Header 2
+ | Text 2
+ | ### Header 3
+ | Text 3
+ | #### Header 4
+ | Text 4
+ | ##### Header 5
+ | Text 5
+ | ###### Header 6
+ | Text 6
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ H1(listOf(Text(body = "Header 1"))),
+ P(listOf(Text(body = "Text 1"))),
+ H2(listOf(Text(body = "Header 2"))),
+ P(listOf(Text(body = "Text 2"))),
+ H3(listOf(Text(body = "Header 3"))),
+ P(listOf(Text(body = "Text 3"))),
+ H4(listOf(Text(body = "Header 4"))),
+ P(listOf(Text(body = "Text 4"))),
+ H5(listOf(Text(body = "Header 5"))),
+ P(listOf(Text(body = "Text 5"))),
+ H6(listOf(Text(body = "Header 6"))),
+ P(listOf(Text(body = "Text 6")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun anonymousSection() {
- runTestFor("Summary\n\nDescription\n")
+ @Test fun `Bold New Line Bold`() {
+ val kdoc = """
+ | **line 1**
+ | **line 2**
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ B(listOf(Text(body = "line 1"))),
+ Text(body = "\n"),
+ B(listOf(Text(body = "line 2")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun specialSection() {
- runTestFor(
- "Plain text \n\$\$summary: Summary \n\${\$description}: Description \n\${\$An example of a section}: Example")
+ @Test fun `Horizontal rule`() {
+ val kdoc = """
+ | ***
+ | text 1
+ | ___
+ | text 2
+ | ***
+ | text 3
+ | ___
+ | text 4
+ | ***
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ HorizontalRule,
+ P(listOf(Text(body = "text 1"))),
+ HorizontalRule,
+ P(listOf(Text(body = "text 2"))),
+ HorizontalRule,
+ P(listOf(Text(body = "text 3"))),
+ HorizontalRule,
+ P(listOf(Text(body = "text 4"))),
+ HorizontalRule
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- @Test fun emptySection() {
- runTestFor(
- "Plain text \n\$summary:")
+ @Test fun `Blockquote`() {
+ val kdoc = """
+ | > Blockquotes are very handy in email to emulate reply text.
+ | > This line is part of the same quote.
+ |
+ | Quote break.
+ |
+ | > Quote
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ BlockQuote(listOf(
+ P(listOf(Text(body = "Blockquotes are very handy in email to emulate reply text.\nThis line is part of the same quote.")))
+ )),
+ P(listOf(Text(body = "Quote break."))),
+ BlockQuote(listOf(
+ P(listOf(Text(body = "Quote")))
+ ))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
- val b = "$"
- @Test fun pair() {
- runTestFor(
- """Represents a generic pair of two values.
-There is no meaning attached to values in this class, it can be used for any purpose.
-Pair exhibits value semantics, i.e. two pairs are equal if both components are equal.
+ @Test fun `Blockquote nested`() {
+ val kdoc = """
+ | > text 1
+ | > text 2
+ | >> text 3
+ | >> text 4
+ | >
+ | > text 5
+ |
+ | Quote break.
+ |
+ | > Quote
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ BlockQuote(listOf(
+ P(listOf(Text(body = "text 1\ntext 2"))),
+ BlockQuote(listOf(
+ P(listOf(Text(body = "text 3\ntext 4")))
+ )),
+ P(listOf(Text(body = "text 5")))
+ )),
+ P(listOf(Text(body = "Quote break."))),
+ BlockQuote(listOf(
+ P(listOf(Text(body = "Quote")))
+ ))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Ignore //TODO: Again ATX_1 consumes white space
+ @Test fun `Blockquote nested with fancy text enhancement`() {
+ val kdoc = """
+ | > text **1**
+ | > text 2
+ | >> # text 3
+ | >> * text 4
+ | >> * text 5
+ | >
+ | > text 6
+ |
+ | Quote break.
+ |
+ | > Quote
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ BlockQuote(listOf(
+ P(listOf(
+ Text(body = "text "),
+ B(listOf(Text(body = "1"))),
+ Text(body = "\ntext 2")
+ )),
+ BlockQuote(listOf(
+ H1(listOf(Text(body = "text 3"))),
+ Ul(listOf(
+ Li(listOf(P(listOf(Text(body = "text 4"))))),
+ Ul(listOf(
+ Li(listOf(P(listOf(Text(body = "text 5")))))
+ )
+ )))
+ )),
+ P(listOf(Text(body = "text 6")))
+ )),
+ P(listOf(Text(body = "Quote break."))),
+ BlockQuote(listOf(
+ P(listOf(Text(body = "Quote")))
+ ))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
-An example of decomposing it into values:
-${b}{code test.tuples.PairTest.pairMultiAssignment}
+ @Test fun `Simple Code Block`() {
+ val kdoc = """
+ | `Some code`
+ | Sample text
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ Code(listOf(Text(body = "Some code"))),
+ Text(body = "\nSample text")
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
-${b}constructor: Creates new instance of [Pair]
-${b}first: First value
-${b}second: Second value""""
- )
+ @Test fun `Multilined Code Block`() {
+ val kdoc = """
+ | ```kotlin
+ | val x: Int = 0
+ | val y: String = "Text"
+ |
+ | val z: Boolean = true
+ | for(i in 0..10) {
+ | println(i)
+ | }
+ | ```
+ | Sample text
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ Code(
+ listOf(
+ Text(body = "val x: Int = 0\nval y: String = \"Text\"\n\n val z: Boolean = true\n" +
+ "for(i in 0..10) {\n println(i)\n}")
+ ),
+ mapOf("lang" to "kotlin")
+ ),
+ P(listOf(Text(body = "Sample text")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
}
+
+ @Test fun `Inline link`() {
+ val kdoc = """
+ | [I'm an inline-style link](https://www.google.com)
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(A(
+ listOf(Text(body = "I'm an inline-style link")),
+ mapOf("href" to "https://www.google.com")
+ )))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test fun `Inline link with title`() {
+ val kdoc = """
+ | [I'm an inline-style link with title](https://www.google.com "Google's Homepage")
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(A(
+ listOf(Text(body = "I'm an inline-style link with title")),
+ mapOf("href" to "https://www.google.com", "title" to "Google's Homepage")
+ )))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test fun `Full reference link`() {
+ val kdoc = """
+ | [I'm a reference-style link][Arbitrary case-insensitive reference text]
+ |
+ | [arbitrary case-insensitive reference text]: https://www.mozilla.org
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(P(listOf(A(
+ listOf(Text(body = "I'm a reference-style link")),
+ mapOf("href" to "https://www.mozilla.org")
+ )))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test fun `Full reference link with number`() {
+ val kdoc = """
+ | [You can use numbers for reference-style link definitions][1]
+ |
+ | [1]: http://slashdot.org
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(P(listOf(A(
+ listOf(Text(body = "You can use numbers for reference-style link definitions")),
+ mapOf("href" to "http://slashdot.org")
+ )))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test fun `Short reference link`() {
+ val kdoc = """
+ | Or leave it empty and use the [link text itself].
+ |
+ | [link text itself]: http://www.reddit.com
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(P(listOf(
+ Text(body = "Or leave it empty and use the "),
+ A(
+ listOf(Text(body = "link text itself")),
+ mapOf("href" to "http://www.reddit.com")
+ ),
+ Text(body = ".")
+ ))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test fun `Autolink`() {
+ val kdoc = """
+ | URLs and URLs in angle brackets will automatically get turned into links.
+ | http://www.example.com or <http://www.example.com> and sometimes
+ | example.com (but not on Github, for example).
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ Text(body = "URLs and URLs in angle brackets will automatically get turned into links.\nhttp://www.example.com or "),
+ A(
+ listOf(Text(body = "http://www.example.com")),
+ mapOf("href" to "http://www.example.com")
+ ),
+ Text(body = " and sometimes\nexample.com (but not on Github, for example).")
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test fun `Various links`() {
+ val kdoc = """
+ | [I'm an inline-style link](https://www.google.com)
+ |
+ | [I'm an inline-style link with title](https://www.google.com "Google's Homepage")
+ |
+ | [I'm a reference-style link][Arbitrary case-insensitive reference text]
+ |
+ | [You can use numbers for reference-style link definitions][1]
+ |
+ | Or leave it empty and use the [link text itself].
+ |
+ | URLs and URLs in angle brackets will automatically get turned into links.
+ | http://www.example.com or <http://www.example.com> and sometimes
+ | example.com (but not on Github, for example).
+ |
+ | Some text to show that the reference links can follow later.
+ |
+ | [arbitrary case-insensitive reference text]: https://www.mozilla.org
+ | [1]: http://slashdot.org
+ | [link text itself]: http://www.reddit.com
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(
+ P(listOf(A(
+ listOf(Text(body = "I'm an inline-style link")),
+ mapOf("href" to "https://www.google.com")
+ ))),
+ P(listOf(A(
+ listOf(Text(body = "I'm an inline-style link with title")),
+ mapOf("href" to "https://www.google.com", "title" to "Google's Homepage")
+ ))),
+ P(listOf(A(
+ listOf(Text(body = "I'm a reference-style link")),
+ mapOf("href" to "https://www.mozilla.org")
+ ))),
+ P(listOf(A(
+ listOf(Text(body = "You can use numbers for reference-style link definitions")),
+ mapOf("href" to "http://slashdot.org")
+ ))),
+ P(listOf(
+ Text(body = "Or leave it empty and use the "),
+ A(
+ listOf(Text(body = "link text itself")),
+ mapOf("href" to "http://www.reddit.com")
+ ),
+ Text(body = ".")
+ )),
+ P(listOf(
+ Text(body = "URLs and URLs in angle brackets will automatically get turned into links.\nhttp://www.example.com or "),
+ A(
+ listOf(Text(body = "http://www.example.com")),
+ mapOf("href" to "http://www.example.com")
+ ),
+ Text(body = " and sometimes\nexample.com (but not on Github, for example).")
+ )),
+ P(listOf(Text(body = "Some text to show that the reference links can follow later.")))
+ ))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
}